Back to Skills
    🦞

    git-workflows

    Advanced git operations beyond add/commit/push.

    By @gitgoodordietrying
    View on GitHub
    SKILL.md
    ---
    name: git-workflows
    description: Advanced git operations beyond add/commit/push. Use when rebasing, bisecting bugs, using worktrees for parallel development, recovering with reflog, managing subtrees/submodules, resolving merge conflicts, cherry-picking across branches, or working with monorepos.
    metadata: {"clawdbot":{"emoji":"🌿","requires":{"bins":["git"]},"os":["linux","darwin","win32"]}}
    ---
    
    # Git Workflows
    
    Advanced git operations for real-world development. Covers interactive rebase, bisect, worktree, reflog recovery, subtrees, submodules, sparse checkout, conflict resolution, and monorepo patterns.
    
    ## When to Use
    
    - Cleaning up commit history before merging (interactive rebase)
    - Finding which commit introduced a bug (bisect)
    - Working on multiple branches simultaneously (worktree)
    - Recovering lost commits or undoing mistakes (reflog)
    - Managing shared code across repos (subtree/submodule)
    - Resolving complex merge conflicts
    - Cherry-picking commits across branches or forks
    - Working with large monorepos (sparse checkout)
    
    ## Interactive Rebase
    
    ### Squash, reorder, edit commits
    
    ```bash
    # Rebase last 5 commits interactively
    git rebase -i HEAD~5
    
    # Rebase onto main (all commits since diverging)
    git rebase -i main
    ```
    
    The editor opens with a pick list:
    
    ```
    pick a1b2c3d Add user model
    pick e4f5g6h Fix typo in user model
    pick i7j8k9l Add user controller
    pick m0n1o2p Add user routes
    pick q3r4s5t Fix import in controller
    ```
    
    Commands available:
    ```
    pick   = use commit as-is
    reword = use commit but edit the message
    edit   = stop after this commit to amend it
    squash = merge into previous commit (keep both messages)
    fixup  = merge into previous commit (discard this message)
    drop   = remove the commit entirely
    ```
    
    ### Common patterns
    
    ```bash
    # Squash fix commits into their parent
    # Change "pick" to "fixup" for the fix commits:
    pick a1b2c3d Add user model
    fixup e4f5g6h Fix typo in user model
    pick i7j8k9l Add user controller
    fixup q3r4s5t Fix import in controller
    pick m0n1o2p Add user routes
    
    # Reorder commits (just move lines)
    pick i7j8k9l Add user controller
    pick m0n1o2p Add user routes
    pick a1b2c3d Add user model
    
    # Split a commit into two
    # Mark as "edit", then when it stops:
    git reset HEAD~
    git add src/model.ts
    git commit -m "Add user model"
    git add src/controller.ts
    git commit -m "Add user controller"
    git rebase --continue
    ```
    
    ### Autosquash (commit messages that auto-arrange)
    
    ```bash
    # When committing a fix, reference the commit to squash into
    git commit --fixup=a1b2c3d -m "Fix typo"
    # or
    git commit --squash=a1b2c3d -m "Additional changes"
    
    # Later, rebase with autosquash
    git rebase -i --autosquash main
    # fixup/squash commits are automatically placed after their targets
    ```
    
    ### Abort or continue
    
    ```bash
    git rebase --abort      # Cancel and restore original state
    git rebase --continue   # Continue after resolving conflicts or editing
    git rebase --skip       # Skip the current commit and continue
    ```
    
    ## Bisect (Find the Bug)
    
    ### Binary search through commits
    
    ```bash
    # Start bisect
    git bisect start
    
    # Mark current commit as bad (has the bug)
    git bisect bad
    
    # Mark a known-good commit (before the bug existed)
    git bisect good v1.2.0
    # or: git bisect good abc123
    
    # Git checks out a middle commit. Test it, then:
    git bisect good   # if this commit doesn't have the bug
    git bisect bad    # if this commit has the bug
    
    # Repeat until git identifies the exact commit
    # "abc123 is the first bad commit"
    
    # Done — return to original branch
    git bisect reset
    ```
    
    ### Automated bisect (with a test script)
    
    ```bash
    # Fully automatic: git runs the script on each commit
    # Script must exit 0 for good, 1 for bad
    git bisect start HEAD v1.2.0
    git bisect run ./test-for-bug.sh
    
    # Example test script
    cat > /tmp/test-for-bug.sh << 'EOF'
    #!/bin/bash
    # Return 0 if bug is NOT present, 1 if it IS
    npm test -- --grep "login should redirect" 2>/dev/null
    EOF
    chmod +x /tmp/test-for-bug.sh
    git bisect run /tmp/test-for-bug.sh
    ```
    
    ### Bisect with build failures
    
    ```bash
    # If a commit doesn't compile, skip it
    git bisect skip
    
    # Skip a range of known-broken commits
    git bisect skip v1.3.0..v1.3.5
    ```
    
    ## Worktree (Parallel Branches)
    
    ### Work on multiple branches simultaneously
    
    ```bash
    # Add a worktree for a different branch
    git worktree add ../myproject-hotfix hotfix/urgent-fix
    # Creates a new directory with that branch checked out
    
    # Add a worktree with a new branch
    git worktree add ../myproject-feature -b feature/new-thing
    
    # List worktrees
    git worktree list
    
    # Remove a worktree when done
    git worktree remove ../myproject-hotfix
    
    # Prune stale worktree references
    git worktree prune
    ```
    
    ### Use cases
    
    ```bash
    # Review a PR while keeping your current work untouched
    git worktree add ../review-pr-123 origin/pr-123
    
    # Run tests on main while developing on feature branch
    git worktree add ../main-tests main
    cd ../main-tests && npm test
    
    # Compare behavior between branches side by side
    git worktree add ../compare-old release/v1.0
    git worktree add ../compare-new release/v2.0
    ```
    
    ## Reflog (Recovery)
    
    ### See everything git remembers
    
    ```bash
    # Show reflog (all HEAD movements)
    git reflog
    # Output:
    # abc123 HEAD@{0}: commit: Add feature
    # def456 HEAD@{1}: rebase: moving to main
    # ghi789 HEAD@{2}: checkout: moving from feature to main
    
    # Show reflog for a specific branch
    git reflog show feature/my-branch
    
    # Show with timestamps
    git reflog --date=relative
    ```
    
    ### Recover from mistakes
    
    ```bash
    # Undo a bad rebase (find the commit before rebase in reflog)
    git reflog
    # Find: "ghi789 HEAD@{5}: checkout: moving from feature to main" (pre-rebase)
    git reset --hard ghi789
    
    # Recover a deleted branch
    git reflog
    # Find the last commit on that branch
    git branch recovered-branch abc123
    
    # Recover after reset --hard
    git reflog
    git reset --hard HEAD@{2}   # Go back 2 reflog entries
    
    # Recover a dropped stash
    git fsck --unreachable | grep commit
    # or
    git stash list  # if it's still there
    git log --walk-reflogs --all -- stash  # find dropped stash commits
    ```
    
    ## Cherry-Pick
    
    ### Copy specific commits to another branch
    
    ```bash
    # Pick a single commit
    git cherry-pick abc123
    
    # Pick multiple commits
    git cherry-pick abc123 def456 ghi789
    
    # Pick a range (exclusive start, inclusive end)
    git cherry-pick abc123..ghi789
    
    # Pick without committing (stage changes only)
    git cherry-pick --no-commit abc123
    
    # Cherry-pick from another remote/fork
    git remote add upstream https://github.com/other/repo.git
    git fetch upstream
    git cherry-pick upstream/main~3   # 3rd commit from upstream's main
    ```
    
    ### Handle conflicts during cherry-pick
    
    ```bash
    # If conflicts arise:
    # 1. Resolve conflicts in the files
    # 2. Stage resolved files
    git add resolved-file.ts
    # 3. Continue
    git cherry-pick --continue
    
    # Or abort
    git cherry-pick --abort
    ```
    
    ## Subtree and Submodule
    
    ### Subtree (simpler — copies code into your repo)
    
    ```bash
    # Add a subtree
    git subtree add --prefix=lib/shared https://github.com/org/shared-lib.git main --squash
    
    # Pull updates from upstream
    git subtree pull --prefix=lib/shared https://github.com/org/shared-lib.git main --squash
    
    # Push local changes back to upstream
    git subtree push --prefix=lib/shared https://github.com/org/shared-lib.git main
    
    # Split subtree into its own branch (for extraction)
    git subtree split --prefix=lib/shared -b shared-lib-standalone
    ```
    
    ### Submodule (pointer to another repo at a specific commit)
    
    ```bash
    # Add a submodule
    git submodule add https://github.com/org/shared-lib.git lib/shared
    
    # Clone a repo with submodules
    git clone --recurse-submodules https://github.com/org/main-repo.git
    
    # Initialize submodules after clone (if forgot --recurse)
    git submodule update --init --recursive
    
    # Update submodules to latest
    git submodule update --remote
    
    # Remove a submodule
    git rm lib/shared
    rm -rf .git/modules/lib/shared
    # Remove entry from .gitmodules if it persists
    ```
    
    ### When to use which
    
    ```
    Subtree: Simpler, no special commands for cloners, code lives in your repo.
             Use when: shared library, vendor code, infrequent upstream changes.
    
    Submodule: Pointer to exact commit, smaller repo, clear separation.
               Use when: large dependency, independent release cycle, many contributors.
    ```
    
    ## Sparse Checkout (Monorepo)
    
    ### Check out only the directories you need
    
    ```bash
    # Enable sparse checkout
    git sparse-checkout init --cone
    
    # Select directories
    git sparse-checkout set packages/my-app packages/shared-lib
    
    # Add another directory
    git sparse-checkout add packages/another-lib
    
    # List what's checked out
    git sparse-checkout list
    
    # Disable (check out everything again)
    git sparse-checkout disable
    ```
    
    ### Clone with sparse checkout (large monorepos)
    
    ```bash
    # Partial clone + sparse checkout (fastest for huge repos)
    git clone --filter=blob:none --sparse https://github.com/org/monorepo.git
    cd monorepo
    git sparse-checkout set packages/my-service
    
    # No-checkout clone (just metadata)
    git clone --no-checkout https://github.com/org/monorepo.git
    cd monorepo
    git sparse-checkout set packages/my-service
    git checkout main
    ```
    
    ## Conflict Resolution
    
    ### Understand the conflict markers
    
    ```
    <<<<<<< HEAD (or "ours")
    Your changes on the current branch
    =======
    Their changes from the incoming branch
    >>>>>>> feature-branch (or "theirs")
    ```
    
    ### Resolution strategies
    
    ```bash
    # Accept all of ours (current branch wins)
    git checkout --ours path/to/file.ts
    git add path/to/file.ts
    
    # Accept all of theirs (incoming branch wins)
    git checkout --theirs path/to/file.ts
    git add path/to/file.ts
    
    # Accept ours for ALL files
    git checkout --ours .
    git add .
    
    # Use a merge tool
    git mergetool
    
    # See the three-way diff (base, ours, theirs)
    git diff --cc path/to/file.ts
    
    # Show common ancestor version
    git show :1:path/to/file.ts   # base (common ancestor)
    git show :2:path/to/file.ts   # ours
    git show :3:path/to/file.ts   # theirs
    ```
    
    ### Rebase conflict workflow
    
    ```bash
    # During rebase, conflicts appear one commit at a time
    # 1. Fix the conflict in the file
    # 2
    
    ... (truncated)