Fork me on GitHub

Gestion de conflits Branches, merges, récrire l'histoire

Désynchronisation

Les branches, locales ou distantes, se désynchronisent nécessairement :

  • modifications locales,
  • modifications distantes par un collaborateur,
  • branches expérimentales,
  • retour dans le passé…

Commandes fondamentales :

  • fetch : télécharge toutes les modifications distantes, sans toucher à l’historique local.

  • status : montre l’état de synchronisation.

  • merge : importe les commits d’une autre branche.

  • pull : équivalent de fetch, puis merge (avec branche tracée).

Modifications locales

Le répertoire a changé localement, l’origine n’a pas bougé.

git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
(use "git push" to publish your local commits)

L’origine peut être mise à jour par un fast-forward.

git push

Modifications distantes

L’origine a changé, le répertoire local n’a pas bougé

git status
On branch master
Your branch is behind 'origin/master' by 2 commits,
and can be fast-forwarded.
  (use "git pull" to update your local branch)

Le répertoire locale peut être mis à jour par un fast forward.

git pull

Historique divergent

Les deux branches ont subi des modifications concurrentes.

git status
On branch master
Your branch and 'origin/master' have diverged,
and have 1 and 2 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

Le fast-forward est impossible. Il est nécessaire de faire un merge.

Télécharger et faire un merge

git pull

Annuler si le merge n’est pas un fast-forward

git pull --ff-only

Les conflits, ça arrive

git pull
Auto-merging tutorials/tutorial4.md
CONFLICT (content): Merge conflict in tutorials/tutorial4.md
Automatic merge failed; fix conflicts and then commit the result.
git status
On branch master
Your branch and 'origin/master' have diverged,
and have 1 and 2 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)

	both modified:   tutorials/tutorial4.md

no changes added to commit (use "git add" and/or "git commit -a")

Anatomie d’un conflit

Lorem ispsum
<<<<<<< HEAD
dolor sit amet
=======
dolor sit amemus
>>>>>>> a10ce40a3d3aedd7b4517249df5615b08087a044
quousque tandem
  • Modifications locales (HEAD),
  • Modifications distantes,
  • Contexte.
  • L’utilisateur qui réalise le merge doit choisir une résolution pour le conflit.
  • Une fois le conflit résolu : add, puis commit.
  • Outils pour l’aide à la résolution de conflits : KDiff3, DiffMerge, Meld, …

Récrire l’histoire

Important : On ne récrit jamais l’historique d’un répertoire partagé : origin n’oublie pas !

Cependant, on est libres de faire ce que l’on veut avec ses branches locales :

  • commit --amend : récrit les métadonnées d’un commit.
  • revert : crée un commit qui défait un commit précédent.
  • reset : revient à un ancien commit.
  • reset --hard : revient à un ancien commit, et modifie les fichiers.
  • rebase : rejoue une suite de commits à partir d’un autre point de l’histoire.
  • cherry-pick : rejoue un commit à partir de HEAD.

show et checkout permettent aussi d’examiner les fichiers tels qu’ils étaient à un moment dans l’historique.

Oublier des commits : reset

  • Revenir à un moment de l’histoire, les fichiers ne sont pas touchés

    git reset SHA
    

    checkout permet de reporter des fichiers individuels à l’état du commit.

  • Revenir à un moment de l’histoire, les fichiers sont reportés à l’état du commit

    git reset --hard SHA
    

    Attention : ceci détruit tous les changements non commités.

Rejouer des commits : rebase

  • Rejouer les commits à partir du dernier commit commun

    git rebase <branch>
    
  • Faire un fetch et rejouer les commits d’un seul coup

    git pull --rebase
    

    Alternative au merge classique. Adapté pour des petites modifications à pousser immédiatement.

Récrire l’histoire distante

Récrire l’histoire de origin, c’est mal. Mais…

  • Remplacer l’historique distant avec l’historique local

    git push -f
    
  • Effacer une branche distante

    git push origin :<branche>
    

À n’utiliser que si vous savez vraiment ce que vous faites !

Lectures

Le Git Book :

Et, lorsque vous êtes perdus,

git help <command>

très verbeux, mais très complet.