Mon aide-mémoire git
Git est vraiment un logiciel extraordinaire mais certaines commandes sont difficiles à mémoriser. Or il parait qu'il ne faut pas chercher à mémoriser ce qu'on peut retrouver facilement. C'est pourquoi je me suis fait ce petit aide-mémoire, en espérant qu'il peut aussi vous être utile à l'occasion.
-
Créer un dépôt git sur un serveur :
git --bare init
(crée simplement les fichiers directement dans le répertoire courant au lieu d'un répertoire caché .git) -
Cloner ce dépôt (vide au départ) sur une machine de développement :
git clone utilisateur@serveur:/chemin
-
Voir le diff avec vimdiff :
git difftool
Par exemple entre deux commits abcdef et ghijkl, pour le fichier fichier :git difftool abcdef ghijkl fichier
-
Voir le diff mot à mot et non ligne à ligne :
git diff --color-words
(marche aussi avec git show) -
Voir le diff au caractère près :
git diff --color-words=.
-
Voir le diff des modifs déjà validées :
git diff --staged
ougit diff --cached
-
Voir le diff entre un vieux commit abcdef et HEAD :
git diff abcdef..
OK, mais voir le diff entre un vieux commit et l'espace de travail (modifs par encore commitées) :git diff abcdef
(subtile nuance !) -
Modifier le commentaire du dernier commit :
git commit --amend
-
Mais modifier le commentaire d'un vieux commit abcdef :
git rebase --interactive abcdef^
et changer pick en reword (à ne pas faire si on a déjà pushé !) -
Cas particulier du commentaire du premier commit :
git rebase -i --root
-
Commiter un fichier qui a subi plusieurs modifs :
git add -p fichier
(-p pour patch) (joli article sur css-tricks.com/git-add-patch-mode/) -
git log -p fichier
= historique des modifs du fichier avec le diff à chaque commit -
git log -L 42,42:fichier
= historique des modifs du fichier à la ligne 42 -
git log -G foo fichier
= les commits où on a ajouté ou supprimé foo dans le fichier fichier -
Voir l'historique d'un fichier même avant qu'il ait changé de nom ou de répertoire :
git log --follow -- fichier
(les deux tirets devant le nom de fichier sont importants) -
Trouver quand le fichier foobar a été supprimé :
git log -- foobar
-
Trouver un commit par son commentaire :
git log -i --grep="bla bla"
(-i pour ignorer la casse) -
Trouver tous les commits qui contiennent "bla bla" (toutes les occurences dans tous les fichiers dans tous les commits) :
git grep "bla bla" $( git rev-list --all )
(ça peut prendre un certain temps…) -
Le meilleur schéma :
git log --oneline --graph --decorate --all
(plus pratique : mettre dans.gitconfig
une section[alias] l = log --oneline --graph --decorate --all
) -
git show abcdef
= diff du commit abcdef -
git show abcdef fichier
= diff de fichier au commit abcdef -
git show abcdef:fichier
= fichier tel qu'il était au commit abcdef (deux points entre abcdef et fichier !!!) -
git diff --name-only abcdef
= voir quels fichiers ont changé depuis le commit abcdef -
git tag -n1
liste les tags avec 1 ligne de commentaire (ou d'annotation si le tag a été créé avec-a
) -
git checkout abcdef
= faire revenir le dépôt à l'état où il était au commit abcdef, puisgit checkout master
pour revenir à l'état courant -
git checkout abcdef~1 fichier
= faire revenir le fichier tel qu'il était avant le commit abcdef (pas dereset
si on a déjà pushé !) -
Pour trouver à quel commit on a introduit un bug qui a échappé aux tests :
git bisect start ; git bisect good abcdef ; git bisect bad ghijkl ; ...
(excellent article sur le sujet) -
git revert abcdef
= défait explicitement le commit abcdef (pas dereset
si on a déjà pushé !) -
git reset --hard HEAD
= revient à l'état du dernier commit quand, comme dit la doc, "on s'est embrouillé dans son répertoire de travail" mais on n'a pas encore commité.
Note : depuis git v2.23 (2019) on peut fairegit restore
qui évite le risque de se mélanger les pinceaux dans les arguments et d'effacer par erreur le dernier commit (cf ci-dessous) -
git reset HEAD~1
permet de refaire le dernier commit, par exemple pour le séparer en plusieurs commit avecgit add -p
(à ne pas faire si on a déjà pushé !) -
git reset --hard HEAD~1
= efface le dernier commit (à ne pas faire si on a déjà pushé !) -
git reset fichier
= contraire degit add fichier
(quand on a faitadd
par erreur) -
Mettre de côté ses modifs en cours, par exemple parce qu'on a commencé à les faire sur la mauvaise branche :
git stash save
(ici on peut, par exemple, effacer le dernier commit) puis ressortir les modifs mises de côté :git stash pop
-
Oups ! J'ai commité dans la mauvaise branche :
git reset --soft HEAD~1 ; git checkout la_bonne_branche ; git commit
(seulement si on a pas encore pushé) -
Oups ! J'ai commencé à travailler dans master au lieu de créer une branche, et j'ai fait plusieurs commits depuis abcdef ! Je déplace mes commits sur une nouvelle branche :
git branch une-nouvelle-branche ; git reset --hard abcdef ; git checkout une-nouvelle-branche
(seulement si on a pas encore pushé) -
Commiter une modif oubliée dans un vieux commit abcdef (à ne pas faire si on a déjà pushé) :
git stash
git rebase -i abcdef^
- remplacer
pick
paredit
sur la ligne abcdef, enregistrer, quitter git stash pop
git add le_fichier_modifié
git commit --amend
git rebase --continue
git add le_fichier_modifié
git commit --fixup abcdef
git rebase -i --autosquash abcdef^
- enregistrer, quitter (rien d'autre)
- Si le fichier a été modifié plusieurs fois entre ce vieux commit et le dernier commit, il peut y avoir un conflit
- Cette manip change tous les hashes depuis abcdef jusqu'au plus récent
-
git show-ref --tags
= liste des tags avec hash de commit correspondant (ou voir dans.git/refs/tags
) -
git push origin master
(on ne préciseorigin master
que la première fois, ensuite c'est implicite) (voir aussi--set-upstream
plus bas) -
git pull
est un raccourci pourgit fetch ; git merge FETCH_HEAD
mais l'avantage de ces deux commandes séparées c'est qu'on peut faire entre les deuxgit log ..origin/master
ougit diff ..origin/master
-
Pour voir le log depuis le dernier push :
git log origin/master..
-
Créer une branche locale et la pousser sur le serveur :
git checkout -b une_branche
puisgit push origin une_branche
Note : depuis git v2.23 (2019) on peut fairegit switch -c une_branche
au lieu degit checkout -b une_branche
-
Créer une branche à partir d'un vieux commit abcdef :
git checkout -b nom_branche_retour_arrière abcdef
-
Récupérer une branche distante : d'abord récupérer la liste des branches par
git fetch origin
puisgit checkout -b nom_de_la_branche origin/nom_de_la_branche
et surtout pasgit pull origin nom_de_la_branche
sinon ça merge dans la branche locale courante ! -
git branch
liste les branches locales,git branch -a
liste aussi les distantes récupérées,git branch -r
seulement les distantes récupérées. -
Pour voir les branches distantes pas encore récupérées :
git ls-remote --heads origin
(--heads
pour ne pas lister les tags) -
git remote
liste les dépôts distants etgit remote -v
donne plus de détail. -
git remote show nom_du_remote
donne le détail d'un remote (Note: Les remotes sont écrits dans le fichier .git/config) -
git branch --set-upstream master bidule/master
fait en sorte que-
au prochain
git status
dans la branche master, on saura de combien de commits on avance sur le dépot "bidule" (dans la branche master) (mais on peut savoir ça aussi avec la commandegit cherry
à ne pas confondre avec cherry-pick) -
au prochain
git push
sur cette branche on enverra les modifs sur le remote "bidule" (et non sur origin)
-
au prochain
-
On peut faire d'une pierre deux coups avec
git push -u bidule master
: pousser vers "bidule/master" et mémoriser que c'est la nouvelle branche "upstream" (vers laquelle on poussera par défaut la prochaine fois si on fait justegit push
) -
Note : Les deux commandes précédentes sont mémorisées dans
.git/config
sous la forme :[branche "master"] remote = bidule
-
On peut voir vers quel remote on pousse par défaut (quel est l'upstream) en faisant
git branch -vv
qui l'affiche sous la forme[bidule/master]
-
Pousser vers un nouveau dépôt :
git remote add nom_du_remote user@server:~/git/Truc.git
puisgit push --set-upstream nom_du_remote master
par exemple. -
Faire une correction sur la prod (master) pendant que je développe dans une branche (feature-evol-123) et intégrer cette correction dans la branche en cours de développement :
git checkout master
vi truc
git commit -m "correctif bidule" truc
git checkout feature-evol-123
git rebase master
Après ça, c'est comme si j'avais commencé à travailler sur mon évol 123 sur la base du code corrigé de truc. -
Ma méthode préférée pour fusionner une branche d'évolution (feature branch) dans la branche principale (master, historiquement) :
git switch master ; git merge --no-ff évol-bidule
parce que ça permet de bien visualiser quand on a créé la branche d'évol et quand on l'a intégrée dans master avec la commandegit log --graph --oneline
-
Fusionner une branche B dans une branche A en gardant la version de B s'il y a un conflit sur des fichiers (ouf!) :
git checkout A ; git merge -X theirs B
(il faut vraiment être sûr de vouloir faire ça !) -
Appliquer un commit abcdef d'une branche A sur une branche B :
git checkout B ; git cherry-pick abcdef
. Pour ignorer des différences d'espaces :-X ignore-all-space
(cf. Merge Strategies) (ne reste plus qu'à régler les conflits...) -
Recette pour ignorer ce fichu
.htaccess
qu'on a encore commité par erreur : l'ajouter dans.gitignore
puisgit rm --cached .htaccess
puisgit commit
(le fichier n'est pas effacé en local) -
Exporter un dépôt (par exemple pour publier un site) :
mkdir /tmp/cible && git checkout-index --prefix=/tmp/cible/ -a
-
Aplatir les derniers commits sur master pour nettoyer l'historique (supprimer les commits intermédiaires sans intérêt) avant de pusher sur origin/master :
git rebase --interactive origin/master
puis remplacerpick
parsquash
sauf sur la première ligne. (ne jamais faire ça après un push, sinon c'est le bazar pour les autres contributeurs) -
Aplatir les commits d'une branche B au moment de la fusionner, pour garder un historique propre :
git merge --squash B
(ça ne supprime pas la branche B qu'on peut conserver localement pour soi, pour mémoire, comme un brouillon) -
Cloner une seule branche d'un dépôt :
git init ; git remote add -t branche origin utilisateur@serveur:/chemin ; git fetch ; git checkout branche
(Peut éviter de passer par erreur dans une branche de dev sur un serveur de prod où on déploie avec git. On voit qu'on a cloné une seule branche avecgit branch -r
) -
git reflog
donne un historique de toutes les manipulations (commit, checkout, merge, etc...) qui peut éventuellement être pratique si on a écrit quelque part "bla bla au commit 123abc etc" et si le hash 123abc a été changé en 456abc par un rebase ou un commit --amend. Au moins, on peut retrouver dans le reflog la première ligne du commentaire de commit de 123abc et peut-être le relier à 456abc... -
Recréer un espace de travail à partir d'un vieux dossier
.git
sauvegardé quelque part :mkdir espace-de-travail
cp -a le-vieux-point-git/ espace-de-travail/.git
cd espace-de-travail/
git reset --hard HEAD
Sinon, pour tout savoir sur les branches, tout est très clairement expliqué sur git-scm.com/book/fr/Les-branches-avec-Git-Brancher-et-fusionner...