====== Git ======
__**Tuto :**__
* https://git-scm.com/book/fr/v2
* https://www.grafikart.fr/formations/git
* https://education.github.com/git-cheat-sheet-education.pdf
* http://cdiese.fr/git-en-5-min/
* https://delicious-insights.com/fr/articles/bien-utiliser-git-merge-et-rebase/
===== .gitignore =====
Créer un fichier ''.gitignore'' pour ignorer certains fichiers, répertoires, extensions :
*
!*/
*.swp
*.swo
!.gitignore
!README.md
!/dir2/
/dir2/dir1/passwd
!/dir1/dir2/file2
===== Outils =====
==== ungit ====
* [[https://github.com/FredrikNoren/ungit|Officiel]]
* [[https://www.grafikart.fr/formations/git/ungit|Tuto]]
==== gitkraken ====
* [[https://support.gitkraken.com/how-to-install|Officiel]]
* [[https://www.grafikart.fr/formations/git/gitkraken|Tuto]]
==== Astuces ====
* Affiche la liste, taille et attributs des fichiers :
$ git ls-tree --full-tree -rl HEAD
* Affiche la racine du projet géré par git :
$ git rev-parse --show-toplevel
/home/gigix/test-github/gigix
* Affiche tous les changements sur un fichier :
* pour touts les [[#commit]] :
$ git rev-list --all --abbrev-commit | xargs git grep -n mon_fichier
* pour les 10 derniers [[#commit]] :
$ git rev-list --abbrev-commit --all --max-count=10 | xargs git grep -n mon-fichier
* pour tous les fichiers xml du répertoire dir1 :
$ git rev-list --all --abbrev-commit | xargs -I{} git grep dir1 {} -- ".xml"
* Vérifie si un fichier match avec le fichier [[#gitignore|.gitignore]] :
$ git check-ignore -v dir11/file
.gitignore:1:* dir11/file
* Affiche la [[#branch|branche]] courante :
$ git rev-parse --abbrev-ref HEAD
master
* Remplace tous les nom et email d'un auteur par un autre pour l'ensemble des [[#commit]] :
$ git filter-branch --commit-filter '
if [ "$GIT_AUTHOR_NAME" = "root" ];
then
GIT_AUTHOR_NAME="Ghislain LE MEUR";
GIT_COMMITTER_NAME="Ghislain LE MEUR";
GIT_AUTHOR_EMAIL="gigi206@users.noreply.github.com";
GIT_COMMITTER_EMAIL="gigi206@users.noreply.github.com";
git commit-tree "$@";
else
git commit-tree "$@";
fi' -f HEADt commit-tree "$@";
fi' HEAD
* Efface ''mydir/myfile'' pour l'ensemble des [[#commit]] :
$ git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch mydir/myfile' --prune-empty --tag-name-filter cat -- --all
$ git push origin --force --all
$ git push origin --force --tags
$ git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
$ git reflog expire --expire=now --all
$ git gc --prune=now
==== Configuration ====
* Désactiver le pager :
$ git config --global --replace-all core.pager "less -F -X"
===== Subcommand =====
==== aide ====
Affiche l'aide :
$ git help
$ git help config
==== config ====
Gère la configuration locale ou globale des projets git.
**__Options :__**
* **--global** : pour tous les repo git (fichier ''/home/gigix/.gitconfig'')
* **--local** : pour le repo courant (par défaut dans le répertoire ''.git/config'' du projet)
* Pousser une configuration :
$ git config --global user.name "GigiX"
$ git config --global user.email gigixdvelannes.com
* Voir la configuration :
$ git config -l --global
$ git config -l --local
* Editer la configuration :
$ git config -e --global
$ git config -e --local
$ git remote set-url origin https://github.com/gigi206/test.git
* Afficher la configuration d'une variable :
$ git config --get remote.origin.url
==== init ====
Initialise un nouveau projet git :
* Créer un repo local git pour gérer notre projet :
$ git init
* Créer un repo local pour accueillir les [[#push]] :
$ git init --bare
==== remote ====
Gère les remotes.
* Lister les remote :
$ git remote -v
origin https://github.com/gigi206/test.git (fetch)
origin https://github.com/gigi206/test.git (push)
* Ajoute un remote :
$ git remote add origin /home/gigix/test-repo
$ git remote add upstream https://github.com/gigi206/vim-gigix.git
* Supprimer un remote :
$ git remote remove origin /home/gigix/test-repo
* Renommer un remote :
$ git remote rename origin test
En principe on appelle **origin** le repo sur lequel on envoie les [[#push]] et **upstream** le dépôt que l'on a forké pour par exemple envoyé un **pull request**.
==== clone ====
Clone un projet.
* Copie un repo distant :
$ git clone https://github.com/gigi206/vim-gigix.git myclonerepo
* Copie le dépôt avec les 10 derniers [[#commit]] (utile pour récupérer les gros dépôts) :
$ git clone --depth 10 https://github.com/gigi206/vim-gigix.git
==== status ====
Afficher l'état de notre copie de travail :
$ git status
$ git status -s
==== add ====
* Ajoute un fichier à l'index :
$ git add mon_fichier
* Ajoute tous les fichiers à l'index :
$ git add --all
$ git add -A
* Ajouter le répertoire courant :
$ git add .
==== rm ====
* Supprimer un fichier à l'index :
$ git rm mon_fichier
* Supprimer les fichier de l'index :
$ git rm --cached monfichier
* Supprimer tous les fichiers d'un répertoire :
$ git rm -r mon_repertorie
==== mv ====
Renomme un fichier, dossier ou lien de l'espace de travail :
$ git mv file1 file2
==== commit ====
Gère les révisions localement uniquement.
* Envoie de tous les fichiers trackés :
$ git commit -a -m 'mon message'
* Envoie d'un fichier :
$ git commit -m 'mon message' mon-fichier
* Changer l'auteur :
$ git commit --author "GigiX" -m "message"
* Modifie le dernier commit en ajoutant nos modifications actuelles au derniers commit et en modifiant son message (ne crée pas une nouvelle entrée dans [[#log|l'historique]] :
$ git add -A
$ git commit --amend –m "message"
==== push ====
Envoie les [[#commit]], [[#branch|branches]], [[#tag|tags]] au [[#remote]]
* Envoyer ses modifications de la [[#branch|branche]] master au [[#remote]] origin:
$ git push -u origin master
* Envoyer toutes ses [[#branch|branches]] et [[#tag|tags]] :
$ git push --all --tags
==== fetch ====
Met à jour le répertoire local depuis un [[#remote]] :
$ git fetch origin master
$ git fetch --all
==== pull ====
* Récupérer les modifications du [[#remote]] :
$ git pull
* Pull avec un [[#rebase]] à la place d'un [[#merge]] :
$ git pull --rebase
Un [[#pull]] équivaut à faire un [[#fetch]] suivi d'un merge [[#merge]].
Pour que git fasse toujours un [[#rebase]] à la place d'un [[#merge]], il faut ajouter la [[#config|configuration]] ci-dessous :
$ git config --global branch.autosetuprebase always
* Pull avec un [[#rebase]] sur le dernier [[#commit]] uniquement pour préserver le graph [[#log|d'historique]] :
$ git pull --rebase=preserve
Pour que git fasse toujours un **preserve**, on peut ajouter à la configuration de git :
$ git config --global pull.rebase = preserve
* Sans **fast-forward** (l'équivalent d'un [[#merge]] dans [[#log|l'historique]]) :
$ git pull --no-ff
* En forçant le **fast-forward** (l'équivalent d'un [[#rebase]] dans [[#log|l'historique]]) :
$ git pull --ff-only
==== cherry-pick ====
Récupère seulement un seul [[#commit]] effectué sur une autre branche complètement séparée de la branche courante. Elle permet d’appliquer les modifications correspondant à un seul [[#commit]] sans effectuer de [[#merge]] ou [[#rebase]].
* Se placer dans la branche sur laquelle on veut appliquer le [[#commit]], par exemple **//master//** :
$ git checkout master
$ git cherry-pick
Il n’est pas nécessaire d’effectuer un [[#commit]] après cette étape, [[#cherry-pick]] effectue lui-même un [[#commit]]. Cette fonctionnalité est très pratique par exemple lorsqu'on a corrigé un bug sur la branche dev et qu'on veut pousser cette correction sur la branche master.
==== stash ====
Imaginons, vous avez des modifications en cours de création et vous avez envie de les mettre de côté temporairement, plutôt que de [[#commit]] les différentes modifications vous pouvez les stocker grâce à **stash**.
* Backup les modifications courantes :
$ git stash -u
$ git stash save -u mon message
* Ramène les modifications dans une nouvelle [[#branch|branche]] :
$ git stash branch gigix
* Restaure les dernières modifications sauvegardées en **stash** :
$ git stash apply
$ git stash apply stash@{1}
* Supprime le backup :
$ git stash drop
$ git stash drop stash@{1}
* Restaure et supprime le backup :
$ git stash drop
$ git stash drop stash@{1}
* Affiche ce qui a été sauvegardé :
$ git stash pop
$ git stash pop stash@{1}
* Voir l'ensemble des modifications sauvegardées :
$ git stash list
==== log ====
Affiche les révisions, auteurs, date, diff des [[#commit]].
* Affiche les logs des [[#commit]] :
$ git log --oneline
$ git log --raw --oneline -10
$ git log --stat
$ git log -p
$ git log --graph --decorate --oneline
$ git log --author="GigiX" -p hello.py
$ git log master..origin/master # Permet de voir les commits entre ma branche master et celle du remote
$ git log 852e094^..852e094 -p
$ git log 852e094..HEAD --oneline
$ git log 852e094..HEAD -L 2,6:mon_fichier
$ git log --grep=gigix
$ git log --since="1 month" --until="1 day" --oneline
$ git log --graph --abbrev-commit --decorate --date=relative --all
$ git log --graph --topo-order --decorate --oneline --all
$ git log --graph --topo-order --decorate --oneline --boundary test..master
$ git log --graph --full-history --all --color --pretty=format:"%x1b[31m%h%x09%x1b[32m%d%x1b[0m%x20%s"
* Rechercher un fichier et ses révisions (même supprimé) :
$ git log --all --full-history -- test
* Ajouter un alias pour les logs (celui que j'utilise) :
$ git config --global alias.lg 'log --graph --full-history --color --oneline --format="%x1B[33m%h%x1B[32m%d %x1B[0m%s %x1B[36m(%an %cr)%x1B[0m"'
$ git config --global alias.lga 'log --graph --full-history --color --oneline --all --format="%x1B[33m%h%x1B[32m%d %x1B[0m%s %x1B[36m(%an %cr)%x1B[0m"'
$ git lg --all
$ git lga
$ git lg --first-parent mybranch
$ git lg --parents mybranch
==== reflog ====
Affiche les logs des [[#rebase]], [[#merge]], etc...
$ git reflog
==== show ====
* Affiche les différences sur les fichier pour un [[#commit]] ou affiche un fichier pour un [[#commit]] donné :
$ git show 651bb22
$ git show 651bb22:README.md
$ git show --topo-order --decorate --oneline
$ git show --topo-order --decorate --oneline --all
==== blame ====
* Affiche un fichier ligne par ligne avec son [[#commit]] et son auteur.
$ git blame myfile
$ git blame -n myfile
$ git blame -L 3,6 -C myfile
==== diff ====
* Affiche la différence entre 2 [[#commit]] :
$ git diff HEAD^^
$ diff 8084b3f..edeaa87
$ git diff 26e5423:.gitignore 3c99fa1:.gitignore
$ git diff 3c99fa1..26e5423 dir1/dir2/file2
* Comparer avec l'index :
$ git diff --cached
* Créer et appliquer un patch :
$ git diff 2d8de18..17dc028 > path
$ git apply patch
$ git diff
Ou (utilise plutôt [[#revert]]) :
$ git diff 2d8de18..17dc028 | patch
$ git status
==== difftool ====
Utilise un outil tiers comme vim pour afficher les [[#diff]] (comme **vimdiff** par exemple).
$ git difftool HEAD^..HEAD -t vimdiff
Ajouter la configuration ci-dessous pour ne pas avoir à spécifier à chaque fois ''-t vimdiff'' :
$ git config --global alias.vimdiff difftool
==== branch ====
Gère les branches.
* Crée la branche gigix :
$ git branch gigix
* Détruit la branche gigix localement :
$ git branch -d gigix
* Détruit la branche distante gigix sur le remote origin :
$ git push -u origin -d gigix
* Renommer une branche :
$ git branch -m gigix new
* Liste les branches :
$ git branch -vv --all
==== checkout ====
Change de [[#branch|branche]] ou restaure un fichier d'un autre [[#commit]].
* Se positionner sur une révision ou branche pour l'explorer :
$ git checkout HEAD^^
$ git checkout 8084b3f
$ git checkout master
$ git checkout mabranche
* Restaurer un fichier d'un ancien [[#commit]] :
$ git checkout 8084b3f 1
* Créer la [[#branch|branche]] **gigix** et se positionner dessus :
$ git checkout -b gigix
==== revert ====
* Annule seulement les [[#commit]] sélectionnés (n'altère pas [[#log|l'historique]]) :
$ git revert 416515b
$ git revert 7db7d1c..852e094
$ git revert 17dc028 --no-commit
==== reset ====
Retourne à un ancien [[#commit]] et supprime le [[#log]].
* Déplace le **HEAD** sur un [[#commit]] sans rien faire d'autre (ne touche pas à l'index, ni au répertoire de travail) :
$ git reset --soft 181d77f
$ git log --graph --topo-order --decorate --oneline --all --color
$ git status
$ git diff --cached
$ git commit -m "message"
* L'option **--mixed** est l'option par défaut (il n'est donc pas nécessaire de la spécifier). Il déplace le **HEAD** et l'index :
* Sur une révision complète :
$ git reset --mixed 181d77f
$ git log --graph --topo-order --decorate --oneline --all --color
$ git status
$ git diff
$ git commit -a -m "message"
* Seulement sur un fichier :
$ git reset 181d77f myfile
$ git log --graph --topo-order --decorate --oneline --all --color
$ git status
$ git diff
$ git commit -a -m "message"
* Enlève le travail de l'index (donné avec **git add**) :
$ git reset
L'option **--hard** supprimera vos fichiers et votre [[#log|historique]]. L'ensemble de votre travail sera donc perdu. Utiliser [[#reset]] avec précaution !
* L'option **--hard** déplace le **HEAD**, l'index et l'espace de travail (vous perdrez donc votre travail en cours) :
$ git reset --hard 181d77f
* Remet l'espace de travail à l'état du dernier [[#commit]] :
$ git reset --hard
==== clean ====
Supprime les fichiers non trackés.
* Mode intéractif :
$ git clean -i
* Simule :
$ git clean -n
* supprime les fichiers :
$ git clean -f
==== rebase ====
Rapatrie les [[#commit]] d'une autre [[#branch|branche]] dans celle courante.
* Si la [[#branch|branche]] **test** est en avance et qu'on souhaite mettre [[#branch|branche]] courante au même niveau que **test** :
$ git rebase -i test
* Si l'on souhaite éditer, ou fusionner les 3 [[#commit]] précédents :
$ git rebase -i HEAD~3
Les [[#commit]] sont indiqués du plus vieux au plus récent (l’ordre est inversé).
* Rejoue les modifications de dev sur le sommet de la branche master :
$ git rebase master dev
#ou
$ git rebase -i master dev
#On peut ensuite avancer master au niveau de dev et supprimer dev
$ git checkout master
$ git merge dev --ff-only
$ git branch -d dev
* Récupérer une sous branche d'une branche dans master :
Prenons le cas ci-dessous :
{{ :tuto:linux:interesting-rebase-1.png?nolink |}}
Pour fusionner la sous-branche client de server dans master, on fera :
$ git rebase --onto master server client
Ce qui donnera :
{{ :tuto:linux:interesting-rebase-2.png?nolink |}}
==== merge ====
Fusionne 2 [[#branch|branche]] et garde une trace dans [[#log|l'historique]].
* Merge la [[#branch|branche]] test sur celle courante :
$ git merge test
* Merge avec fast forward (équivaut à un [[#rebase]]) :
$ git merge test --ff-only
* Merge sans fast forward :
$ git merge test --no-ff
* Merge une branche dans une autre sans modifier l'historique (exemple : merge la branche gigix dans la branche master) :
$ git checkout master
$ git merge --squash gigix
$ git diff --staged
$ git commit -a -m "New feature gigix"
#Si on souhaite supprimer gigix
$ git push origin -d gigix
==== shortlog ====
Affiche les statistiques.
* Affiche le [[#commit]] par utilisateur :
$ git shortlog -sn
==== archive ====
Créer une archive du dépôt git.
* Affiche les formats de compressions :
$ git archive -l
* Compresser le dépôt courant :
$ git archive --format=tar.gz -o gigix.tar.gz master
$ git archive --format=tar.gz -o gigix.tar.gz HEAD^1
==== tag ====
Gère les tags.
* Affiche les tags :
$ git tag
$ git tag -n
$ git tag -l -n
* Crée le tag **test** :
$ git tag test 6452b46
* Crée un tag annoté :
$ git tag -a test 6452b46 -m "gigix"
* Supprime le tag **test** :
$ git tag -d test
==== grep ====
Recherche dans les fichiers :
$ git grep --all-match -e "^gigix$" -e "modif"
$ git grep -i 'gigix' $(git rev-list --all --abbrev-commit)
==== bisect ====
Trouver par recherche dichotomique la modification qui a introduit un bug :
* [[https://www.youtube.com/watch?v=842RnLfqb3g|Tuto git bisect]]
==== submodule ====
* Config :
git config --local status.submoduleSummary true
git config --local diff.submodule log
* Ajout de submodules:
git submodule add -b docker-edge-to-nightly --force https://github.com/geerlingguy/ansible-role-docker dev/docker
git submodule add -b main https://github.com/roles-ansible/ansible_collection_time.git dev/ntp
git submodule add https://github.com/bennojoy/nginx dev/nginx
* Etat des submodules entre ceux déclarés et ceux connus :
cat .gitmodules
cat .git/config
* Cloner un projet en copiant également les submodules :
git clone --recursive https://xxx xxx
* Initialiser un projet :
git submodule update --init --recursive
* Mettre à jour les sous-projets à la dernière version de la branche :
git submodule update --init --recursive --remote --rebase
* Status:
git submodule status --recursive
* Vérifier le SHA avant de mettre à jour les submodules:
git ls-tree -r HEAD dev/docker
160000 commit c700df55425430d352d16b2a59bea2ab67a6a8bb dev/docker
* Nettoyer les sous-projets :
git submodule status --recursive
git fetch --recurse-submodules --prune
git submodule foreach --recursive 'git reset --hard'
git submodule foreach --recursive 'git clean -fdx'
* Changement de branche de tracking :
git submodule set-branch --branch master dev/docker
git submodule set-branch --default dev/docker
* Suppression d'un submodule:
git submodule deinit -f -- nginx
git rm -f nginx
rm -rf .git/modules/nginx
==== worktree ====
* Créer un worktree sur une nouvelle branche nommée **myfeature** depuis la branche main dans **../wk/myfeature** :
git worktree add -b myfeature ../wk/myfeature main
* Lister les worktree:
$ git worktree list
/home/gigi/git_test4 d67857c [main]
/home/gigi/wk/myfeature d67857c [myfeature]
* Supprimer un worktree:
git worktree remove /home/gigi/wk/myfeature