====== 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]]