Claude / Operating Manual · Topics
Git in parallel
Working a shared repo with someone else. Branches, pull requests, merge versus rebase, and the handful of operations that can actually lose work.
updated 2026-07-28
Claude Code commits, branches, rebases and pushes on your behalf, which is fine on a repo only you touch and becomes consequential the moment a second person is in it. This page is the model underneath those operations: what a branch actually is, why a pull request is not a git feature at all, and which four commands are the only ones that can genuinely destroy someone's work.
A branch is a pointer, nothing more
Git does not store folders. It stores commits, immutable snapshots that each point at their parent, forming a chain. A branch is a sticky note with a name on it, pointing at one commit. That is the entire data structure.
When you commit, the sticky note you are standing on slides forward to the new commit. So "being on a branch" just means which sticky note moves when I commit.
This is why everything below is cheap. Creating a branch writes a 40 character file, it does not copy any code. Deleting a branch throws away the note, never the commits.
Where your code actually lives
Four places, two of them invisible, which is what makes this confusing.
| Where | What it is | Moves onward with |
|---|---|---|
| Working tree | The actual files on disk you are editing | git add |
| Staging area | What you have marked for the next commit | git commit |
| Local repository | Your .git, holding every commit and branch, on your machine | git push |
| Remote | The forge copy your collaborator also talks to | git fetch |
Your local repository is a complete independent copy of the entire history. You and your collaborator each have one. Neither is "the real one", GitHub is simply the copy you have both agreed to sync through.
The one that bites:
git commitshares nothing. Nobody can see your commit until you push, and you cannot see theirs until you fetch. Worse,origin/mainin your repo is not GitHub's main. It is your cached memory of where it was the last time you fetched, it goes stale silently, and nothing warns you.
Why parallel work needs branches
If two people commit straight onto main, they are both moving the same sticky note and
every push becomes a collision. Branches give each person their own note to move.
┌── your branch ──● ● ●
│
main ──●────●────●──┤
│
└── theirs ───────● ●
You each commit freely, neither blocks the other, and you merge back when ready.
What a pull request actually is
A pull request is not a git feature. Git has no idea PRs exist. It is a forge feature
(GitHub, GitLab) and it means: here is a branch, please move main forward to include it.
What you buy by asking rather than just doing:
- A review surface. Someone sees the diff and comments on specific lines.
- Somewhere for CI to run before the code reaches
main, rather than after. - A durable record of the why. The description outlives the commits. In six months, "why does this check exist?" has an answer with a URL.
- A clean rollback point. A merged PR is one revert, not seven.
The cost is latency, you wait for a human. On a solo repo that is pure overhead and
committing straight to main is correct. The moment a second person is in the repo, that
calculus flips.
Worth checking before you assume you are alone in a repo:
git log --format=%an | sort -u # who has committed here
gh pr list --state all --limit 10 # is there a PR convention
git branch -r # whose branches are in flight
Merge versus rebase
Both answer the same question: main moved while I was working. Say your branch started at
B and someone landed C in the meantime.
main ──A──B──C
└──X──Y──Z your work
Merge ties the two together with a new commit that has two parents. Nothing is rewritten. True history, but the graph braids.
main ──A──B──C─────────M
└──X──Y──Z┘
Rebase replays your commits on top of the new tip, as though you had started from C.
Linear and much easier to read.
main ──A──B──C──X'──Y'──Z'
Note X'. It is a new commit with a new SHA. Same changes, different identity. Which
gives the rule that matters:
Rebase your own unshared branch freely. Never rebase anything someone else has pulled. If they pulled
X Y Zand you rewrote them intoX' Y' Z', their repo still holds the originals and git now sees two unrelated histories.
There is a payoff beyond tidiness. Rebasing onto the current main means your tests finally
run against your collaborator's latest code instead of a stale tree. A suite that passes on
your branch and passes on main has still never proved the two work together until you do
this.
Fast forward versus force push
Fast forward means the branch only slides forward along an existing chain. Nothing is rewritten and nothing can be lost. This is the ordinary case and git's default.
Force push points a branch at a commit that is not a descendant of where it was. Commits become orphaned, and anyone who pulled them now has a diverging history.
git push --force-with-lease # "only if the remote is still where I last saw it"
git push --force # "I do not care what is there"
Always the first. --force-with-lease aborts if the remote moved since your last fetch,
which is exactly the case where a bare --force silently deletes a colleague's afternoon.
It is not a formality: a mistyped SHA in the lease will refuse the push rather than guess.
The daily loop
Pull main, branch, commit, fetch, rebase, push, open the PR, then delete the branch once it lands. The fetch before the rebase is the step people skip, and it is the one that makes the rebase meaningful.
git checkout main && git pull # start from current reality, always
git checkout -b feat/thing # your own sticky note
# ... work, commit as often as you like ...
git fetch origin # refresh your cached view of the remote
git rebase origin/main # replay onto their latest, re-run the tests
git push -u origin feat/thing # publish the branch
gh pr create # ask for the merge
# ... review, CI, merge on the forge ...
git checkout main && git pull # pick up your own merged work
git branch -d feat/thing # bin the note, the commits live on in main
Lowercase -d refuses to delete an unmerged branch, which makes it a safety net worth
preferring. -D overrides that check.
When you break it
Commits are almost never truly gone. Git keeps a log of every position each branch has held, for around 90 days, and it is local, so it records things that were never pushed.
git reflog # every commit HEAD has pointed at, newest first
git reflog show origin/main # where one specific branch has been
git branch rescue <sha> # name it first, look before leaping
git reset --hard <sha> # then put the branch back where it was
Before any risky move, make a branch at the current commit. It costs nothing and means the work exists under a name you can find, rather than only as a SHA you would have to dig out of the reflog under pressure.
Rules of thumb
git fetchbefore you reason aboutmain. Yourorigin/mainis a cache.- One branch per piece of work. They are free.
- Rebase onto
origin/mainbefore opening the PR, so CI tests the real combination. - Write the PR description for whoever reads it in six months.
- Create the branch before rewinding anything, so the work is never only in one place.
- Do not push straight to
mainon a repo someone else works in, even when nothing stops you. - Do not rebase a branch anyone else has pulled.
- Never bare
--force.--force-with-leasedoes the same job with a seatbelt. - Do not delete or prune someone else's branches, even merged ones. Ask.
Branch protection is the setting that turns the sixth rule from a convention into a mechanism, and it is worth enabling wherever the plan allows it. Until then the convention is the only guardrail, which means it holds exactly as well as the people and agents working in the repo remember it.