Git tutorial: teamwork with branches, merge and rebase
verified on 7 September 2026 · 10 min
Create a branch with git switch -c nom, do the work, then merge it with git merge from the destination branch. When there is a conflict, Git inserts markers in the file: write the correct content, delete the markers, then git add and git commit. git merge --abort undoes the whole thing at any point.
A branch is a parallel line of development. It lets you work on a feature without touching the stable version, and lets a colleague do the same on their side. This chapter covers the full cycle: creating a branch, merging it, resolving a conflict by hand, and choosing between merge and rebase.
git switch rather than git checkout
Historically, git checkout did everything: switching branches, creating one, restoring a file, moving onto a specific commit. That pile-up of roles is the first source of confusion for beginners, and a source of accidents for everyone else.
Git 2.23, released in August 2019, split the command in two: git switch to move between branches, git restore to undo changes. Both were marked experimental when they shipped, the official documentation no longer says so. This chapter uses them, and always gives the checkout equivalent, which you will keep meeting in tutorials and on Stack Overflow for a long time yet.
| Intent | Modern command | Older form |
|---|---|---|
| Switch branch | git switch nom | git checkout nom |
| Create a branch and move onto it | git switch -c nom | git checkout -b nom |
| Go back to the previous branch | git switch - | git checkout - |
| Undo the changes made to a file | git restore fichier | git checkout -- fichier |
| Take a file out of the index | git restore --staged fichier | git reset HEAD fichier |
Creating a branch and working on it
git switch -c feature/panierSwitched to a new branch 'feature/panier'The branch starts from the commit you were on. You can now edit, add and commit as usual: none of it will show up on main until you merge it.
Git imposes no rule on branch names, but the prefixes feature/, fix/ and hotfix/ are a widespread convention, and hosting platforms group them visually in their interfaces.
To see where you stand:
git branch # the local branches
git branch -vv # with the last commit and the remote branch being tracked
git branch -a # local and remote
git switch - # back to the previous branchTo publish the branch and make it visible to the team:
git push -u origin feature/panierPicking up a colleague’s branch
The other way round, git fetch updates what you know about the server without changing anything in your own branches:
git fetch origin feature/collegue # one specific branch
git fetch --all # every remote
git branch -r # what you know about the server origin/HEAD -> origin/main
origin/feature/collegue
origin/mainAll that is left is to switch onto it. Git creates the local branch and links it to the one on the server for you:
git switch feature/colleguebranch 'feature/collegue' set up to track 'origin/feature/collegue'.
Switched to a new branch 'feature/collegue'When Git refuses to switch branches
This is one of the first errors you will run into:
error: Your local changes to the following files would be overwritten by checkout:
index.html
Please commit your changes or stash them before you switch branches.
AbortingGit is protecting your work: the file you have modified does not have the same content on the other branch, and switching would overwrite it. Three ways out, in order of preference.
Commit, if the work holds together. This is almost always the right answer, an imperfect commit on a working branch costs nothing and can be fixed later with the undo commands covered in the previous chapter.
Stash, if the work is half done:
git stash # stashes the changes
git switch main # …you do whatever you had to do…
git switch -
git stash pop # brings the changes backSaved working directory and index state WIP on main: 6d495d9 Page d accueilThrow it away, with git restore ., if the changes were worth nothing. With no way back.
Merging a branch
Once the feature is finished, move onto the destination branch and merge:
git switch main
git merge feature/panierWhen the two branches have changed different files, Git merges on its own and has nothing to ask you. The interesting case is the other one.
Resolving a conflict
Here is the situation set up for this chapter. On feature/panier, the second line of index.html was replaced by an empty-basket message. On main, the first line was changed to add the brand name. Both branches touched the same file.
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.Nothing is broken. Git merged what it could and leaves the rest for you to settle. First thing to do, ask where you stand:
git statusOn branch main
Your branch is ahead of 'origin/main' by 1 commit.
(use "git push" to publish your local commits)
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")To get only the list of files to deal with:
git diff --name-only --diff-filter=UOpen the file. Git has inserted markers in it:
<<<<<<< HEAD
<h1>Boutique Gekkode</h1>
<p>Bienvenue sur la boutique.</p>
=======
<h1>Boutique</h1>
<p>Votre panier est vide.</p>
>>>>>>> feature/panierReading it is mechanical. Between <<<<<<< HEAD and ======= is the version from the branch you are on, here main. Between ======= and >>>>>>> is the version from the branch you are merging in.
Writing the correct result is your job. It is not necessarily one side or the other: here, both changes are good and have to live together.
<h1>Boutique Gekkode</h1>
<p>Votre panier est vide.</p>Delete the three markers, they must not stay in the file. Then tell Git the matter is settled, and finish the merge:
git add index.html
git commit -m "Fusionne feature/panier dans main"* 0899b0c Fusionne feature/panier dans main
|
| * aacf4c0 Panier : message quand le panier est vide
* | 11ff16b Ajoute le nom de la marque au titre
|/
* 6d495d9 Page d accueilThe merge commit has two parents, which the graph makes visible.
Calling it off and starting over
If the conflict is beyond you, nothing forces your hand:
git merge --abortThe repository goes back to exactly the state it was in before the merge command. Nothing is lost: you can try again later, or talk it over with the author of the other branch.
merge or rebase
Both bring the work of one branch into another, but not in the same way.
git merge creates a merge commit that ties the two histories together. The history keeps an exact record of what happened, including the fact that two branches existed in parallel. The graph is faithful, but it forks.
git rebase replays your commits on top of the target branch, as if you had worked from its latest version. The history stays a straight line, easier to read, but it is rewritten: your commits get new hashes.
git switch feature/panier
git rebase mainA rebase can hit the same conflicts as a merge, commit by commit. Resolving them is identical, only the command to carry on differs:
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
error: could not apply 4937109... Promo
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
hint: Disable this message with "git config set advice.mergeConflict false"
Could not apply 4937109... Promogit add index.html
git rebase --continue
# or, to call the whole thing off
git rebase --abortThe golden rule fits in one sentence: never rebase a branch that other people have already pulled. Rewriting a shared history forces your colleagues to repair theirs by hand. On your own working branch, before you put it forward, a rebase is perfectly legitimate and gives a far more readable history.
The everyday case: someone pushed before you
You try to push, and Git refuses:
To /root/o.git
! [rejected] main -> main (fetch first)
error: failed to push some refs to '/root/o.git'
hint: Updates were rejected because the remote contains work that you do notThe server has commits you do not. You have to bring them in first. And on a fresh repository, Git asks you a question instead of acting:
hint: You have divergent branches and need to specify how to reconcile them.
hint: You can do so by running one of the following commands sometime before
hint: your next pull:
hint:
hint: git config pull.rebase false # merge
hint: git config pull.rebase true # rebase
hint: git config pull.ff only # fast-forward only
hint:Git has refused to choose on your behalf since version 2.27. Answer once and for all, as suggested in the installation chapter:
git config --global pull.rebase trueThe sequence then looks like this:
git pull --rebase
git pushFrom /root/o
622a60b..38b7dc5 main -> origin/main
Rebasing (1/1)Successfully rebased and updated refs/heads/main.
* 5b45117 Ajoute app.js
* 38b7dc5 Ajoute la doc
* 622a60b DepartYour commit has been replayed on top of your colleague’s and the history has stayed linear, with no stray merge commit. This is the setting to recommend to a team that is starting out.
One warning to finish with: never try to unblock a rejected push with git push --force. That command overwrites whatever is on the server. If you really do have to force, after rebasing a branch of your own, use git push --force-with-lease, which refuses to overwrite commits you had not seen.
Cleaning up
A merged branch has no reason to exist any more. If it was published with -u, push its latest commits first: Git refuses to delete a local branch that is ahead of its remote branch, even when it is merged into main.
git push origin feature/panier
git branch -d feature/panierDeleted branch feature/panier (was bc2203a).Without that prior push, the message is explicit:
warning: not deleting branch 'feature/panier' that is not yet merged to
'refs/remotes/origin/feature/panier', even though it is merged to HEAD
error: the branch 'feature/panier' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D feature/panier'
hint: Disable this message with "git config set advice.forceDeleteBranch false"If the branch has not been merged, Git stops you, which is a useful safety net:
error: the branch 'feature/promo' is not fully merged
hint: If you are sure you want to delete it, run 'git branch -D feature/promo'
hint: Disable this message with "git config set advice.forceDeleteBranch false"To delete the branch on the server as well, and clear out local references that have gone stale:
git push origin --delete feature/panier
git fetch --prunegit fetch retrieves the state of the server without merging anything into your branches, unlike git pull. The --prune option also removes the remote branches that no longer exist, the ones your colleagues have merged and deleted.
You now know how to collaborate. The last chapter is about organisation: which branching model to adopt for your team.
Common errors
git stash, before switching branches.git pull refuses to choose between merging and rebasing on its own. Settle the question with git config --global pull.rebase true.git pull --rebase. Never use git push --force to get past it: it overwrites other people’s work.<<<<<<<, ======= and >>>>>>> have to be gone from the file before the git add.