Lorsqu’on est à l’aise avec Git, il est courant d’user et d’abuser du rebase interactif.
Pour rappel, git rebase -i ACOMMIT
vas nous permettre d’éditer, renommer, fusionner vos commits. Il devient rapidement l’outil principal des développeurs une fois pris en main. Cependant il est également la bête noire qui risque de réduire à néant notre travail.
Voici quelques astuces pour nous rendre plus confiant lors de nos rebases.
Avant toute chose, nous devons absolument changer une configuration Git. Ajoutons ceci dans notre ~/.gitconfig
si ce n’est pas déjà fait.
[merge]
conflictstyle = diff3
Ce style de conflit va beaucoup nous aider et devrait selon moi être ainsi par défaut. En cas de conflit, voici ce que nous verrons :
<<<<<<< HEAD
THIS IS USEFUL
||||||| merged common ancestors
This is useful
=======
This is really useful
>>>>>>> c2392943.....
Lorsque nous rencontrons ceci :
HEAD
ou du c2392943
est notre changement, celui qui est de notre côté.Concrètement :
THIS IS REALLY USEFUL
Il est commun de devoir gérer des conflits lorsque on fait des rebases (ou tout autre manipulation Git d’ailleurs !). Je nous propose ici d’installer une ceinture de sécurité qui va nous empêcher de passer à travers le pare-brise au premier dérapage.
L’idée est d’être informé si nous avons modifié quelque chose lors de notre gestion du conflit.
Une petite vidéo vaut mieux que toutes les explications ?
Ajoutons ces deux scripts dans notre dossier .git/hooks
avec les droits d’exécution. On peut également les ajouter globalement grâce à ~/.gitconfig
ainsi :
[init]
templatedir = ~/.gitconfig.d/template
Notons que cela n’impactera pas les repos Git existant !
Ajoutons-les ensuite dans ce dossier ~/.gitconfig.d/template/hooks/
Fichier pre-rebase
:
#!/usr/bin/env sh
export GIT_DIR="$(git rev-parse --absolute-git-dir)"
rebase_in_progress_file="$GIT_DIR/rebase_in_progress"
git rev-parse HEAD > "$rebase_in_progress_file"
Celui-ci vraiment simple. On stocke la référence Git au départ d’une réécriture.
Fichier post-rewrite
:
#!/usr/bin/env sh
export GIT_DIR="$(git rev-parse --absolute-git-dir)"
rebase_in_progress_file="$GIT_DIR/rebase_in_progress"
confirm_rebase_diff() {
old_ref="$1"
new_ref="$2"
if [ -z "$(git diff "$old_ref..$new_ref")" ]; then
printf "%s\n" "Your rebase caused no code changes, good for you" >&2
return
fi
printf "%s\n" "Your rebase resulted in code changes" >&2
git diff "$old_ref..$new_ref" >&2
printf "%s" "Do you want to keep the changes? [Y/n] " >&2
read -r reply < /dev/tty
if [ -z "$reply" ] || [ "$reply" = "y" ]; then
printf "%s\n" "Cool! All changes kept" >&2
else
printf "%s\n\n" "Discarding changes..." >&2
git reset --hard "$old_ref"
fi
}
if [ -f "$rebase_in_progress_file" ] && [ "$1" = "rebase" ]; then
old_sha="$(cat "$rebase_in_progress_file")"
confirm_rebase_diff "$old_sha" "HEAD"
rm "$rebase_in_progress_file"
fi
On vient comparer la différence entre la référence Git que nous avons précédemment stocké et celle à la fin.
Plusieurs choix s’offrent à nous si c’est le cas :
Si la réécriture n’a engendré aucune différence dans le code, tant mieux ! Rien ne s’affiche et la vie continue
Vous avez fait une bêtise. Ne mentez pas, je le vois à vos yeux horrifiés qui scrutent l’open-space. Vous cherchez de l’aide.
Pas de panique. Git intègre une mécanique d’historique (cela ne devrait vraiment pas nous étonner).
$ git reflog
Git reflog vas nous donner la liste des dernières actions qu’il a réalisées. Ce ne sont que d’autres références Git vers des états antérieurs. Elles sont sous la forme HEAD@{0}
.
Si nous avons ravagé notre travail à cause d’un rebase, cherchons le reflog qui commence par rebase (start)
. Nous pouvons aussi cibler le reflog de notre précédent commit. Gardons juste en tête qu’un reflog représente l’état après l’action.
Si nous voulons revenir avant le début du rebase dans l’exemple suivant, faisons par exemple :
$ git reflog
2d0272ac0 (HEAD) HEAD@{0}: commit: fix: Add a missing shipping type
8fef1ba6d HEAD@{1}: commit: fix: add a missing technical link between garment listing and its ordered products
6b8df6666 HEAD@{2}: commit: fix: Add a required index to make Odoo sync faster
54bfc6977 HEAD@{3}: checkout: moving from feature/ops to 54bfc6977
fa529fdea HEAD@{4}: rebase (continue) (finish): returning to refs/heads/feature/ops
fa529fdea HEAD@{5}: rebase (continue) (pick): WIP: feat: odoo sync customer bills
ca827f90c HEAD@{6}: rebase (continue): WIP: feat: odoo sync bank account and entities
54bfc6977 HEAD@{7}: rebase (continue) (pick): feat(ops): add ops config
eea26d0ed HEAD@{8}: rebase (continue): feat: Odoo add a reset command
fe252ae98 HEAD@{9}: rebase (start): checkout perso/production
...
$ git reset --hard HEAD@{9}^
Au final, nous pourrions tout aussi bien écrire sur un bout de papier la référence de notre dernier commit avant la catastrophe
car cela revient au même.
Une fois que nous avons travaillé vos commits, nous pouvons enfin montrer au monde nos talents d’arboriste.
Éditer l’historique Git nous oblige maintenant à pousser nos commits avec force et conviction. Difficile de savoir quelles seront les conséquences de l’autre côté du réseau.
Pourtant Git nous offre un moyen simple d’en déterminer les issues.
$ git push origin HEAD:oopsi/fixing-typo -fn
Notons le paramètre -n
(“n” pour dry-run ¯\_(ツ)_/¯
) adossé au -f
. En réponse à cette commande, le repo distant va nous donner ceci :
To git.synbioz.com:~ebarraco/hello-world.git
+ cfc907c4...d575e61a d575e61a -> oopsi/fixing-typo (forced update)
Ce qui nous intéresse ici est cfc907c4…d575e61a
. Cela signifie que le push fais passer la référence oopsi/fixing-typo
du commit cfc907c4
au commit d575e61a
. À l’aide de notre souris et de nos petits doigts, utilisons cet intervalle et retirons un point
$ git diff cfc907c4..d575e61a
Nous avons là la différence du code après notre futur push. Si nous n’avons fait que regrouper des commits, la commande ne doit rien renvoyer. Ou alors peut être que quelqu’un d’autre a ajouté des commits ?
Certains utilisent git rerere
comme un outil pour simplifier la résolution de conflits. Je nous le déconseille vivement ! Bien au contraire, je souhaite nous pousser à garder un œil très attentif sur les impacts qu’a Git sur notre code. Vérifions les différences, nos commits, nos rebases et les impacts de nos push. Usons et abusons du git log -p
et du git show <git-ref>
pour lister et afficher dans le détail chacun de nos commits. Et ne soyons pas radins sur les descriptions de ceux-ci !
L’équipe Synbioz.
Libres d’être ensemble.
Nos conseils et ressources pour vos développements produit.