Table of Contents
- 2.1 About this course
- 2.2 In-flight branching — the “oops” path
- 2.3 Move from one branch to another
- 2.4 Rename and delete branches
- 3.1 Compare and merge branches
- 3.2 Understanding Git Diff
- 3.3 Using Git Diff in practice
- 3.4 Resolve merge conflicts
- 3.5 Abort a merge in progress
- 4.1 Configure remotes
- 4.2 Use remotes with code
- 4.3 Using remotes with branches
- 4.4 Use Pull Requests
- 4.5 Ignoring files with .gitignore
- 4.6 Establishing team agreements
- 4.7 Write code that can easily merge
- 5.1 Reorder history with Rebase
- 5.2 Squasher multiple commits
- 5.3 Rebase from main
- 5.4 Use Cherry Pick
- 5.5 Cherry Pick demos
- 5.6 Conclusion
1. Course Overview
This course, Git Branching and Merging, was designed for anyone who may have used Git before but finds branching complex or intimidating. The goal is to understand both the why and the how of Git branches, so that you can comfortably integrate them into your daily workflow.
Main topics covered
- The basics of branching: create, rename, delete branches
- Compare and merge branches: use of
git diffandgit merge - Remotes and Pull Requests: share code and collaborate as a team
- Strategies to avoid and resolve conflicts:
rebaseandcherry-pick
Learning Objective
At the end of this course, you will be able to use Git branches to better manage and track your own code, and to more effectively coordinate work in a shared codebase with your team.
Prerequisites
Have a basic understanding of Git before starting.
2. Understand the basics of Git branches
Module duration: 32m 27s
2.1 About this course
Whether you’re just starting out or have been working with Git for a while, branching is an integral part of writing code. Learning to use branches more effectively can truly improve your productivity and reduce your frustration levels.
Environment used in the course
The course was created with specific versions of Git. Since branching is a fundamental concept of how Git works, the principles taught apply to any version. The demonstrations use the command line, available in any environment, accompanied by an IDE to visualize the directory structure, file contents and a branch and commit diagram updating in real time.
The main vs master convention
You may notice that some codebases still use the name master. This term refers to the English definition of the adjective master (main, like the main water pipe in a building that supplies all branches). In October 2020, GitHub changed the default branch name for new repositories from master to main. This course uses the term main to refer to the first or main branch of a repository, following current convention.
2.2 In-flight branching — the “oops” path
The problem
The reality of large software projects is that they are too large and complex for a single person to understand all the code. Multiple teams of developers are required to create and maintain all of the code. Git branches help manage this, whether working alone or in a team.
The simplest use case is the so-called “oops path” (the error path): you started doing a quick fix on main, then you realize that it’s not that fast and you need to do something else on main before you finish. How to save your work in progress and come back to it later?
The solution with branches
- Create a branch to commit all existing changes
- Continue working in this branch now or later
- Return to
main, which will be in the same state as before the modifications began
It’s like a time machine: main returns to exactly the state it was in before you started your changes.
Demonstration: installation
# Initialiser un répertoire vide comme dépôt Git
git init
# Créer un fichier de bug et y ajouter du contenu
# (créez le fichier et ajoutez du texte exemple, puis sauvegardez)
# Ajouter et committer le fichier
git add .
git commit -m "Initial commit"
# Faire des modifications représentant l'état actuel de main
# (modifiez le fichier)
# Créer une branche pour sauvegarder les changements en cours
git checkout -b quickfix
# Committer les changements sur la nouvelle branche
git add .
git commit -m "Work in progress on quickfix"
# Retourner sur main — il est revenu à son état précédent
git switch main
# ou : git checkout main
Note: The course exercise files contain the commands for each demonstration as well as the slides. You can download them to follow the examples.
2.3 Moving from one branch to another
The problem
Suppose you are not sure how to approach a certain problem. You start trying something and realize it doesn’t work. How do you get back to a good starting point? The classic options — undo or delete and start again — have a limit: If you realize later that you actually had most of the solution and were only missing a little work, you can’t get back what was deleted.
The solution with branches
Branching allows you to:
- Commit initial code to a branch
- Create a new branch when starting something uncertain
- If the code doesn’t work, create another branch, remove this code and try something else
- If you need code from a previous attempt, return to the corresponding branch to retrieve it
This helps keep things organized and separated, while still being able to easily go back if needed.
Demonstration: Navigating between attempts
# Initialiser un nouveau dépôt
git init
# Créer le code de base et committer sur main
git add .
git commit -m "Initial code on main"
# Créer une première branche pour tenter une solution
git checkout -b attempt1
# Travailler sur une première approche (modifications de fichiers)
git add .
git commit -m "First attempt - partial solution"
# Revenir sur main pour repartir proprement
git switch main
# Créer une deuxième branche pour une nouvelle tentative
git checkout -b attempt2
# Travailler sur la solution — réaliser qu'on a besoin de quelque chose de attempt1
git add .
git commit -m "Partial solution on attempt2"
# Revenir sur attempt1 pour copier ce dont on a besoin
git switch attempt1
# Copier le code nécessaire, puis retourner sur attempt2
git switch attempt2
# Compléter la solution
git add .
git commit -m "Complete solution"
Tip: Branching in Git is very lightweight. Feel free to branch frequently to isolate your experiments.
2.4 Rename and delete branches
Rename a branch
There are two famous problems in computing: cache invalidation, naming things, and offset-one errors. If you don’t like the name of a branch, no problem: you can rename it.
General syntax:
# Renommer une branche (depuis n'importe quelle branche)
git branch -m <ancien-nom> <nouveau-nom>
# Exemple : renommer "quickfix" en "longfix"
git branch -m quickfix longfix
Shortcut to rename the current branch:
# Renommer la branche sur laquelle vous vous trouvez actuellement
git branch -m <nouveau-nom>
# Exemple : renommer la branche courante en "hotfix1"
git branch -m hotfix1
Demonstration: renaming branches
# Lister les branches existantes
git branch
# Résultat : main, quickfix
# Depuis main, renommer quickfix en longfix
git branch -m quickfix longfix
# Vérifier le renommage
git branch
# Résultat : main, longfix
# Basculer sur longfix
git switch longfix
# ou : git checkout longfix
# Renommer la branche courante (longfix) en hotfix1 avec le raccourci
git branch -m hotfix1
# Vérifier
git status
# Résultat : On branch hotfix1
git branch
# Résultat : main, hotfix1 (longfix n'existe plus)
Delete a branch
If you are not careful, you can find yourself surrounded by many branches on your workstation. It can become tedious to scroll through pages of branch names every time you run git branch. Deleting branches in Git is simple and requires no physical manipulation.
Prerequisites:
- You cannot delete the branch you are currently on
- You must switch to another branch before deleting
Syntax:
# Supprimer une branche (après avoir basculé sur une autre branche)
git branch -d <nom-de-la-branche>
# Exemple : supprimer la branche "hotfix1" depuis main
git switch main
git branch -d hotfix1
# Forcer la suppression (même si la branche n'a pas été mergée)
git branch -D <nom-de-la-branche>
Best practice: Regularly delete branches that you no longer need to keep your repository clean and readable.
3. Simplified merge
Module duration: 39m 35s
3.1 Compare and merge branches
From branching to merge
You may have discovered the solution to a problem on a branch other than main. The natural question is: how to bring this solution back into main? The answer is merge.
The Fast Forward Merge
If no additional work has occurred on main since the branch was created, Git can perform a fast forward merge. This is the simplest form of merge: the main reference is simply advanced to the last commit of the branch being merged.
Analogy: You have started watching a movie on your television (TV branch). You watched for 30 minutes and then left. While you were away, you watched an additional 15 minutes from your phone (phone branch). When you return, your TV fast forwards to minute 45, as if you had been watching on the TV the whole time. Fast forward merges work the same way: since there was no conflicting activity on main after the solution branch was created, it’s as if the work had always been done on main.
Demonstration: fast forward merge
# Être sur main, créer une branche solution
git checkout -b solution
# Faire du travail et committer
git add .
git commit -m "Implement solution"
# Revenir sur main
git switch main
# Merger la branche solution (fast forward)
git merge solution
# Supprimer la branche après le merge
git branch -d solution
The terms: target and source
To avoid confusion when talking about merge:
- Target: the branch you are merging into (the one you are on)
- Source: the branch you are merging (the one whose changes you are integrating)
Important: You must be on the target branch when you run
git merge. Thegit merge <source>command integrates the changes from<source>into your current branch.
3.2 Understanding Git Diff
See what will happen before merging
You may want to see what will happen before running git merge. git diff allows you to compare branches.
Basic syntax
# Comparer deux branches
git diff <branche1> <branche2>
# Exemple : voir les différences entre main et ticket1
git diff main ticket1
Running git diff this way does not change anything in your filesystem. It simply produces a report of the differences between the peaks of the two branches (i.e. their latest commits).
Decrypt Git Diff output
The output of git diff may seem complex at first. Here’s how to read it:
Line 1 — Files compared:
diff --git a/example.txt b/example.txt
Git labels files a and b. Here, we compare two versions of the same example.txt file.
Lines 2-3 — File hashes:
index abc1234..def5678 100644
These hashes allow each version to be viewed individually with git show <hash>.
Lines 4-5 — File markers:
--- a/example.txt
+++ b/example.txt
- Lines starting with
---belong to filea - Lines starting with
+++belong to fileb dev/nullappears in place of a file name if the file was just created or deleted
The chunk header:
@@ -1,5 +1,6 @@
Double @@ symbols mark a chunk header. It indicates which lines of files a and b are shown in this block. The format -<start>,<number_of_lines> indicates the lines of file a, and +<start>,<number_of_lines> those of file b.
The content of the diff:
- A line without prefix is present in both files (context)
- A line starting with
-has been deleted (present ina, absent inb) - A line starting with
+has been added (absent ina, present inb)
3.3 Using Git Diff in practice
Common scenarios
See unstaged changes:
git diff
Shows only changes that are not yet added with git add.
See staged changes:
git diff --cached
# ou :
git diff --staged
Shows only changes already added with git add.
See all changes (staged and unstaged):
git diff HEAD
Ignore whitespace changes:
git diff -w
# ou :
git diff --ignore-all-space
Useful when team members use different code formatters that modify whitespace.
Compare specific commits:
# Comparer deux commits par leur SHA
git diff <sha1> <sha2>
# Voir ce qu'un fichier ressemblait à un commit spécifique
git show <sha>:<chemin/du/fichier>
Compare branches:
# Voir toutes les différences entre deux branches
git diff main ticket1
# Voir les changements introduits par une branche depuis sa création
git diff main...ticket1
The syntax with three dots (
...) compares the changes that have occurred since the point of divergence of the two branches, not their respective current states.
Full demo with git diff
# Initialiser un dépôt et créer un fichier colors.txt
git init
# Créer colors.txt avec une liste de couleurs (rouge, bleu, vert, etc.)
git add colors.txt
git commit -m "Initial colors file"
# Ajouter quelques lignes à colors.txt
# (ajouter jaune, violet, orange...)
git diff
# Affiche les nouvelles lignes non staged
# Stager les changements
git add colors.txt
# Ajouter encore une ligne (gris)
git diff
# Montre uniquement le changement non staged (gris)
git diff --cached
# Montre uniquement les changements staged
git diff HEAD
# Montre tous les changements (staged + unstaged)
# Committer
git commit -m "Add more colors"
# Créer une branche et faire des changements
git checkout -b colors2.0
# (modifier colors.txt)
git add .
git commit -m "Changes in colors2.0"
# Comparer les deux branches
git diff main colors2.0
# Comparer depuis le point de divergence
git diff main...colors2.0
3.4 Resolve merge conflicts
When do conflicts appear?
If you are working alone, merges are quite simple. However, as a team, it is likely that two people will edit the same file and their changes will need to be merged. Git resolves most differences on its own, but sometimes there is a conflict: Git needs you to tell it how to combine the changes.
How Git reports a conflict
When a conflict arises, Git puts all the contents of both versions into a single file with file markers to indicate the origin of each part:
<<<<<<< HEAD
Contenu de votre branche courante (target)
=======
Contenu de la branche en cours de merge (source)
>>>>>>> nom-de-la-branche-source
- The lines under
<<<<<<<come from the file in your current branch - The signs
=======separate the contents of the two files - Lines above
>>>>>>>come from the file of the branch you are merging
Conflict resolution process
- Open the conflicting file
- Choose which version to keep for each line (or combine both)
- Imperatively delete the markers
<<<<<<<,=======and>>>>>>>— a common mistake is to forget them! - Save the file
- Add and commit changes
# Après avoir résolu manuellement les conflits dans les fichiers
git add <fichier-resolu>
git commit -m "Resolve merge conflict in <fichier>"
The Merge Commit
A merge commit is the commit that combines the contents of what is being merged. It has two parent commits (one on each side of the merge). This is what sets it apart from a regular commit.
Demonstration: Resolving a Conflict
# Préparer : avoir main avec un fichier colors.txt
# Créer une branche colors2.0 avec des modifications conflictuelles
git switch main
# Modifier colors.txt (ajouter/changer/supprimer des lignes)
git add colors.txt
git commit -m "Changes on main"
git switch colors2.0
# Modifier colors.txt aux mêmes endroits avec des valeurs différentes
git add colors.txt
git commit -m "Changes on colors2.0"
# Tenter le merge
git merge main
# Git annonce : CONFLICT (content): Merge conflict in colors.txt
# Automatic merge failed; fix conflicts and then commit the result.
# Ouvrir colors.txt et résoudre manuellement chaque conflit
# Supprimer les marqueurs <<<, ===, >>>
# Finaliser le merge
git add colors.txt
git commit -m "Merge main into colors2.0, resolve conflicts"
Tip: Most IDEs have built-in support for merging with color graphical displays and interactive windows. These tools can make certain merges even easier.
3.5 Aborting a merge in progress
When to abort a merge
It may happen that while a merge is in progress (comparing files and resolving conflicts), you may need to stop. Maybe you found a new bug to fix, forgot to add a requirement, or need a colleague to help resolve some conflicts.
Command aborts
git merge --abort
This command stops the merge and returns the directory to the previous state before the merge started.
The Evil Merge
Beware of what is called the evil merge: a merge which introduces new content as part of the merge. A merge commit should only combine existing content. New content introduced in a merge commit can be confusing because there is no context about its origin or purpose.
How to avoid evil merge:
# Si vous réalisez qu'il manque quelque chose pendant le merge :
# 1. Abandonner le merge
git merge --abort
# 2. Effectuer le changement dans un nouveau commit
git add .
git commit -m "Add missing requirement before merge"
# 3. Relancer le merge
git merge <branche-source>
Demonstration: avoiding an evil merge
# Contexte : avoir main avec colors.txt, numbers.txt, letters.txt
# Créer des changements conflictuels dans la branche colors2.0
# Commencer le merge depuis colors2.0
git switch colors2.0
git merge main
# Conflit détecté
# Pendant la résolution, on réalise qu'il manque quelque chose
# Abandonner le merge
git merge --abort
# Effectuer les corrections nécessaires
git add .
git commit -m "Fix: add missing content before merge"
# Relancer le merge proprement
git merge main
# Résoudre les conflits normalement
git add colors.txt
git commit -m "Merge main into colors2.0"
4. Use Git branches as a team
Module duration: 37m 55s
4.1 Configure remotes
What is a remote?
A remote is simply a location where a version of your project is hosted. In most cases it acts as a central repository, but not always. Many teams use services like GitHub, Bitbucket, and GitLab to host their remote repositories. But a remote could be found just about anywhere.
A remote is generally the version of the project that everyone will use as a reference for the team’s overall progress. It is around remotes that the tracking of milestones, code coverage for tests, and code reviews revolve.
Clone an existing repository
# Cloner un dépôt depuis GitHub
git clone <URL-du-dépôt>
# Exemple avec HTTPS
git clone https://github.com/utilisateur/projet.git
# Vérifier la configuration du remote après le clone
git remote -v
# Résultat : origin <URL> (fetch)
# origin <URL> (push)
Note: After a
git clone, a remote namedoriginis automatically configured to point to the URL of the cloned repository.
Use the Fork on GitHub
On GitHub, the fork functionality creates a copy of a repository in your own account. This allows you to use your own forked copy of the project as a remote to experiment and make changes without affecting the original repository.
Steps to fork and clone:
- Go to the project’s GitHub page
- Click on the Fork button
- Retrieve the URL of your fork (Code button → copy HTTPS URL)
- Run
git clone <URL>on your local machine
4.2 Using remotes with code
Receive changes from remote
The beauty of Git is that each developer has their own copy of the repository. This gives them control over when to integrate others’ changes and when to share their own work.
git fetch:
git fetch
Download information from the remote that is not yet in your local copy. You can then evaluate the changes and decide what to do. Do NOT modify your local branch of work.
git pull:
git pull
Combination of fetch and merge. Downloads the information and automatically launches a merge on your local branch. This is the equivalent of git fetch followed by git merge origin/main.
Send changes to remote
git push
Shares your local changes with the remote to make them available to other repository users.
Warning: Git will not allow you to push if you have unresolved conflicts with the remote. You must resolve conflicts first.
Demonstration: pull and push
# Simuler un changement fait sur le remote (via GitHub par exemple)
# → Ajouter un fichier config.txt directement depuis l'interface GitHub
# Mettre à jour la connaissance de Git sur le remote
git remote update
# Vérifier le statut
git status
# Message : Your branch is behind 'origin/main' by 1 commit, and can be fast-forwarded.
# Récupérer les changements
git pull
# Faire des modifications locales
# (modifier un fichier)
git add .
git commit -m "Local changes"
# Pusher vers le remote
git push
# Scénario de conflit : que faire si le remote a changé pendant qu'on travaillait ?
# Git refusera le push avec un message d'erreur
# Solution :
git pull # Récupérer et merger les changements du remote
# Résoudre les conflits si nécessaire
git push # Puis pusher
See remote branches
# Lister toutes les branches (locales et remote)
git branch -a
# Lister uniquement les branches remote
git branch -r
4.3 Using remotes with branches
Push a local branch to the remote
Let’s say you’ve done a lot of work on a local branch, but it’s not ready to be merged. You want to have a copy on the remote to be able to continue working on another machine. You can push your branch to the remote, which creates a copy of your branch in the remote repository.
# Créer une branche locale et faire des commits
git checkout -b feature4
# (faire du travail, committer...)
git add .
git commit -m "Work on feature4"
# Pusher la branche vers le remote
git push origin feature4
# ou, pour configurer le suivi automatique :
git push -u origin feature4
Note: Be aware that once pushed, anyone with access to the repository can pull it. Some teams have naming conventions to clearly distinguish developer branches (specific prefixes/suffixes).
Retrieve a branch from the remote (simulate another machine)
# Sur une autre machine ou dans un autre répertoire cloné :
# Lister les branches disponibles sur le remote
git branch -r
# Résultat : origin/main, origin/feature4
# Récupérer les informations sur la branche (sans la checker out)
git fetch origin feature4
# Créer une branche locale qui suit la branche remote
git checkout -b feature4 origin/feature4
# ou avec la syntaxe moderne :
git switch -c feature4 origin/feature4
Complete demonstration: sharing a branch between two machines
# === MACHINE 1 (machine principale) ===
# Cloner le dépôt distant (widget project)
git clone https://github.com/mon-compte/widget.git
# Créer une branche feature4
git checkout -b feature4
# Faire du travail
git add .
git commit -m "Feature 4 implementation"
# Pusher la branche vers le remote
git push origin feature4
# === MACHINE 2 (autre machine - simulée par un autre répertoire) ===
# Cloner le même dépôt dans un répertoire différent
mkdir otherMachine
cd otherMachine
git clone https://github.com/mon-compte/widget.git
# Récupérer les informations sur les branches remote
git remote update
# Voir les branches disponibles
git branch -a
# → origin/feature4 est visible
# Checkout de la branche feature4
git checkout -b feature4 origin/feature4
# Continuer le travail et pusher
git add .
git commit -m "Continue work on feature4 from other machine"
git push origin feature4
4.4 Using Pull Requests
Pull Request: what is it?
A pull request (often abbreviated PR) is a feature of GitHub (the company) and other Git hosts like GitLab and Bitbucket. This is a feature of these platforms, not a feature of Git itself. This confusion is common when you start.
A pull request provides a place to review and test the changes that you propose to push into the main branch. On some projects you don’t even have access to merge directly in main, making pull requests mandatory so that the project maintainers can review and then merge your code.
The workflow of a Pull Request
1. Créer une branche locale pour une fonctionnalité
↓
2. Travailler et committer sur la branche
↓
3. Pusher la branche vers le remote
↓
4. Ouvrir un Pull Request via l'interface web (GitHub/GitLab/Bitbucket)
↓
5. Revue de code par l'équipe (commentaires, discussions)
↓
6. Effectuer les corrections nécessaires (commits supplémentaires)
→ Ces nouveaux commits s'ajoutent automatiquement au PR
↓
7. Approbation du PR
↓
8. Merge dans main
↓
9. Supprimer la branche
Important: Do do not rebase after pushing your branch to the remote. If you don’t know what rebase is yet, we’ll see it in section 5.
What can be done in a Pull Request
- See the list of commits included in the PR
- Add reviewers
- Discuss changes with your team via comments
- Make fixes (new commits) that are automatically added to the PR
- Perform a code review with line-by-line comments
Demonstration: create and manage a Pull Request
# 1. Créer une branche feature
git checkout -b feature5
# 2. Implémenter le code
git add .
git commit -m "Implement feature 5"
# 3. Pusher vers le remote
git push -u origin feature5
# 4. Aller sur GitHub et créer un Pull Request
# → Comparer feature5 avec main
# → Ajouter un titre et une description
# → Assigner des reviewers
# 5. Suite aux retours de la revue, faire des corrections :
# (modifier des fichiers selon les commentaires)
git add .
git commit -m "Fix: address review feedback"
git push origin feature5
# Le nouveau commit apparaît automatiquement dans la PR
# 6. Après approbation, merger via l'interface GitHub
# 7. Supprimer la branche remote depuis GitHub
# (bouton "Delete branch" après le merge)
# 8. Supprimer la branche locale
git switch main
git pull # Récupérer les changements merged
git branch -d feature5
4.5 Ignoring files with .gitignore
The three file states in Git
All files in Git are in one of three states:
- Tracked: you added or committed them
- Untracked: Git tells you that they are not tracked every time you run
git status - Ignored: Git doesn’t say anything about them or do anything with them
Why ignore files?
You will generally want to ignore files generated by your codebase:
- Compiled code (files
.class,.o,.exe,.dll) - Log files
- Build directories (like
bin/,dist/,build/) - Caches (
.cache/,__pycache__/) - Hidden system files (
.DS_Storeon macOS,Thumbs.dbon Windows) - Individual configuration files (IDE preferences like
.vscode/settings.json)
These items are most likely to create conflicts with other team members during merges and do not need to be included in the repository. Ignoring a file also ensures that it will not be added to a branch or pushed to a remote, and that it will not appear or disappear when you change branches.
.gitignore file syntax
The .gitignore file uses syntax similar to regular expressions to specify patterns of files and directories to ignore:
| Character | Meaning |
|---|---|
* | Matches everything except a slash |
? | Matches a single character (except slash) |
! | Negator (do not ignore this pattern) |
/ | Always used as directory separator |
[] | Allows you to specify character ranges |
# | Comment |
** | Matches a directory anywhere in the repository |
Examples of .gitignore rules
# Ignorer tous les fichiers .log
*.log
# Ignorer le répertoire build/ à la racine
/build/
# Ignorer tout répertoire bin/ n'importe où dans le dépôt
**/bin/
# Ignorer tous les fichiers .zip n'importe où dans le dépôt
*.zip
# Ignorer le répertoire node_modules/ et tout son contenu
node_modules/
# Ignorer les fichiers de configuration d'IDE
.vscode/settings.json
.idea/
# Ignorer les fichiers système
.DS_Store
Thumbs.db
# Ne PAS ignorer les fichiers .gitkeep (exception)
!.gitkeep
# Ignorer les fichiers .env (variables d'environnement sensibles)
.env
*.env.local
Tip: It is recommended to have a
.gitignorefile at the root of your project to ensure that everyone ignores the same things. It’s really annoying when someone accidentally checks build artifacts or IDE configuration files in the repository.
Useful commands with .gitignore
# Vérifier si un fichier spécifique est ignoré
git check-ignore -v <chemin/du/fichier>
# Voir les fichiers ignorés
git status --ignored
# Ajouter un fichier déjà commité à .gitignore
# (nécessite de le supprimer du suivi sans supprimer le fichier)
git rm --cached <fichier>
echo "<fichier>" >> .gitignore
git add .gitignore
git commit -m "Remove <fichier> from tracking"
4.6 Establish team conventions
The cliff metaphor
There is a story about a village near a cliff. People visiting got too close to the edge and fell. The village was divided: half wanted to buy an ambulance to help those who fell (reactive solutions), the other half wanted to build a fence on top of the cliff to prevent falls (preventive solutions).
In Git:
- The cliff represents potential conflicts when working in a team
- The ambulance represents the merge tools available to resolve difficult conflicts
- Fence represents team conventions and best practices that help avoid falling
The objective is to reduce the number of “ambulance in the valley” situations through good practices.
Recommended conventions
1. A common .gitignore file for each project
This reduces confusion and prevents unwanted files from being added to the repository. Make sure this file is created at the start of the project and that the whole team uses it.
2. A README file with important information
Use Markdown format for your README — it’s much more readable than a plain text file. The README should include:
- How to install and configure the project
- How to run tests
- Commit conventions used
- Important branches and their role
3. Stay connected to codebase activity
Don’t disappear into a cellar for a month. Frequent, small commits are easier to manage than one giant commit once a month.
4. Coordination for large refactors
If a commit will affect a large percentage of the project’s files (large refactor), coordinate with team members before making the change. Merging a large change when dozens of people have branches flying on the same project can create a lot of confusion.
5. Automated tests
Many teams have automated tests that run before or after new code is included in the repository. Typically, these are unit tests to ensure that no existing functionality is broken by a new commit.
6. The concept of “breaking the build”
If everyone is working on main, a commit that breaks the build (fails tests or prevents compilation) immediately affects everyone. Branches allow everyone to work on their own, and tests can ensure that the code works correctly before being merged into main.
4.7 Write code that can easily merge
Commit related changes
Evaluate each commit by asking yourself: Would this commit make sense on its own?
Grouping related changes makes merging easier, because commits will only conflict if they touch the same area of functionality.
Write good commit messages
The subject line:
- Treat it as the subject of an email
- Can it tell the reader what the commit does?
- Try to keep it to 50 characters or less so team members can get quick and concise overviews when looking through a list of commits
The message body:
- Add a body to your commit when appropriate
- Body stores additional information with subject line
- Some commits will not be simple one-line topics — sometimes you need to give more context on why the change was made
- This really helps someone reading the project history a year or later
Rule of thumb: People can use the diff to see what has changed. Use the commit body to tell them why it changed.
# Bon message de commit avec sujet et corps
git commit -m "Fix login timeout bug
Users were being logged out after 5 minutes instead of 30.
The session timeout value was incorrectly set to 300 seconds
instead of 1800 seconds. This affected all users since v2.3.1."
Eliminate commented code
Commented code is a mystery in a codebase. You may come across a block like this where it’s not obvious why it hasn’t just been deleted. As long as the code has been committed at least once, Git will remember it. Don’t let your codebase become a graveyard of items you might need later.
Commented code costs time for everyone who encounters it and wonders why it is there. Let Git do its job of remembering your history and clean up your codebase.
Whitespace issues
Git sees everything, including whitespace. If different team members use different code formatters that modify the whitespace, this can create false merge conflicts. Team conventions on code formatting (indentation, spaces vs. tabs, line endings) help avoid these problems.
# Si nécessaire, dire à Git d'ignorer les changements de whitespace lors du merge
git merge -Xignore-all-space <branche>
git merge -Xignore-space-change <branche>
5. Advanced merge methods
Module duration: 31m 22s
5.1 Reorder history with Rebase
The culinary metaphor
Sometimes you have to make a mess to make something good. If you look at your kitchen after cooking an amazing meal, there are probably several pots, pans, plates and other things to clean. The same thing can happen when writing code. You can forget things along the way, create checkpoint commits, and have multiple attempts that don’t work.
Git rebase can be used to clean up your local history and focus on the end result. The goal is to increase the precision and clarity of what the code is doing and why changes were made, so that someone else reading the history or performing a merge has the information they need. Think of it as the difference between reading a draft and the final version of an article.
When NOT to use rebase
Absolute rule: Never rebase on a public branch.
If you do a rebase on a public branch (a branch shared on the remote), you will almost always create confusion and even risk other people losing their work.
There is a lot of debate around the use of rebase. Some teams choose not to use it at all. Always check your team’s guidelines.
In summary:
- ✅ Rebase is acceptable on unpublished local branches
- ❌ Never rebase a branch already pushed to the remote
- ❌ Never rebase
mainor any other shared branch
5.2 Squash multiple commits
The concept of squash
Let’s say you are working on a branch and you make 3 related commits. The end result you wanted to build is completed in the last commit, and you want all 3 commits to appear as one. You can use interactive rebase to squash (overwrite/combine) the second and third commits into the first, so that they appear as a single commit.
What Git actually does: it creates a new commit, then copies the changes from each commit into this new commit. You can then choose the commit message for this new commit, and it will appear as a single commit in your history.
Find the starting point of the rebase
# Trouver le commit de base (là où la branche a divergé de main)
git merge-base ticket1 main
# Retourne le SHA complet du commit de base
Note how the first characters of the returned SHA correspond to the initial commit in
main.
Start interactive rebase
# Lancer le rebase interactif depuis le commit de base
git rebase -i <sha-du-commit-de-base>
# Exemple :
git rebase -i abc1234
Git presents a file with each commit that is part of the rebase:
pick abc1234 First checkpoint
pick def5678 Second checkpoint
pick ghi9012 Final solution
Commands available in interactive rebase
| Order | Abbreviation | Description |
|---|---|---|
pick | p | Keep commit as is |
reword | r | Keep the commit but modify its message |
edit | e | Mark commit for amendment |
squash | s | Combine with previous commit (keeps both messages) |
fixup | f | Combine with previous commit (discard this message) |
drop | d | Delete commit |
Demonstration: squashing 3 commits in 1
# Initialiser et préparer
git init
git add initial.txt
git commit -m "First file in main"
# Créer une branche ticket1
git checkout -b ticket1
# Faire plusieurs commits
echo "Checkpoint 1" > ticket1.txt
git add ticket1.txt
git commit -m "Checkpoint 1"
echo "More work" >> ticket1.txt
git add ticket1.txt
git commit -m "Checkpoint 2"
echo "Final work" >> ticket1.txt
git add ticket1.txt
git commit -m "Finished ticket1"
# Vérifier l'historique
git log --oneline
# abc1234 Finished ticket1
# def5678 Checkpoint 2
# ghi9012 Checkpoint 1
# jkl3456 First file in main
# Trouver le point de base
git merge-base ticket1 main
# → jkl3456...
# Lancer le rebase interactif
git rebase -i jkl3456
# Dans l'éditeur, modifier pour squasher :
# pick ghi9012 Checkpoint 1
# squash def5678 Checkpoint 2
# squash abc1234 Finished ticket1
# Sauvegarder et fermer l'éditeur
# Git vous demande de choisir/écrire le message du commit final
# Vérifier le résultat
git log --oneline
# → Un seul commit représentant tout le travail de ticket1
5.3 Rebase from main
The concept
Let’s say you’re on a branch derived from main and you’ve made a few commits. If you run git rebase main, then all changes that happened in main will be pulled to your branch. Then your commits will be replayed after the last commit of main. This can help sequence your changes to main and create a more linear history when you merge back into main.
Rebase vs Merge — comparison
| Appearance | git merge | git rebase |
|---|---|---|
| History | Non-linear (merge commits) | Linear |
| Merge commit | Yes, create a merge commit | No |
| Trace changes | Preserves Accurate History | Rewrites history |
| Security on shared branches | Yes | No |
| Clarity of history | Can be complex | Own history |
Demonstration: rebasing from main vs merging
# Préparer le dépôt
git init
echo "Content" > file1.txt
git add file1.txt
git commit -m "Create file1"
echo "More content" > file2.txt
git add file2.txt
git commit -m "Create file2"
# Créer une branche ticket2
git checkout -b ticket2
# Ajouter des fichiers dans ticket2
echo "Ticket2 content" > ticket2.txt
git add ticket2.txt
git commit -m "Add ticket2.txt"
echo "Helper content" > ticket2helper.txt
git add ticket2helper.txt
git commit -m "Add ticket2helper.txt"
# Revenir sur main et ajouter un commit
git switch main
echo "New content" > main3.txt
git add main3.txt
git commit -m "Add main3"
# === VERSION REBASE ===
git switch ticket2
git rebase main
# Git séquence les commits : main1, main2, main3, ticket2, ticket2helper
# L'historique est maintenant linéaire
# === VERSION MERGE (pour comparaison) ===
# (dans une copie du répertoire)
git switch ticket2
git merge main
# Git crée un merge commit pour combiner les deux histoires
# L'historique montre deux lignes qui se rejoignent
After a rebase, when you merge the branch into
main, it’s a fast forward merge — clean and without additional merge commits.
5.4 Use Cherry Pick
The concept
Imagine that the cherries on a branch represent multiple commits in a Git branch. What do you do if you only want to bring one of these commits to another branch? You cherry-pick it.
git cherry-pick adds any commit to the HEAD of the branch you are in, effectively creating a copy of the commit in a different branch.
Important: Cherry-pick does not move the commit. It creates a copy of the commit with a new parent in the target branch. The original commit remains intact and is not removed from the original branch.
Use cases
1. A bug fix applicable to several versions If each version of a product is on its own branch, you can make the commit to fix the bug in one branch, then cherry-pick that commit in the other branches to apply the fix.
2. Retrieve useful commits from a branch that will not be merged You can cherry-pick a few commits from a branch that will never be merged, but some commits of which are still useful.
3. Parallel development There may be times when parallel development is happening in different branches and you need something from another branch for your task, but the branches are not yet ready to be merged.
Warnings
- Cherry-pick creates a duplicate commit. If the original branch is merged into the target branch, this could cause confusion if the merger is not aware of the cherry-pick.
- Like rebase, cherry-pick is an advanced feature. It should be used only when necessary and should not replace regular merge.
Syntax
# Cherry-picker un commit spécifique dans la branche courante
git cherry-pick <sha-du-commit>
# Cherry-picker plusieurs commits
git cherry-pick <sha1> <sha2> <sha3>
# Cherry-picker une plage de commits
git cherry-pick <sha-debut>..<sha-fin>
5.5 Cherry Pick Demos
Scenario: find the values of x and y
You simulate solving a difficult problem where you must discover two different things (x and y). You create a branch to work on x and another to work on y. Once you find the solution for x and y, you cherry-pick the solution commits into main.
Full demo
# Initialiser le dépôt
git init
echo "Initial content" > initial.txt
git add initial.txt
git commit -m "Initial commit"
# === BRANCHE POUR X ===
git checkout -b solve-x
# Simuler plusieurs tentatives pour trouver x
echo "Attempt 1 for x" > x_attempt1.txt
git add x_attempt1.txt
git commit -m "Try x = 10"
echo "Attempt 2 for x" > x_attempt2.txt
git add x_attempt2.txt
git commit -m "Try x = 20"
echo "Solution for x = 42" > x_solution.txt
git add x_solution.txt
git commit -m "Found x = 42"
# Noter le SHA du commit de solution pour x
git log --oneline
# abc1234 Found x = 42 ← Ce SHA nous intéresse
# === BRANCHE POUR Y ===
git switch main
git checkout -b solve-y
# Simuler plusieurs tentatives pour trouver y
echo "Attempt 1 for y" > y_attempt1.txt
git add y_attempt1.txt
git commit -m "Try y = 5"
echo "Attempt 2 for y" > y_attempt2.txt
git add y_attempt2.txt
git commit -m "Try y = 10"
echo "Solution for y = 7" > y_solution.txt
git add y_solution.txt
git commit -m "Found y = 7"
# Voir l'historique et noter le SHA de la solution pour y
git log --oneline
# def5678 Found y = 7 ← Ce SHA nous intéresse
# === CHERRY-PICK VERS MAIN ===
git switch main
# Vérifier que main n'a que le commit initial
git log --oneline
# Initial commit
# Cherry-picker la solution pour x
git cherry-pick abc1234
# Le commit "Found x = 42" est maintenant copié dans main
# Cherry-picker la solution pour y
git cherry-pick def5678
# Le commit "Found y = 7" est maintenant copié dans main
# Vérifier l'historique de main
git log --oneline
# def5678-copy Found y = 7
# abc1234-copy Found x = 42
# initial Initial commit
# Vérifier l'historique de solve-x (la branche d'origine)
git switch solve-x
git log --oneline
# abc1234 Found x = 42 ← Le commit original est toujours là, non modifié
Note: Your SHA will be different than shown in the example. Be sure to copy the SHAs from your own history.
5.6 Conclusion
Summary of acquired skills
You have covered the entire Git Branching and Merging course. Here is a summary of everything you have mastered now:
Branch basics:
- Create, rename and delete branches
- Use branches to keep track of multiple things at once
- Switch between branches
The merge:
- Combine work by merging branches
- Understanding fast forward merge
- Use
git diffto compare branches and commits - Resolve merge conflicts
- Avoid evil merges with
git merge --abort
Teamwork:
- Share code using remotes (
fetch,pull,push) - Contribute to projects using pull requests (PR)
- Use
.gitignoreto prevent unwanted files from being committed - Apply team conventions to avoid confusion
Advanced methods:
- Use rebase to avoid additional merges and clean up commit history
- Use cherry-pick to move specific changes between branches without merging the entire branch
The command line and IDEs:
- Perform command line actions (available in any environment)
- Use IDE tools to work with branches (additional graphical tools)
Next steps
To review the concepts in this course, download the exercise files to follow the examples in your own environment. Many people find it helpful to review some demonstrations by doing them themselves to try out different commands and options.
6. Appendix: Git Command Summary
Basic branch commands
# Créer une branche
git branch <nom-de-branche>
# Créer une branche et basculer dessus
git checkout -b <nom-de-branche>
# ou (syntaxe moderne)
git switch -c <nom-de-branche>
# Lister les branches locales
git branch
# Lister toutes les branches (locales + remote)
git branch -a
# Basculer sur une branche
git checkout <nom-de-branche>
# ou
git switch <nom-de-branche>
# Renommer une branche (depuis une autre branche)
git branch -m <ancien-nom> <nouveau-nom>
# Renommer la branche courante
git branch -m <nouveau-nom>
# Supprimer une branche (mergée)
git branch -d <nom-de-branche>
# Forcer la suppression d'une branche (non mergée)
git branch -D <nom-de-branche>
Merge commands
# Merger une branche dans la branche courante
git merge <nom-de-branche>
# Abandonner un merge en cours
git merge --abort
# Merger en ignorant les différences de whitespace
git merge -Xignore-all-space <nom-de-branche>
Diff commands
# Voir les changements non staged
git diff
# Voir les changements staged
git diff --cached
git diff --staged
# Voir tous les changements (staged + unstaged)
git diff HEAD
# Comparer deux branches
git diff <branche1> <branche2>
# Voir les changements depuis la divergence
git diff <branche1>...<branche2>
# Ignorer les changements de whitespace
git diff -w
# Voir un fichier à un commit spécifique
git show <sha>:<chemin/du/fichier>
Remote commands
# Voir les remotes configurés
git remote -v
# Mettre à jour les informations sur les remotes
git remote update
# Télécharger les changements sans merger
git fetch
# Télécharger et merger les changements
git pull
# Envoyer les changements vers le remote
git push
# Pusher une branche vers le remote et configurer le suivi
git push -u origin <nom-de-branche>
# Supprimer une branche remote
git push origin --delete <nom-de-branche>
Rebase commands
# Trouver le point de base entre deux branches
git merge-base <branche1> <branche2>
# Lancer un rebase interactif
git rebase -i <sha-commit-de-base>
# Rebaser la branche courante sur main
git rebase main
# Annuler un rebase en cours
git rebase --abort
# Continuer un rebase après résolution de conflits
git rebase --continue
Cherry-pick commands
# Cherry-picker un commit
git cherry-pick <sha>
# Cherry-picker plusieurs commits
git cherry-pick <sha1> <sha2>
# Cherry-picker une plage de commits
git cherry-pick <sha-debut>..<sha-fin>
# Annuler un cherry-pick en cours
git cherry-pick --abort
Search Terms
git · branching · merging · ci/cd · devops · merge · demonstration · branches · commands · rebase · branch · remote · pull · diff · gitignore · request · changes · commit · concept · conflict · remotes · syntax · another · between