Branches and merging
A branch lets you work on something without disturbing the main line of the project, then bring the work back when it is ready. By the end of this lesson you can create branches, merge them both ways Git can, and read the graph that results.
A branch is a label
In lesson 1 you saw that commits form a chain, each pointing at its parent. A branch is nothing more than a name that points at one commit. Git stores it as a tiny file holding that commit's hash.
c4906d4 ◄── 11d88db ◄── f11adbc ◄── 719f474 ◄── dce44e0
▲
mainWhen you commit on a branch, Git creates the new commit and moves the label forward to it. The label always points at the newest commit on that branch, called its tip.
Because a branch is only a label, creating one is instant and costs nothing, however big the project. Use them freely: one branch per task is normal.
HEAD: where you are
You can have many branches, but you are on one at a time. HEAD is Git's name for "the branch you are on now". Git stores it as a pointer to a branch:
cat .git/HEADref: refs/heads/mainSo HEAD points at main, and main points at a commit. When you commit, the branch HEAD points at is the one that moves.
Detached HEAD
If you check out a commit directly (for example git switch --detach 11d88db) instead of a branch, HEAD points at the commit itself. Git calls this detached HEAD. It is fine for looking around. Commits you make there belong to no branch, so create one (git switch -c new-name) before you leave, or they become hard to find.
Create and switch branches
git branch* maingit branch lists your branches; the * marks the one you are on. Create a branch and switch to it in one go:
git switch -c day-planSwitched to a new branch 'day-plan'Now commit something on it:
printf '# Day plan\n\nDay 1: Arrive, walk the old town\n' > days.md
git add days.md
git commit -m "Add day plan"[day-plan ec2d506] Add day plan
1 file changed, 3 insertions(+)
create mode 100644 days.md main
▼
… ◄── 719f474 ◄── dce44e0 ◄── ec2d506
▲
day-plan, HEADSwitch back to main and look at the folder:
git switch main
lsREADME.md
packing.txtdays.md is gone from the folder, because it does not exist on main yet. It is not lost: it is safe in the commit on day-plan. Switching branches changes the files on disk to match the branch.
| Command | What it does |
|---|---|
git branch | List local branches |
git switch name | Switch to an existing branch |
git switch -c name | Create a branch at the current commit and switch to it |
git switch - | Switch back to the previous branch |
git branch -d name | Delete a branch that has been merged |
Older guides use git checkout name and git checkout -b name. They still work; git switch was added in Git 2.23 to do this one job with fewer surprises.
When Git will not switch
If you have uncommitted edits that do not clash with the other branch, Git carries them over to it. If switching would overwrite them, Git refuses:
error: Your local changes to the following files would be overwritten by checkout:
packing.txt
Please commit your changes or stash them before you switch branches.
AbortingCommit the edits, or put them aside with git stash (lesson 6), then switch.
Merge: bring work back
Merging combines another branch's work into the branch you are on. You always merge into the current branch, so first switch to the branch that should receive the work.
Fast-forward
main has no new commits since day-plan started. Git does not need to combine anything: it moves the main label forward to the tip of day-plan.
git switch main
git merge day-planUpdating dce44e0..ec2d506
Fast-forward
days.md | 3 +++
1 file changed, 3 insertions(+)
create mode 100644 days.mdBefore: After:
main main, day-plan
▼ ▼
… ◄── dce44e0 ◄── ec2d506 … ◄── dce44e0 ◄── ec2d506
▲
day-planNo new commit was created. The history stays a straight line.
Merge commit
Now both branches move on. You start a budget branch and commit on it, then go back to main and commit there too. The two lines of work have split, or diverged:
git log --oneline --graph --all* 86d5275 Add budget
| * 63e3bcf Plan day 2
|/
* ec2d506 Add day plan
* dce44e0 Ignore local settings, logs and build output
…A fast-forward is impossible: main cannot jump to 86d5275 without losing 63e3bcf. So Git makes a merge commit, a commit with two parents that joins the two lines:
git merge budgetMerge made by the 'ort' strategy.
budget.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 budget.txtGit opens your editor for the merge message, with Merge branch 'budget' filled in; save and close to accept it. (ort is the name of Git's default merge method; you can ignore it.)
git log --oneline --graph --all* 7aa942b Merge branch 'budget'
|\
| * 86d5275 Add budget
* | 63e3bcf Plan day 2
|/
* ec2d506 Add day plan
* dce44e0 Ignore local settings, logs and build output
…| Fast-forward | Merge commit | |
|---|---|---|
| When | The current branch has no commits of its own since the split | Both branches have new commits |
| New commit? | No, the label moves | Yes, with two parents |
| History shape | A straight line | Two lines that join |
If both branches changed the same lines of the same file, Git cannot decide which version wins and stops with a conflict. That is lesson 5.
Read a commit graph
git log --oneline --graph --all draws the history as text. --all includes every branch, not only the current one. Add --decorate if your terminal does not show branch names:
* 7aa942b (HEAD -> main) Merge branch 'budget'
|\
| * 86d5275 (budget) Add budget
* | 63e3bcf Plan day 2
|/
* ec2d506 (day-plan) Add day plan- Each
*is a commit. Newest is on top. |,/and\are the lines between parents and children.|/is where a branch split off;|\is where two lines joined in a merge commit.- Names in brackets are labels:
HEAD -> mainmeans you are onmain, and it points here.budgetandday-planshow where those branches point.
BranchPilot's commit graph shows the same thing with colours: each dot is a commit, a hollow ring is a merge commit, each branch has its own lane, and labels mark branches, tags and HEAD. Right-click a branch and choose Merge into current… to merge; double-click a branch to switch to it.
Tidy up merged branches
Once a branch is merged, its label is no longer needed. The commits stay in the history either way.
git branch -d budget day-planDeleted branch budget (was 86d5275).
Deleted branch day-plan (was ec2d506).-d refuses to delete a branch with commits that are not merged anywhere:
error: the branch 'other' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D other'-D deletes it anyway. Do that only if you are sure you no longer need those commits. BranchPilot asks the same question with Branch not fully merged and a Force delete button.
Good branch habits
- Keep
mainworking. Do each piece of work on its own branch and merge it when it is done. - Use short, descriptive names in lower case with hyphens:
fix-login-timeout,add-day-plan. Many teams add a prefix such asfeature/orfix/. - Merge or delete branches when they are finished, so the list stays readable.
Check yourself
Question 1 of 6
What is a branch in Git?
What you learned
- A branch is a label that points at a commit and moves forward when you commit.
- HEAD says which branch you are on; switching branches changes the files on disk.
git switch -ccreates and switches;git mergebrings another branch into the current one.- A fast-forward moves the label; a merge commit has two parents and joins two lines of work.
git log --oneline --graph --alland BranchPilot's graph show how branches split and join.
Next steps
- Remotes: share your branches with other people
- Branch and merge in BranchPilot