---
title: "Advanced Git Operations: Rebase, Cherry-Pick, Bisect, and Repository Maintenance"
description: "Hands-on guide to advanced Git operations — interactive rebase, cherry-pick, bisect, reflog recovery, stash, worktrees, submodules, git archaeology, and repository cleanup."
url: https://agent-zone.ai/knowledge/cicd/git-advanced-operations/
section: knowledge
date: 2026-02-22
categories: ["cicd"]
tags: ["git","rebase","cherry-pick","bisect","advanced"]
skills: ["git-advanced-operations","repository-maintenance","code-archaeology"]
tools: ["git","git-filter-repo"]
levels: ["intermediate"]
word_count: 1169
formats:
  json: https://agent-zone.ai/knowledge/cicd/git-advanced-operations/index.json
  html: https://agent-zone.ai/knowledge/cicd/git-advanced-operations/?format=html
  api: https://api.agent-zone.ai/api/v1/knowledge/search?q=Advanced+Git+Operations%3A+Rebase%2C+Cherry-Pick%2C+Bisect%2C+and+Repository+Maintenance
---


# Advanced Git Operations

These are the commands that separate someone who uses Git from someone who understands it. Each one solves a specific problem that basic Git workflows cannot handle.

## Interactive Rebase

Interactive rebase rewrites commit history. Use it to clean up a branch before merging.

```bash
# Rebase the last 4 commits interactively
git rebase -i HEAD~4
```

This opens an editor with your commits listed oldest-first:

```
pick a1b2c3d feat: add user export endpoint
pick e4f5g6h WIP: export formatting
pick i7j8k9l fix typo in export
pick m0n1o2p feat: add CSV download button
```

Change the commands to reshape history:

```
pick a1b2c3d feat: add user export endpoint
squash e4f5g6h WIP: export formatting
fixup i7j8k9l fix typo in export
pick m0n1o2p feat: add CSV download button
```

- **squash**: merge into previous commit, combine commit messages.
- **fixup**: merge into previous commit, discard the commit message.
- **reword**: keep the commit, edit its message.
- **drop**: delete the commit entirely.
- **edit**: pause rebase at this commit so you can amend it.

Reorder lines to reorder commits. Delete a line to drop that commit.

```bash
# If rebase goes wrong, abort and return to original state
git rebase --abort

# If a conflict occurs during rebase, resolve it then:
git add resolved-file.go
git rebase --continue
```

**Rule: never rebase commits that have been pushed to a shared branch.** Rebase rewrites commit hashes, which breaks anyone who has pulled those commits.

## Cherry-Pick

Apply a specific commit from one branch to another without merging the entire branch.

```bash
# Apply a single commit
git cherry-pick abc1234

# Apply a range of commits (exclusive start, inclusive end)
git cherry-pick abc1234..def5678

# Apply a range inclusive of both endpoints
git cherry-pick abc1234^..def5678

# Cherry-pick without committing (stage changes only)
git cherry-pick --no-commit abc1234
```

Common use case: a bugfix lands on `main` and you need it on `release/2.3`:

```bash
git checkout release/2.3
git cherry-pick abc1234
git push
```

If there is a conflict, resolve it the same way as a merge conflict, then `git cherry-pick --continue`.

## Bisect for Finding Bugs

Binary search through commits to find which one introduced a bug.

```bash
# Start bisect
git bisect start

# Mark the current commit as bad (bug exists here)
git bisect bad

# Mark a known good commit (bug did not exist here)
git bisect good v2.1.0
```

Git checks out a commit halfway between good and bad. Test it, then mark:

```bash
# If the bug exists at this commit
git bisect bad

# If the bug does not exist
git bisect good
```

Repeat until Git identifies the exact commit. For automated bisect:

```bash
# Run a test script at each step -- exit 0 means good, non-zero means bad
git bisect start HEAD v2.1.0
git bisect run ./test-login.sh
```

When done:

```bash
git bisect reset
```

## Reflog for Recovery

The reflog records every position `HEAD` has been at. It is your undo history for Git itself.

```bash
# Show reflog
git reflog

# Output looks like:
# abc1234 HEAD@{0}: commit: feat: add export
# def5678 HEAD@{1}: rebase: checkout main
# ghi9012 HEAD@{2}: commit: WIP changes
```

**Recover from a bad rebase:**

```bash
# Find the commit before the rebase started
git reflog
# Look for the entry before the rebase entries

# Reset to that point
git reset --hard HEAD@{5}
```

**Recover a deleted branch:**

```bash
# Find the last commit on the deleted branch
git reflog | grep "checkout: moving from deleted-branch"

# Create a new branch at that commit
git branch recovered-branch abc1234
```

The reflog is local and expires (default 90 days). It does not exist on remote repositories.

## Stash

Temporarily shelve changes without committing them.

```bash
# Stash all tracked modified files
git stash

# Stash with a description
git stash push -m "WIP: half-finished auth refactor"

# Stash including untracked files
git stash push --include-untracked -m "WIP with new files"

# List stashes
git stash list

# Apply the most recent stash (keep it in stash list)
git stash apply

# Apply and remove from stash list
git stash pop

# Apply a specific stash
git stash apply stash@{2}

# Drop a specific stash
git stash drop stash@{1}

# Clear all stashes
git stash clear
```

Use stash when you need to switch branches but have uncommitted work. Prefer frequent small commits over heavy stash usage -- stashes are easy to forget and lose track of.

## Worktrees

Check out multiple branches simultaneously in separate directories. No more stashing or committing WIP to switch branches.

```bash
# Create a worktree for a different branch
git worktree add ../myproject-hotfix hotfix/critical-bug

# Work in that directory independently
cd ../myproject-hotfix
# Make changes, commit, push

# List worktrees
git worktree list

# Remove when done
git worktree remove ../myproject-hotfix
```

Worktrees share the same `.git` repository, so commits in one are visible from the other. This is ideal for reviewing PRs while keeping your current work intact.

## Submodules vs Subtrees

**Submodules** pin an exact commit of an external repository:

```bash
git submodule add https://github.com/org/shared-lib.git libs/shared
git submodule update --init --recursive
```

Submodules are pointers. The parent repo records a specific commit hash. Updating requires an explicit step. They are notorious for confusing developers who forget to `git submodule update` after pulling.

**Subtrees** copy the external repository's content directly into your tree:

```bash
git subtree add --prefix=libs/shared https://github.com/org/shared-lib.git main --squash
git subtree pull --prefix=libs/shared https://github.com/org/shared-lib.git main --squash
```

Subtrees are simpler for consumers -- cloning the repo gets everything with no extra steps. The tradeoff is a larger repository and more complex update workflow.

**Prefer subtrees** unless you need to push changes back to the external repo or the external repo is very large. For most cases, a package manager (Go modules, npm, pip) is better than either approach.

## Git Archaeology

Find when code was introduced, changed, or deleted.

```bash
# Who last modified each line
git blame src/auth/login.go

# Search for when a string was added or removed
git log -S "DatabaseConnection" --oneline

# Search with regex
git log -G "func.*Export" --oneline

# Follow a file through renames
git log --follow --oneline -- src/auth/login.go

# Show diff for a specific function over time
git log -p -L :handleLogin:src/auth/login.go

# Find commits that touched a specific line range
git log -L 50,75:src/auth/login.go
```

The `-S` flag (pickaxe) finds commits where the number of occurrences of a string changed. `-G` finds commits where the diff matches a regex. Use `-S` to find when something was added or deleted, `-G` to find when it was modified.

## Repository Cleanup

Over time, repositories accumulate large files, dead objects, and bloat.

```bash
# Garbage collection (compresses objects, removes unreachable commits)
git gc --aggressive

# Remove unreachable objects older than 2 weeks
git prune

# Find large files in repository history
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  awk '/^blob/ {print $3, $4}' | sort -rn | head -20
```

To permanently remove large files from history, use `git-filter-repo` (not the deprecated `filter-branch`):

```bash
# Install
pip install git-filter-repo

# Remove a file from all history
git filter-repo --path secrets.env --invert-paths

# Remove files larger than 50MB from history
git filter-repo --strip-blobs-bigger-than 50M
```

After `filter-repo`, all commit hashes change. Every collaborator must re-clone. Use this only when absolutely necessary -- a 500MB repo with an accidentally committed binary is the typical case.

