Files
howtos/topics/git-branching.md
user 60f9c85f3f docs: add git howto collection
Two reference files covering:
- git.md — config, daily workflow, diff, log, stash, remotes, tags
- git-branching.md — branches, merge, rebase, cherry-pick, bisect, reflog, reset, revert, worktrees
2026-02-21 20:48:58 +01:00

272 lines
6.9 KiB
Markdown

# Git Branching
> Branches, merging strategies, rebasing, and history recovery.
## Branches
```bash
# List
git branch # local
git branch -r # remote
git branch -a # all
git branch -v # with last commit
git branch --merged # merged into current
git branch --no-merged # not yet merged
# Create
git branch feature-login
git checkout -b feature-login # create + switch
git switch -c feature-login # modern syntax
# Switch
git switch main
git checkout main # older syntax
# Rename
git branch -m old-name new-name
git branch -m new-name # rename current
# Delete
git branch -d feature-login # safe (refuses if unmerged)
git branch -D feature-login # force delete
# Cleanup stale remote tracking branches
git fetch --prune
git remote prune origin # same effect
```
## Merge
```bash
# Merge branch into current
git merge feature-login
# Merge with commit message
git merge feature-login -m "feat: integrate login"
# No fast-forward (always create merge commit)
git merge --no-ff feature-login
# Fast-forward only (fail if not possible)
git merge --ff-only feature-login
# Abort in-progress merge
git merge --abort
# Squash (combine all commits, don't auto-commit)
git merge --squash feature-login
git commit -m "feat: login feature"
```
### Merge Strategies
| Strategy | When to use |
|-------------------|--------------------------------------------|
| Fast-forward | Linear history, no divergence |
| `--no-ff` | Preserve branch topology in history |
| `--squash` | Collapse noisy branch into single commit |
| `ours` | Keep our side entirely, discard theirs |
| `recursive` / `ort` | Default for diverged branches |
### Conflict Resolution
```bash
# After merge conflict
git status # shows conflicted files
# Edit files, resolve markers:
# <<<<<<< HEAD
# our changes
# =======
# their changes
# >>>>>>> feature-branch
# Mark resolved
git add resolved-file.txt
git merge --continue
# or
git commit
# Use theirs/ours for a file
git checkout --theirs path/to/file
git checkout --ours path/to/file
```
## Rebase
```bash
# Rebase current branch onto main
git rebase main
# Interactive rebase (rewrite last N commits)
git rebase -i HEAD~5
# Continue after resolving conflicts
git rebase --continue
# Abort rebase
git rebase --abort
# Rebase onto specific base
git rebase --onto main feature-base feature-branch
# Auto-squash fixup commits
git commit --fixup abc123
git rebase -i --autosquash main
```
### Interactive Rebase Commands
| Command | Effect |
|------------|---------------------------------------------|
| `pick` | Keep commit as-is |
| `reword` | Keep commit, edit message |
| `edit` | Pause at commit for amending |
| `squash` | Merge into previous commit, combine messages|
| `fixup` | Merge into previous commit, discard message |
| `drop` | Remove commit entirely |
| `reorder` | Move lines to reorder commits |
### Rebase vs Merge
| | Merge | Rebase |
|---|---|---|
| History | Preserves branch topology | Linear, clean |
| Conflicts | Resolve once | May resolve per commit |
| Shared branches | Safe | **Dangerous** (rewrites history) |
| Use case | Integrating feature | Cleaning up before merge |
## Cherry-Pick
```bash
# Apply specific commit to current branch
git cherry-pick abc123
# Multiple commits
git cherry-pick abc123 def456
# Range (exclusive start)
git cherry-pick abc123..def456
# Without committing (stage only)
git cherry-pick --no-commit abc123
# Abort
git cherry-pick --abort
```
## Bisect
```bash
# Start binary search for a bug
git bisect start
git bisect bad # current commit is broken
git bisect good v1.0 # this tag/commit was working
# Git checks out middle commit — test it, then:
git bisect good # if this commit works
git bisect bad # if this commit is broken
# Repeat until git finds the first bad commit
# Done
git bisect reset
# Automated bisect with a test script (exit 0 = good, exit 1 = bad)
git bisect start HEAD v1.0
git bisect run ./test-script.sh
```
## Reflog — Recovery Safety Net
```bash
# Show reflog (all recent HEAD movements)
git reflog
git reflog show feature-branch # specific branch
# Recover deleted branch
git reflog # find the commit hash
git branch recovered-branch abc123
# Undo a rebase
git reflog # find pre-rebase HEAD
git reset --hard abc123
# Recover amended commit
git reflog # find pre-amend HEAD
git branch pre-amend abc123
# Reflog expires after 90 days (default)
```
## Reset
```bash
# Soft — undo commit, keep staged
git reset --soft HEAD~1
# Mixed (default) — undo commit, unstage, keep working tree
git reset HEAD~1
# Hard — undo commit, discard everything
git reset --hard HEAD~1
# Reset to specific commit
git reset --hard abc123
# Reset single file
git restore --staged file.txt # preferred
git reset HEAD file.txt # older syntax
```
| Mode | Commit | Staging | Working Tree |
|-----------|--------|---------|--------------|
| `--soft` | Undo | Keep | Keep |
| `--mixed` | Undo | Undo | Keep |
| `--hard` | Undo | Undo | **Undo** |
## Revert
```bash
# Create a new commit that undoes a previous one (safe for shared history)
git revert abc123
git revert HEAD # undo last commit
git revert abc123..def456 # range
# Without auto-commit
git revert --no-commit abc123
```
## Worktrees
```bash
# Work on multiple branches simultaneously
git worktree add ../hotfix hotfix-branch
git worktree add ../experiment -b experiment
# List
git worktree list
# Remove
git worktree remove ../hotfix
# Prune stale entries
git worktree prune
```
## Gotchas
- Never rebase commits already pushed to shared branches — it rewrites history
- `git reset --hard` is **permanent** unless you know the reflog hash
- `git branch -D` force-deletes — if unmerged, that work is only in reflog (90 days)
- Cherry-pick creates a **new commit** with a different hash — it duplicates, not moves
- Bisect requires a clean working tree — stash or commit first
- Merge `--squash` does not record the branch as merged — git won't know it was integrated
- Reflog is **local only** — it doesn't sync to remotes
- `revert` of a merge commit needs `-m 1` to specify which parent to keep
## See Also
- `git` — core workflow, config, stash, remotes, tags
- [Pro Git: Branching](https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell)