Daily Git Workflow: From Morning Pull to Evening Push
Hands-on tutorial for a productive daily Git workflow from morning pull to evening push, covering branching, committing, reviewing, and pushing best practices.
Introduction
Knowing individual Git commands is like knowing musical notes — useful, but not music. A daily Git workflow is the composition that turns those notes into a productive rhythm. The best developers do not just know Git commands; they have a repeatable, reliable process for using Git every single day.
This tutorial walks through a complete daily workflow: starting your day by syncing with the team, creating feature branches, making atomic commits, reviewing your work, handling conflicts, and pushing clean changes. Each step is explained with the reasoning behind it, so you understand not just what to do but why.
Whether you are a solo developer or part of a large team, this workflow adapts to your needs. The principles remain the same: sync often, commit atomically, review before pushing, and keep history clean. For foundational concepts, see git add and git commit.
When to Use / When Not to Use
Use this workflow when:
- Working on a team with a shared repository
- Developing features, fixing bugs, or making any code changes
- You want clean, reviewable commit history
- You want to minimize merge conflicts
- You want a predictable, repeatable daily routine
Adapt or simplify when:
- Working on a solo personal project — you can skip some review steps
- Making trivial changes like typo fixes — a single commit is fine
- Working in a GitFlow or trunk-based environment — adjust branching strategy accordingly
- Using a platform with squash-merge — focus on branch cleanliness over individual commits
Core Concepts
A productive daily Git workflow follows a simple rhythm:
- Sync — Start with the latest code from the team
- Branch — Isolate your work
- Commit — Save logical units of work
- Review — Verify your changes before sharing
- Sync again — Incorporate any new team changes
- Push — Share your work
graph LR
A[Sync<br/>Pull latest] --> B[Branch<br/>Isolate work]
B --> C[Commit<br/>Atomic changes]
C --> D[Review<br/>Verify quality]
D --> E[Sync again<br/>Resolve conflicts]
E --> F[Push<br/>Share work]
The Three-State Model in Daily Work
graph LR
A[Remote<br/>origin/main] -->|git pull| B[Local main<br/>Synced]
B -->|git switch -c feature| C[Feature branch<br/>Isolated work]
C -->|git add| D[Staging area<br/>Prepared changes]
D -->|git commit| E[Local commits<br/>Atomic history]
E -->|git push| A
Architecture or Flow Diagram
Complete Daily Workflow
sequenceDiagram
participant Remote as Remote Repository
participant Local as Local Repository
participant WD as Working Directory
participant You as Developer
Note over Remote,You: Morning: Sync
Remote->>Local: git fetch origin
Local->>Local: git switch main
Local->>Local: git pull --rebase origin main
Note over Remote,You: Start Work
Local->>Local: git switch -c feature/name
You->>WD: Write code
WD->>Local: git add -p
Local->>Local: git commit -m "message"
Note over Remote,You: Midday: Checkpoint
You->>WD: Write more code
WD->>Local: git add -p
Local->>Local: git commit -m "message"
Note over Remote,You: Review
Local->>Local: git diff main...feature
Local->>Local: git log main..feature --oneline
Note over Remote,You: Afternoon: Sync Again
Remote->>Local: git fetch origin
Local->>Local: git rebase origin/main
Note over Remote,You: Push
Local->>Remote: git push -u origin feature/name
Branch Strategy Overview
graph LR
A[main<br/>Production-ready] --> B[develop<br/>Integration]
B --> C[feature/auth<br/>Feature work]
B --> D[feature/api<br/>Feature work]
C --> B
D --> B
Step-by-Step Guide / Deep Dive
Morning: Sync with the Team
Start every day by getting the latest code. This minimizes conflicts and ensures you are building on the most recent foundation.
# 1. Fetch all remote updates without merging
git fetch --all --prune
# 2. Switch to your main branch
git switch main
# 3. Update with remote changes (rebase keeps linear history)
git pull --rebase origin main
# 4. Verify you are up to date
git status
# Should show: "Your branch is up to date with 'origin/main'"
Why --rebase instead of merge? Rebase replays your local commits on top of the updated remote, creating a linear history. Merge creates a merge commit that clutters the log. For the main branch with no local commits, both produce the same result, but --rebase is a good habit.
Create a Feature Branch
Never work directly on main. Always create a branch for your work.
# Create and switch to a new branch
git switch -c feature/user-authentication
# Naming conventions:
# feature/description - New features
# fix/description - Bug fixes
# refactor/description - Code improvements
# docs/description - Documentation changes
# chore/description - Maintenance tasks
Why branch? Branches isolate your work. If something goes wrong, main is unaffected. Branches enable parallel work — multiple developers can work on different features simultaneously without interfering with each other.
Make Atomic Commits
Work in small, logical units. Each commit should represent a single, complete thought.
# Write some code...
# Review what changed
git status -s
git diff
# Stage selectively
git add src/auth.py
git add -p src/app.py # Only stage auth-related hunks
# Verify staged content
git diff --staged
# Commit with a meaningful message
git commit -m "feat(auth): add JWT token generation
Implement HMAC-SHA256 based token generation with
configurable expiration. Tokens include user ID,
role, and expiration timestamp.
Implements requirement AUTH-001."
# Verify the commit
git log -1
What makes a commit atomic? An atomic commit:
- Does one thing and does it completely
- Can be understood independently of other commits
- Can be reverted without breaking the codebase
- Passes all tests on its own
Midday: Checkpoint Your Work
If you have been working for a while, commit your progress even if the feature is incomplete.
# Save work-in-progress
git add .
git commit -m "WIP: user authentication - login endpoint in progress"
# Or use stash for temporary saves
git stash push -m "WIP: auth work"
# Continue working...
WIP commits vs stash: WIP commits are visible in history and can be pushed to share progress. Stash is local and hidden. Use WIP commits for work you might need to reference later; use stash for temporary context switches.
Review Your Work
Before pushing, review everything you have done.
# See all your commits
git log main..feature/user-authentication --oneline
# See the complete diff of your branch
git diff main...feature/user-authentication
# See file-level summary
git diff main...feature/user-authentication --stat
# Run tests
npm test
# Run linter
npm run lint
Why review? This is your last chance to catch mistakes before they become visible to the team. Review your commit messages for clarity, verify that all intended changes are included, and ensure no debug code or temporary files slipped in.
Afternoon: Sync Again
While you were working, teammates may have pushed new changes. Incorporate them before pushing.
# Fetch latest remote changes
git fetch origin
# Rebase your feature branch on top of the updated main
git rebase origin/main
# If there are conflicts:
# 1. Edit the conflicted files
# 2. Stage the resolved files
git add src/conflicted-file.py
# 3. Continue the rebase
git rebase --continue
# Run tests again after rebase
npm test
Why rebase instead of merge? Rebasing creates a clean, linear history where your feature appears as if it was developed on top of the latest main. This makes the eventual merge (or squash-merge) clean and easy to review.
Evening: Push Your Work
# Push your feature branch to the remote
git push -u origin feature/user-authentication
# The -u flag sets the upstream tracking
# Future pushes can use just: git push
# If you rebased after pushing before:
git push --force-with-lease origin feature/user-authentication
Why --force-with-lease instead of --force? --force-with-lease checks that the remote branch has not been updated by someone else since you last fetched. If it has, the push is rejected, preventing you from overwriting a teammate’s work. --force would overwrite without checking.
End of Day: Clean Up
# Switch back to main
git switch main
# Verify clean state
git status
# Optional: delete merged feature branches locally
git branch --merged | grep -v "main" | xargs git branch -d
Production Failure Scenarios + Mitigations
| Scenario | Impact | Mitigation |
|---|---|---|
| Pushing without pulling first | Rejected push, potential conflicts | Always git fetch and git rebase before pushing |
| Pushing broken code | Broken builds, blocked teammates | Run tests and linting before every push |
| Force-pushing to shared branch | Overwrites teammate’s work, lost commits | Use --force-with-lease; never force-push to main |
| Working on main directly | Unstable main, blocks other developers | Always create feature branches |
| Not committing frequently | Lost work if system crashes | Commit at logical checkpoints throughout the day |
| Pushing WIP commits to main branch | Broken production, confused team | Only push WIP to feature branches; clean up before PR |
| Large end-of-day push | Overwhelming PR, difficult review | Push incrementally throughout the day |
Trade-offs
| Approach | Advantages | Disadvantages | When to Use |
|---|---|---|---|
| Rebase before push | Clean linear history, easy to review | Requires conflict resolution, rewrites local history | Feature branches, personal workflow |
| Merge before push | Preserves exact history, no rewriting | Creates merge commits, cluttered log | When history preservation is critical |
| WIP commits | Visible progress, recoverable | Clutters history if not cleaned up | Long features, pair programming |
| Stash | Clean history, temporary | Hidden, easy to forget about | Quick context switches, temporary saves |
| Push frequently | Early feedback, shared progress | May push incomplete work | Collaborative features, pair programming |
| Push at end of day | Polished, reviewed changes | Delayed feedback, larger pushes | Solo work, well-defined tasks |
Implementation Snippets
Complete Daily Workflow Script
#!/bin/bash
# daily-git.sh - Your daily Git workflow helper
BRANCH_NAME=$1
if [ -z "$BRANCH_NAME" ]; then
echo "Usage: ./daily-git.sh <feature-name>"
exit 1
fi
echo "=== Morning Sync ==="
git fetch --all --prune
git switch main
git pull --rebase origin main
echo "=== Create Feature Branch ==="
git switch -c "feature/$BRANCH_NAME"
echo "=== Ready to work ==="
echo "Branch: feature/$BRANCH_NAME"
echo "Base: $(git rev-parse --short HEAD)"
Pre-Push Checklist Script
#!/bin/bash
# pre-push-check.sh - Run before every push
echo "=== Pre-Push Checklist ==="
echo "1. Current branch: $(git branch --show-current)"
echo "2. Status: $(git status --short | wc -l) changed files"
echo "3. Uncommitted changes: $(git diff --stat | tail -1)"
echo "4. Commits to push: $(git log origin/$(git branch --show-current)..HEAD --oneline | wc -l)"
echo "5. Last commit: $(git log -1 --oneline)"
echo ""
echo "6. Running tests..."
npm test
echo ""
echo "7. Running linter..."
npm run lint
echo ""
echo "=== Ready to push ==="
Conflict Resolution Workflow
# When rebase reports conflicts
echo "=== Conflict Resolution ==="
# 1. See which files conflict
git status | grep "both modified"
# 2. For each conflicted file, resolve manually
# Look for <<<<<<<, =======, >>>>>>> markers
# 3. Stage resolved files
git add src/conflicted.py
# 4. Continue rebase
git rebase --continue
# 5. If you need to abort
git rebase --abort
# 6. Verify after resolution
git status
git log --oneline -5
Observability Checklist
- Logs: Use
git log --oneline --since="today"to track daily progress - Metrics: Count commits per day to measure productivity rhythm
- Traces: Use
git diff main...featureto trace all changes in a feature branch - Alerts: CI/CD should block pushes that fail tests or linting
- Audit: Use
git log --format="%h %an %ad %s" --date=shortfor daily standup reports - Health: Check
git statusat end of day to ensure no uncommitted critical work - Validation: Run full test suite before every push to main or shared branches
Security/Compliance Notes
- Never commit secrets: Use
.gitignorefor.envfiles, use secret scanning tools, and configure pre-commit hooks to block secret commits - Signed commits for compliance: In regulated environments, configure
commit.gpgSign trueto sign all daily commits - Branch protection: Configure main branch protection rules to require pull request reviews, status checks, and signed commits
- Audit trail: Your daily commit history is an audit trail. Write meaningful messages that explain the purpose of each change
- Force-push policies: Many organizations prohibit force-push to protected branches. Configure server-side hooks to enforce this
Common Pitfalls / Anti-Patterns
- Working on main: This is the cardinal sin of Git. Main should always be stable. Always branch for any work
- Committing without reviewing: Skipping
git diff --stagedmeans you might commit debug code, wrong files, or incomplete changes - Pushing without pulling: This causes rejected pushes and unnecessary merge commits. Always sync before pushing
- Huge end-of-day commits: One massive commit at 5 PM is impossible to review and likely contains unrelated changes. Commit throughout the day
- Ignoring failing tests before push: Pushing broken code blocks the entire team. Always run tests first
- Using
git push --forceinstead of--force-with-lease: Force-push without lease can overwrite a teammate’s work. Always use--force-with-lease - Not cleaning up feature branches: Accumulating stale branches clutters the repository. Delete merged branches regularly
Quick Recap Checklist
- Morning:
git fetch --all --pruneandgit pull --rebase origin main - Always work on a feature branch, never on main
- Commit atomically — one logical change per commit
- Write meaningful commit messages following Conventional Commits
- Review with
git diff --stagedbefore every commit - Run tests and linting before every push
- Midday:
git rebase origin/mainto incorporate team changes - Review complete branch with
git diff main...featurebefore pushing - Push with
git push -u origin feature/name - Use
--force-with-leasenot--forcewhen rebasing after push - End of day: switch back to main, verify clean state
- Delete merged feature branches to keep repository clean
Interview Q&A
git pull without --rebase creates a merge commit every time you pull, even for simple updates. This clutters the history with "Merge branch 'main'" messages that add no information. git pull --rebase replays your local commits on top of the remote, creating a linear history that is easier to read, bisect, and review. The only time merge is preferred is when you want to preserve the exact topology of how branches were integrated.
--force unconditionally overwrites the remote branch with your local version, regardless of whether someone else has pushed new commits in the meantime. This can permanently delete a teammate's work. --force-with-lease checks that the remote branch has not been updated since you last fetched. If someone else pushed, your push is rejected with an error, protecting their work. Always use --force-with-lease — there is almost never a good reason to use bare --force.
An atomic commit represents a single, complete logical change. It does one thing, does it completely, and leaves the codebase in a working state. It matters because atomic commits enable clean code review (reviewers can understand each change), easy reverting (you can undo one change without affecting others), effective bisecting (finding bugs is faster with smaller commits), and cherry-picking (you can selectively apply specific changes to other branches). A non-atomic commit mixing a bug fix, a refactor, and a new feature is impossible to review or revert cleanly.
Rebase your feature branch onto main before pushing or creating a PR. This creates a clean, linear history where your feature appears on top of the latest main, making the PR easy to review. Merge main into your feature branch only when you need to test integration before the feature is complete, or when your organization's workflow requires it. The general rule: rebase for cleanliness, merge for integration testing. Never rebase a branch that others are working on.
Production Failure: Merge Conflicts During Morning Pull
A developer arrives Monday morning after a weekend of feature work on their branch. They run git pull --rebase origin main and hit a wall — 12 files with conflicts across 3 rebase steps. The main branch received a major refactor over the weekend that reorganized the entire src/ directory structure.
What went wrong:
- Stale base — the developer branched from main on Friday, 3 days old
- No mid-week sync — they never rebased during the weekend work
- Stash conflict — they had uncommitted changes stashed with
git stash, and after resolving rebase conflicts,git stash popcreated additional conflicts in the same files - Lost momentum — 2 hours spent resolving conflicts instead of coding
Mitigation:
# Sync frequently — at least once per day
git fetch origin
git rebase origin/main
# If you have stashed work, apply it carefully
git stash list
git stash pop # resolve any conflicts that arise
# Better: commit before stashing, not just stash
git add -p
git commit -m "WIP: save point before rebase"
git rebase origin/main
Golden rule: Rebase your feature branch onto main at least once per day. The longer you wait, the more painful the merge becomes.
Observability Checklist: CI Pipeline Health After Each Push
- Build status: Verify the CI build passes within 5 minutes of push — check GitHub Actions, GitLab CI, or your platform
- Test results: Confirm all unit, integration, and E2E tests pass — no skipped or flaky tests
- Lint/Format: Ensure no linting or formatting violations — CI should reject non-compliant code
- Security scan: Check that secret scanning, dependency audits, and SAST tools report no new findings
- Deploy preview: If your CI generates preview deployments, verify the preview renders correctly
- Branch protection: Confirm all required status checks are green before marking PR as ready for review
- Artifact integrity: Verify build artifacts (bundles, binaries, containers) are produced without errors
- Performance budget: Check that bundle size, load time, or other performance metrics have not regressed
- Notification: Set up Slack/email alerts for CI failures so you can fix them immediately
- Rollback readiness: If CI deploys automatically, verify the rollback procedure is documented and tested
Quick Recap: End-of-Day Git Hygiene
- Run
git status— working tree should be clean or have only intentional WIP commits - Push your feature branch — do not leave unpushed work that could be lost
- Check CI status — verify all tests and builds pass on the remote
- Clean up stashes —
git stash listshould be empty; apply or drop any lingering stashes - Delete merged branches —
git branch --mergedand remove branches no longer needed - Update main —
git switch main && git pull --rebase origin mainso tomorrow starts fresh - Review tomorrow’s plan — check open issues, PR comments, and pending code reviews
- No dangling commits —
git reflogshould not show orphaned work you forgot to branch - Commit messages are clean — no “fixup”, “wip”, or empty messages in pushed history
- No secrets committed — run
git diff HEAD~5and scan for accidental credentials or keys
Resources
- Pro Git — Git Branching — Official branching guide
- A Successful Git Branching Model — GitFlow model
- Conventional Commits — Commit message standard
- git add — Selective staging techniques
- git commit — Writing effective commit messages
- git status and git diff — Understanding changes
Category
Related Posts
Git Branch Basics: Creating, Switching, Listing, and Deleting Branches
Master the fundamentals of Git branching — creating, switching, listing, and deleting branches. Learn the core commands that enable parallel development workflows.
Git Stash and Stash Management: Save Work Without Committing
Master git stash for saving uncommitted changes, named stashes, stash list management, and when to use stash vs commit in production workflows.
Git Worktree for Parallel Work: Multiple Branches Simultaneously
Master git worktree to work on multiple branches simultaneously without stashing or cloning. Learn parallel development workflows, management commands, and best practices.