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.

published: reading time: 17 min read updated: March 31, 2026

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:

  1. Sync — Start with the latest code from the team
  2. Branch — Isolate your work
  3. Commit — Save logical units of work
  4. Review — Verify your changes before sharing
  5. Sync again — Incorporate any new team changes
  6. 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

ScenarioImpactMitigation
Pushing without pulling firstRejected push, potential conflictsAlways git fetch and git rebase before pushing
Pushing broken codeBroken builds, blocked teammatesRun tests and linting before every push
Force-pushing to shared branchOverwrites teammate’s work, lost commitsUse --force-with-lease; never force-push to main
Working on main directlyUnstable main, blocks other developersAlways create feature branches
Not committing frequentlyLost work if system crashesCommit at logical checkpoints throughout the day
Pushing WIP commits to main branchBroken production, confused teamOnly push WIP to feature branches; clean up before PR
Large end-of-day pushOverwhelming PR, difficult reviewPush incrementally throughout the day

Trade-offs

ApproachAdvantagesDisadvantagesWhen to Use
Rebase before pushClean linear history, easy to reviewRequires conflict resolution, rewrites local historyFeature branches, personal workflow
Merge before pushPreserves exact history, no rewritingCreates merge commits, cluttered logWhen history preservation is critical
WIP commitsVisible progress, recoverableClutters history if not cleaned upLong features, pair programming
StashClean history, temporaryHidden, easy to forget aboutQuick context switches, temporary saves
Push frequentlyEarly feedback, shared progressMay push incomplete workCollaborative features, pair programming
Push at end of dayPolished, reviewed changesDelayed feedback, larger pushesSolo 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...feature to 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=short for daily standup reports
  • Health: Check git status at 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 .gitignore for .env files, use secret scanning tools, and configure pre-commit hooks to block secret commits
  • Signed commits for compliance: In regulated environments, configure commit.gpgSign true to 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 --staged means 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 --force instead 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 --prune and git 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 --staged before every commit
  • Run tests and linting before every push
  • Midday: git rebase origin/main to incorporate team changes
  • Review complete branch with git diff main...feature before pushing
  • Push with git push -u origin feature/name
  • Use --force-with-lease not --force when rebasing after push
  • End of day: switch back to main, verify clean state
  • Delete merged feature branches to keep repository clean

Interview Q&A

Why should you use `git pull --rebase` instead of `git pull` (merge)?

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.

What is the difference between `git push --force` and `git push --force-with-lease`?

--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.

What makes a commit "atomic" and why does it matter?

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.

When should you rebase versus merge your feature branch?

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 pop created 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 list should be empty; apply or drop any lingering stashes
  • Delete merged branches — git branch --merged and remove branches no longer needed
  • Update main — git switch main && git pull --rebase origin main so tomorrow starts fresh
  • Review tomorrow’s plan — check open issues, PR comments, and pending code reviews
  • No dangling commits — git reflog should 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~5 and scan for accidental credentials or keys

Resources

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 #version-control #branching

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 #version-control #git-stash

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.

#git #version-control #git-worktree