Git

Git est un logiciel de gestion de versions. Il permet de suivre l’évolution d’un fichier (version 1, version 2, version 3, etc), de savoir qui a effectué chaque modification, quand et pourquoi, et de revenir en arrière en cas de problème.

Il permet également de travailler à plusieurs sur des fichiers, sans risquer que les modifications d’une personne efface les modifications d’une autre.

Cheatsheet Git


Installation

OS Installation
Windows/Mac Installer Gitbash
Linux sudo apt-get install git

Tree-ish

Certaines commandes git prennent en paramètre un type tree-ish.
Un objet “tree-ish” désigne une arborescence à un instant de l’historique.
Quelques exemples :

Exemple Description
1ff91bf13a6d5461a85ad44f4cbacd77ee6c4917 SHA du commit
1ff91bf1 SHA abrégé (non ambigu, au moins 4 caractères)
HEAD Pointeur en cours
master Branche master
v0.99.8 Tag v0.99.8
HEAD^, acf87505^, master^
HEAD~1, HEAD~
Le parent de...
HEAD^^
HEAD~2
Le grand-parent de...
HEAD^^^
HEAD~3
L'arrière grand-parent de...

Versionner un projet

La première étape est d’ajouter git au répertoire dont les fichiers doivent être versionnés, puis de versionner les fichiers lorsqu’ils sont prêts.

cd monrepertoire
git init
git status
git diff

git add --all
git status

git commit -m "Init"

help

Pour afficher la man page d’une commande git, utiliser git help cmd. Exemple: git help init. On peut obtenir le même résultat en tapant man git-init.


init

git init permet d’initiliser git pour le répertoire courant et ainsi de versionner les fichiers qui s’y trouvent.
Crée un répertoire .git, où seront enregistrés les commits (= différentes versions) des fichiers.

git init     Initialise git pour le répertoire courant

add

Quand des fichiers ont été modifiés dans un répertoire versionné (= qui contient un répertoire .git), il faut pour enregistrer une version

  1. indiquer quels fichiers doivent être enregistrés (= les mettre dans le staging area)
  2. enregistrer ces fichiers (= créer un commit)
git add file.txt Ajoute file.txt au staging area
git add -u Tous les fichiers modifiés et supprimés (mais pas les nouveaux)
git add .
git add --all
Tous les fichiers ajoutés, modifiés et supprimés
git add mydir/ Tous les fichiers dans le répertoire mydir
git add *.txt Tous les fichiers .txt dans le répertoire courant (avec l'expansion du shell)
git add '*.txt' Tous les fichiers .txt dans tout le projet (avec l'expansion de git)

.gitkeep

Git est conçu pour mémoriser les versions de fichiers, pas les répertoires. Les répertoires vides sont donc ignorés et ne peuvent pas être versionnés. Par convention, pour versionner un répertoire vide on y place un fichier .gitkeep vide.


rm

git rm file.txt permet de supprimer un fichier. Le fichier pourra être récupéré via un checkout d’un commit antiérieur.

git rm file.txt Supprimer file.txt du disque et de git (ou uniquement de git si supprimé manuellement)
git rm --cached file.txt Supprimer file.txt de git mais pas du disque

mv

git mv oldname newname permet de renommer et/ou déplacer un fichier

git mv file.txt newfile.txt Renommer file.txt en newfile.txt

Notons qu’on peut obtenir le même résultat avec rm suivit de add. Git compare les données entre les deux fichiers et en déduit qu’il s’agit d’un déplacement ou non. Les fichiers n’ont pas a être identiques, le seuil de tolérance est d’environ 50%.

mv file.txt newfile.txt
git rm file.txt
git add newfile.txt

status

git status liste les fichiers qui ont été modifiés depuis le dernier commit (ajoutés, modifiés et supprimés). Indique lesquels sont dans le staging area (= ceux qui ont été marqués pour faire partie du prochain commit) ou non.

git status Affiche les fichiers modifiés depuis le dernier commit
git status -s  Affiche les fichiers modifiés de manière condensée
$ git status

    Changes to be committed:
        (use "git reset HEAD ..." to unstag)

            modified: this.txt

    Untracked files:
        (use "git add ..." to include in what will be committed)

            that.txt

$ git status -s

    M  this.txt
    ?? that.txt

diff

git diff affiche les modifications apportés aux fichiers depuis le dernier commit.
Par défaut, les fichiers dans le staging area ne sont pas affichés.

git diff Affiche les modifications des fichiers
git diff --staged Affiche les modifications des fichiers dans le staging area
git diff file.txt Affiche les modifications du fichier file.txt
git diff --color-words file.txt Affiche les modifications du fichier file.txt ligne par ligne
git diff HEAD^^ Affiche les modifications depuis l'avant-dernier commit
git diff HEAD^..HEAD Entre le dernier et l'avant-dernier commit
git diff f5a6..4sdsd Entre les deux commits donnés
git diff master cat Entre les branches master et cat
# Dans file.txt, sur la ligne 1, "Hello World" est devenu "Hello Everybody" :

$ git diff

    --- a/file.txt
    +++ b/file.txt
    @@ -1 +1 @@
    -Hello World
    +Hello Everybody

$ git diff --color-words file.txt

    --- a/file.txt
    +++ b/file.txt
    @@ -1 +1 @@
    Hello WorldEverybody
# Vérifier si le fichier Dockerfile ou requirements.txt
# Ont changés depuis le tag staging-1.0
git diff staging-1.0..HEAD -- Dockerfile requirements.txt

Setter l’option git config color.ui true pour que diff affiche des couleurs

show

git show permet d’afficher le contenu d’un commit donné

git show f5a6

commit

git commit -m "message" permet de commiter les fichiers du staging area.
Le message de log doit expliquer brièvement à quoi sert le commit.

git commit -m "about" Commite les fichiers dans le Staging area, avec le message de log "about"
git commit -am "about" Raccourcis pour git add .; git commit
Rappel: ajoute les fichiers modifiés, mais pas les nouveaux ni supprimés

Il est également possible d’ajouter les modifications au dernier commit avec l’option --amend. C’est utile si l’on a oublié d’ajouter des fichiers au staging area. Cette méthode modifie le SHA et la date du commit.
NB Pour modifier un commit plus ancien que le dernier, il faut créer un nouveau commit (et éventuellement utiliser un rebase pour rassembler plusieurs commits)

git commit --amend -m "message" Modifie le message et/ou les fichiers du dernier commit

Maintenir

Quand ils versionnés, il est possible de récupérer les fichiers tels qu’ils étaient lors d’une ancienne version, de voir qui a fait des modifications et pourquoi.

git log
git checkout 1ff91bf1

log

git log liste tous les commits effectués, du plus récent (en haut) au plus ancien (en bas).
Seule la première ligne du message de commit est affichées (moins de 50 caractères).

git log Affiche tous les commits
git log -n 2 Affiche les 2 derniers commits
git log -- file.txt Les commits qui ont affectés le fichier file.txt
git log --oneline Affiche les commits en version condensée
git log -p Affiche les commits et leurs diffs
git log --stat Affiche les commits et leur statut (x file changed, x insertion, x deletion)

Filtres

--since=2001-01-02 Commité après le 02/01/2001
--since=1.day.ago Depuis hier
--since="1 year 6 months ago" Depuis un an et demi
--until=2001-01-02 Avant le 02/01/2001
--author=Charlie Commité par Charlie
--author="Alice\|Bob" Par Alice ou Bob
--grep="Init" Commit dont la description contient "Init", sensible à la casse
-i --grep="Init" Dont la description contient "Init", insensible à la casse

Format date :

(last|next) (second|minute|hour|day|week|month|year)
(X (seconds|minutes|hours|days|weeks|months|years))+ ago
(yesterday|tomorrow)
@XXXXXXXXX (= since epoch)

info date pour plus de détails (Entrée pour suivre un lien, q pour quitter)

Formats

$ git log

    commit bcb29516a61cbdf6c8d6eb85d12376344f693cd3
    Author: username 
    Date:   Tue Nov 29 19:52:45 2016 +0100

        2

    commit 6160e8d5ef1c2c2fb78d9af986d440d499220bbe
    Author: username
     Date:   Tue Nov 29 19:24:26 2016 +0100

        Init

$ git log --oneline

    bcb2951 2
    6160e8d Init

$ git log --pretty=format:"%h %ad - %s [%an]"

    bcb2951 Tue Nov 29 19:52:45 2016 +0100 - 2 [username]
    6160e8d Tue Nov 29 19:24:26 2016 +0100 - Init [username]

$ git log --pretty=format:"%C(white reverse blue bold)%h%Creset %s"

    # Affiche le hash en bleu foncé sur fond blanc
    bcb2951 2
    6160e8d Init

Instructions pretty format :

%adauthor date
%anauthor name
%hSHA hash abrégé
%ssubject
%ddescription
%C(colors)switch color
Couleurs : normal, black, red, green, yellow, blue, magenta, cyan, white
Attributs : bold, dim, ul (underline), blink, reverse (swap foreground and background)
%Cresetreset color

git help log pour la doc complète (/PRETTY FORMATS + entrée + n)


blame

git blame file.txt permet d’afficher l’historique de modification du fichier file.txt, ligne par ligne, avec la date et l’auteur de la modification. Le format de sortie est le suivant : <hash> (<author> <date> <line>) <content>.

git blame file.txt Affiche l'historique des modifications du fichier file.txt
git blame file.txt --date short Affiche uniquement la date (pas l'heure)
$ git blame --date short file.txt

    96776 (Gregg 2012-06-29 9) Hello


checkout

git checkout tree-ish permet de récupérer les fichiers d’un commit antérieur

git checkout v0.0.1 Récupère les fichiers du commit taggé v0.0.1
git checkout bbd70e01 -- file.txt Le fichier file.txt tel qu'il était au commit bbd70e01
git checkout -- file.txt Le fichier file.txt du dernier commit (= supprime les modifications locales)
git checkout mybranch Les fichiers de la branch mybranch

revert

git revert commit permet d’inverser les modifications d’un commit.
L’annulation est commitée tout de suite, sans possibilité de modifier.
Si des fichiers ont été renommés ou déplacés, une technique plus avancée sera nécessaire (merge)

git revert bbd70e01 Annuler les modifications de la révision bbd70e01

reflog

À chaque fois que le pointeur HEAD bouge (commit, checkout, reset, etc), une entrée est ajoutée dans le reflog. Un reflog est local à un projet et à une machine.

git reflogAffiche le reflog
$ git reflog

    43c13e7 HEAD@{0} reset: moving to 43c1
    1e62107 HEAD@{1} commit: Add third section

Le reflog est utile pour connaître le SHA des commits ou branches supprimés et de pouvoir ainsi les récupérer.

# Récupérer un commit
git reset --hard 1e62

# Récupérer une branche
git branch my_branch 280e

clean

git clean permet de gérer les fichiers non versionnés

git clean -n Lister tous les fichiers non versionnés (ni staging area ni commits)
git clean -f Supprimer tous les fichiers non versionnés

ls-tree

git ls-tree tree-ish liste tous les fichiers commités à un instant donné.
Le format de sortie est le suivant: <mode> <type> <refname> <path> (blob = fichier, tree = répertoire)

git ls-tree HEAD Liste tous les fichiers commités (à la racine)
git ls-tree -r HEAD Liste tous les fichiers commités à la racine et dans les sous-répertoires
git ls-tree HEAD^ Liste tous les fichiers qui étaient commités lors de l'avant-dernier commit
git ls-tree master assets/ Liste tous les fichiers dans le répertoire assets/ de la branche master
$ git ls-tree HEAD

    100644 blob badbc02f6c1a754631baa23fc51d58df8276b359    .gitignore
    100644 blob e01bb431533191a62ec6c2d85b439cec2e7a2cdc    _config.yml
    040000 tree 2eb76a49dd221f11c93a51d0a7a4a8eee35e1446    assets

filter-branch

git filter-branch permet de supprimer des fichiers de l’historique, par exemple pour supprimer des données sensibles (mots de passe), des fichiers binaires lourds ou des fichiers qui violent un copyright.

filter-branch fait un checkout de chaque commit, exécute la commande shell donnée, et re-commit. À noter que

  1. Si la commande shell échoue, il s’arrête.
    Faire en sorte que la commande shell marche toujours, ex: rm -f
  2. Compte tenu que l’on checkout chaque commit, le processus risque d’être très long s’il y a beaucoup de commits.
    Pour accélerer le traitement on peut
    • filtrer sur un commit spécifique
    • ne pas executer filter-branch sur les fichiers mais sur les index de fichiers dans l’historique (option index-filter). Dans ce cas il n’est pas possible d’utiliser des commandes shell mais uniquement dans commandes git
git filter-branch --tree-filter 'cmd' -- --allExecuter cmd (commande shell) sur tous les commits de la branche
git filter-branch --tree-filter 'cmd' -- HEADExecuter cmd (commande shell) sur le dernier commit
git filter-branch --index-filter 'cmd'Executer cmd (commande git) sur tous les commits

Exemples :

# Supprimer le fichier password.txt dans les commits de la branche, via rm (shell)
git filter-branch --tree-filter 'rm -f password.txt' -- --all
# Supprimer le fichier password.txt dans les commits de la branche, via git
git filter-branch --index-filter 'git rm --cached --ignore-unmatch passwords.txt'
# Supprimer le fichier password.txt dans le dernier commit
git filter-branch --tree-filter 'rm -f password.txt' -- HEAD

prune-empty

Après des suppressions de fichier, il arrive que des commits ne contiennent plus aucune modification.
L’option prune-empty permet de les supprimer.

Exemples :

# Supprimer les commits qui ne modifient aucun fichier
git filter-branch --f --prune-empty -- --all
# Supprimer le fichier password.txt et supprimer le commit s'il est vide
git filter --tree-filter 'rm -f password.txt' --prune-empty -- --all

reset

git reset tree-ish permet d’annuler des modifications ajoutées au staging area et/ou aux fichiers. Pour ce faire, on déplace HEAD (pointeur qui désigne le dernier commit de la branche en cours), donc on altère l’historique.

git reset --soft bbd70e01 Déplace HEAD à la révision bbd70e01, sans changer le staging area ni les fichiers locaux
En cas de commit, tous les commits qui suivent bbd70e01 sont supprimés - mais les modifications se retrouvent dans le nouveau commit
git reset --mixed bbd70e01 Déplace HEAD à la révision bbd70e01, calque le staging area sur cette révision sans changer les fichiers locaux
Appliqué à HEAD, il annule l'ajout des fichiers au staging area.
Mixed est le comportement par défaut de reset.
git reset --hard bbd70e01 Déplace HEAD à la révision bbd70e01, calque le staging area et les fichiers locaux dessus
ATTENTION, les modifications locales des fichiers sont définitivement perdues

Le log affiché par git log affiche les commits jusqu’à HEAD. Pour replacer le HEAD à un commit ultérieur après un reset (tant qu’on a pas commité les modifications), on utilise git reflog afin de récupérer le SHA du commit voulu et faire un reset dessus.


Configurer

Git peut être configuré: retenir le nom d’utilisateur et mot de passe, créer des alias, colorer l’interface, etc. Les configurations de Git peuvent être appliquées à tous les utilisateurs, à un seul utilisateur, ou sur un projet en particulier.

Ces configurations sont enregistrées dans des fichiers, qui peuvent être édités directement avec un éditeur de texte, ou en utilisant des commandes git (qui modifient les fichiers de configuration). Si deux fichiers définissent une même configuration, le local écrase le global.

Portée Description Commande (préfixe)   Path Linux Path Windows
Système S’applique à tous les utilisateurs git config –system   /etc/gitconfig Program Files\Git\etc\gitconfig
Utilisateur S’applique à un seul utilisateur git config –global   ~/.gitconfig $HOME\.gitconfig
Projet S’applique à un projet git config   myproject/.git/config myproject\.git\config

config

git config --global --list Liste les configurations globales
git config --local --list Liste les configurations locales
git config --list Liste les configurations globales (en premier) et locales
git config --global user.name Affiche la valeur de la config globale
git config --local user.name Affiche la valeur de la config locale
git config user.name Affiche la valeur de la config qui s'applique
git config --global user.name "username" Met à jour la valeur de la config globale
git config --global alias.olog "log --oneline"Définit un alias "olog"

Quelques configs utiles :

Globales :

user.name "username"            Nom d'utilisateur
user.email "bob@exemple.com"    Email
core.editor "vim"               Editeur de texte à utiliser
color.ui true                   Colorer l'interface

Locales :

core.autocrlf input             Changer les retours chariots CR/LF à LF au commit
core.autocrlf true              Changer les retours chariots LF à CR/LF au checkout

push.default matching           Pusher toutes les branches
push.default simple             Pusher la branche en cours (comportement par défaut avec Git 2.0)

pull.rebase true                Utiliser automatiquement rebase au pull (git pull --rebase)
rerere.enabled true             Activer reReRe (Reuse Recorded Resolution)

Alias :

Un alias peut s’appeler comme une commande git native, par exemple git olog.
Pour définir un alias :

git config --global alias.plog "log --pretty=format:'%h %s [%an]' --graph"
git config --global alias.lol "log --graph --decorate --pretty=oneline --abbrev-commit --all"

.gitattributes

Les attributs sont des configs de conversion des retours chariot. L’intérêt des attributs est qu’ils sont communs à toute personne qui participe au projet, contrairement aux configurations qui sont locales à une machine (puisqu’elles ne sont pas commités). Les attributs sont définis dans un fichier .gitattributes, à la racine du projet.

text=auto Choisir la conversion automatiquement (par défaut)
binary Ne faire aucune conversion
text Convertir au retour chariot de l'OS au checkout, convertir à LF au commit
text eol=crlf Convertir à CR/LF au checkout, convertir à LF au commit
text eol=lf Convertir à LF au commit

Exemple :

*       text=auto
*.html  text
*.css   text
*.bat   text eol=crlf
*.sh    text eol=lf
*.jpg   binary
*.png   binary

.gitignore

Il est également possible d’indiquer à git d’ignorer des fichiers, pour qu’il ne les affiche plus dans la liste des fichiers non versionnés. La liste des fichiers à ignorer est placée dans un fichier .gitignore à la racine du projet (commun entre tous les développeurs).

Les fichiers déjà versionnés ne sont pas ignorés, même s’ils obéissent à la règle.

Exemple :

*.log
npm-debug.log*
.grunt
bower_components
node_modules/
.env

Sont généralement ignorés

Listing des fichiers à ignorer selon le language / frameword / OS


exclude

Local au projet :

Même principe que le fichier .gitignore mais non versionné, le fichier .git/info/exclude contient la liste des fichiers à ignorer (à partir de la racine du projet).

Global :

Une liste de fichiers à ignorer globale peut également être mise en place (qui se cumule au .gitignore).
Elle doit être activé via les configs :

git config --global core.excludesfile ~/.gitignoreDéfinit l'emplacement du exclude global

update-index

Permet de modifier l’index du fichier dans git. Les modifications apportés sur l’index ne sont pas commités. Cette commande est particulièrement utile pour “geler” (freeze) le contenu de certains fichiers (que les modifications apportées au fichier soient ignorées par git), par exemple les fichiers de configuration.

git update-index --assume-unchanged foo.rbIgnore les changements de foo.rb. Le contenu du fichier est écrasé au pull. Utile lorsqu'on part du principe que le développeur ne va pas changer ce fichier (ex: package-lock.json)
git update-index --no-assume-unchanged foo.rbSupprime l'entrée assume-unchanged pour foo.rb
git update-index --skip-worktree foo.rbIgnore les changements de foo.rb. Le contenu du fichier est conservé au pull. Utile lorsqu'on part du principe que le développeur va changer ce fichier mais ne va pas commiter les changements (ex: docker-compose.yml)
git update-index --no-skip-worktree foo.rbSupprime l'entée skip-worktree pour foo.rb

Utiliser un serveur distant

Utiliser un serveur distant permet de partager des fichiers versionnés avec d’autres personnes, et qui peuvent éventuellement modifier ces fichiers. Les serveurs les plus connus sont Github et BitBucket (serveurs herbégés), Gitotis et Gitorious (serveurs auto-gérés).

Utiliser un serveur distant n’est absolument pas une obligation, le versionning peut complètement être gardé en local, dans le répertoire .git.

git clone http://github.com/username/myproject.git
git pull origin master
git push -u origin master

clone

git clone url permet de récupérer en local un projet situé sur un serveur distant, qui a été crée au préalable sur ce serveur. Par défaut, seule la branche principale (généralement master) est téléchargée.

Un projet versionné peut être envoyé à différents serveurs (ex: serveur de versioning Github et serveur de production Heroku). Pour cette raison un projet peut être associé à plusieurs URL, auxquelles on donne un raccourcis nommé - ce qui permet de facilement envoyer et récupérer les modifications d’un projet distant en local3.
Git crée automatiquement un raccourcis vers l’URL du projet lorsqu’on clone un projet, qui s’appelle “origin” par défaut (on peut le nommer autrement).

git clone http://github.com/username/myproject.gitCréer un répertoire myproject et télécharge le contenu du projet dedans
git clone http://github.com/username/myproject.git feature...en nommant le raccourci "feature"
git clone -b 00_start http://github.com/username/myproject.gitRécupère la branche 00_start plutôt que master

Pour télécharger toutes les branches :

mkdir projectname
cd projectname

git clone --mirror http://github.com/username/projectname/file.git .git
git config --bool core.bare false
git reset --hard

Pour supprimer le versioning du projet téléchargé (par exemple pour l’utiliser comme template) :

rm -drf .git

remote

git remote permet de gérer les raccourcis d’URL pour les projets distants.

git remote -v Liste les raccourcis d'URL connus pour le projet en cours
git remote add origin Crée un raccourcis "origin" pour l'URL "http://github.com/username/myproject.git"
git remote rm name Supprime le raccourcis "name"

fetch

git fetch récupère les modifications du projet distant. Les modifications sont importées dans des branches temporaires, ce qui donne la possibilité de comparer les différences et si besoin de les importer dans la branche normale.
Permet par exemple de vérifier si de nouvelles branches ont été ajoutées.

git fetchRécupère les modifications du dépot distant sans mettre à jour les fichiers locaux (branche temporaire)
git fetch origin master...de la branche master uniquement

Pour voir les différences entre la branche master temporaire et la branche locale :

git log master..origin/master

Pour récupérer les modifications :

git checkout master
git merge origin/master

pull

git pull permet de récupérer les modifications du projet distant en local en une fois (git pull = git fetch + git merge). Il est déconseillé d’utiliser un pull tant qu’il y a des modifications locales non commitées.

git pullRécupère les modifications du dépot distant dans la copie locale
git pull origin master...de la branche master uniquement
git pull --rebaseRécupère les modifications du dépot distant en utilisant un rebase

Résoudre un conflit

Si lors d’un git pull, utilisé avant git commit, des modifications locales entrent en conflit avec les modifications distantes (on essaie de modifier une même ligne) alors les deux versions sont gardées dans le fichier avec le format suivant :

<<<<<<< HEAD
the cake is a lie.                  # Version locale
=======
the cake is telling the truth!      # Version distante
>>>>>>>
4e7d3542...

Il est nécessaire d’éditer le fichier en conflit puis de commiter pour marquer le conflit comme résolu.

git pull
vim index.html            # Editer le conflit
git commit -am "message"  # Commiter les fichiers modifiés

push

git push permet d’envoyer les modifications du projet local sur le serveur distant.

git push -u origin masterEnvoie les modifications sur la branche master du serveur origin (et garde cet emplacement en mémoire)
git push... au dernier emplacement enregistré (1)
git push heroku-staging staging:master... de la branche locale staging à la branche distante master du serveur heroku-staging

(1) Il est donc nécessaire d’utiliser git push -u remote branch au moins une fois

Mettre en cache son mot de passe


README.md

Le fichier README.md est un simple fichier Markdown situé à la racine du projet, qui est affiché par Github lorsqu’on accède au dépot distant via github.com. Il doit contenir une courte introduction/résumé qui explique le projet.

La syntaxe utilisée est Github Flavored Markdown.


Utiliser des branches

Les fichiers sont commités ensemble lorsqu’ils sont fonctionnels et peuvent faire l’objet d’une nouvelle version (images, css, js, code, etc). Cela peut poser problème lorsqu’on travaille sur plusieurs fonctionnalités en même temps, pour retrouver quels fichiers devraient être commités ensemble. Il est également difficile de créer des “savepoints” pour revenir en arrière si nécessaire, lorsqu’on travaille sur de grosses modifications. Utiliser des branches permet de résoudre ces problèmes.


branch

Une branche est créée à partir d’une branche d’origine et contient tous les fichiers et l’historique de la branche d’origine jusqu’à ce point. Une fois créée, les fichiers et l’historique de cette nouvelle branche peuvent être modifiés de manière indépendente des autres branches.

git branch permet de lister les branches ou d’en créer, renommer, supprimer.
Il existe au minimum une branche, la branche par défaut, nommée “master”.

git branchListe les branches locales (1)
git branch -rListe les branches distantes (2)
git branch -aListe toutes les branches
git branch app01Crée branche app01 à partir de la branche en cours (sans changer la branche active)
git branch -m v1 app01Renomme la branche v1 en app01
git branch -d alternateSupprime la branche locale app01 (3)
git branch -D alternateSupprime la branche locale app01 même s'il y a des changements non commités

(1) L’étoile à gauche indique la branche locale.
  Les branches non commitées sont entourées de parenthèses

$ git branch

    * (HEAD detached from bbd70e01)
      master

(2) Ne requête pas le serveur pour vérifier les branches.
  Pour voir les nouvelles branches, il faut d’abord utiliser un git fetch ou git pull.
  Sinon, utiliser git remote show origin

(3) Pour supprimer la branche distante et non la branche locale: git push origin :app01
  Si on essaie d’utiliser une branche supprimée sur le serveur distant, une erreur est retournée (stale).
  Pour supprimer en local les branches supprimées sur le serveur distant, utiliser git remote prune origin


checkout

git checkout permet de changer de branche active.
En changeant de branche active, les fichiers du répertoire sont mis à jour/ajoutés/supprimés pour refléter l’état de la branche. Il faut donc d’abord commiter ses modifications avant de changer de branche (ou utiliser le stashing).

git checkout app01Passe la branche actuelle à app01 (la télécharge si elle n'est pas disponible localement)
git checkout -b adminCrée la branche admin et se place sur cette branche
git checkout -b 02_01 origin/02_01Télécharge la branche origin/02_01 comme 02_01 et se place sur cette brancge

merge

git merge mybranch prend les changements de mybranch et les apporte à la branche courante.
Crée un “merge commit” dans la branche courante.
Elle est utilisée après un git fetch pour inclure les changements de la branche temporaire dans la copie locale du projet.

git merge app01Merge la branche app01 dans la branche courante

rebase

git rebase permet d’appliquer les modifications d’une autre branche sur la branche locale. Avec merge, on aurait un seul commit (message du commit: “merge my_branch into master”). Avec rebase, on récupère la liste de commits :

  1. Tous les changements dans master (branche cible) qui ne sont pas dans origin/master (branche importée) sont mis dans une zone temporaire
  2. Tous les commits de origin/master sont importés
  3. Tous les commits dans la zone temporaire sont importés, un par un
git rebase masterApporte les commits de la branche courante à la branche master
git rebase --continueReprend le rebase après que le conflit ait été rectifié
git rebase --abortAnnule le rebase (en cas de conflit), revient à l'état du dépot avant la tentative de rebase

Importer les modifications distantes sans avoir à créer un nouveau commit :

git fetch
git rebase

# Pour résoudre un conflit
vim index.html
git add index.html
git rebase --continue

Appliquer les modifications de mybranch dans master, puis supprimer mybranch :

git checkout mybranch
git rebase master
git branch -b mybranch
git push origin :mybranch

rebase -i

Un rebase interractif peut être utilisé pour modifier l’historique.
Cela permet de nettoyer l’historique avant de l’importer dans une autre branche.
Ne JAMAIS utiliser un rebase interractif sur une branche partagée, où d’autres personnes peuvent faire des modifications.

git rebase -iDémarre un rebase interractif sur la branche en cours (1)

(1) Affiche un éditeur pour décider ce qu’il faut faire sur les commits (du plus vieux au plus récent).

Pour envoyer les modifications d’historique au serveur distant avec un rebase, il faut utiliser git push -f.
Un force oblige le dépot distant de se calquer sur le dépot local, sans essayer de faire de merge. Cette manipulation est dangereuse puisqu’elle peut effacer des commits de l’historique et donc supprimer de manière irrécupérable des modifications sur le dépot distant.

Changer l’ordre des lignes pour réordonner les commits :

pick 1ee9572 Switch this one
pick 746ef3e And this one

Renommer le message du commit (un nouvel éditeur va s’afficher pour modifier le message) :

reword 9afe987 The old message

Fractionner un commit en 2 commits ou plus :

edit 39b23ce The message
# Save and close
# Go back to that commit
git reset HEAD^

# Commit the files of the first commit
git add index.html
git commit -m "First commit"

# And then of the second
git add contact.html
git commit -m "Second commit"

# Continue the rebase
git rebase --continue

Rassembler plusieurs commits en un seul (un nouvel éditeur va s’afficher pour éditer le message du commit) :

pick 6e8e5d6 First commit
squash e8005f4 Second Commit
# Will merge the second commit into the first

cherry-pick

git cherry-pick permet de récupérer un ou des commits particuliers d’une autre branche dans la branche actuelle. Cette commande est utile pour récupérer une correction de bug par exemple.

git cherry-pick 5321e5 Récupére le commit 5321e5 dans la branche courante
5321e5 est le SHA du commit dans la branche d'origine
git cherry-pick -x 5321... ajoute le SHA du commit source au message du commit
git cherry-pick --signoff 5321... ajoute le nom de l'utilisateur courant au message du commit
git cherry-pick --edit 5312... ouvre un éditeur pour modifier le message du commit
git cherry-pick --no-commit 5321 55aeRécupère les changements mais ne commite pas
Par exemple pour commiter 2 commits en un seul, ou faire quelques modifications

Utiliser des tags

Un tag est une référence à un commit. Il permet de revenir à l’état du code tel qu’il était au moment où le tag a été crée. On les utilise principalement pour créer des releases, autrement dit pour marquer chaque mise en production d’un numéro de version.
Une bonne pratique est de respecter la convention des versions sémantiques: v[majeur].[mineur].[patch]

Il existe 3 types de tags

tag

git tagListe tous les tags
git checkout v0.0.1Récupère le commit du tag v0.0.1
git tag -a v0.0.3 -m "version 0.0.3"Crée un tag annoté
Si -m est omis, un éditeur va s'ouvrir pour éditer le message
git push --tagsEnvoie les tags au dépot distant
git tag -a v0.0.3 -m "Version 0.0.3" 
git push --tags

Branche vs tag

On peut utiliser les branches pour créer des releases, sur le même principe qu’un tag. La différence qui les sépare est qu’un tag désigne un commit spécifique tandis qu’une branche peut être mise à jour avec de nouveaux commits (pour rectifier des bugs par exemple). En général, on nomme les branches de release r[majeur].[mineur].[patch]

Il n’est pas nécessaire de créer une branche si la version crée n’a pas de support long-terme.
Un autre possibilité en cas de bug est de créer une branche temporaire pour patcher le tag :

git checkout v1.1
git checkout -b rb1.1
# hotfix
git -m "Hotfix"
git tag v1.1.1 -m "Hotfix"
git checkout master
git merge rb1.1 -m "Merged hotfix"
git branch -d rb1.1

Stashing

Le stash est un cache temporaire où l’on peut placer ses modifications. Les modifications sont supprimées du dépot local et peuvent être restaurées ultérieurement. Cela permet de changer de branche quand bien même les modifications ne sont pas prêtes à être commitées.

Tous les fichiers sont placés dans le stash, qu’ils soient dans le staging area ou non, mais par défaut, les nouveaux fichiers fichiers ne sont pas sauvegardés.

Plusieurs caches peuvent être utilisés.

stash

git stashSauvegarde les modifications en cache et restaure le dernier commit
git stash saveIdem
git stash save "Le message" Sauvegarde avec un message
git stash save --keep-indexNe sauvegarde pas les fichiers dans le staging area
git stash save --include-untrackedSauvegarde aussi les nouveaux fichiers
git stash listListe les caches
git stash list --statListe les caches et leurs modifications
git stash showListe les modifications du dernier cache
git stash show stash@{0}Idem
git stash applyRestaure le dernier cache
git stash apply stash@{0}Idem
git stash apply stash@{1}Restaure stash1
git stash dropSupprime le dernier cache (1)
git stash drop stash@{1}Supprime stash1
git stash popRestaure le dernier cache et le supprime (2)
git stash branch mybranch stash@{0}Crée une nouvelle branche et supprime le cache (3)
git stash clearSupprime tous les caches

(1) Après avoir restauré le cache avec apply, le cache n’est pas supprimé - il faut donc le faire manuellement
(2) Raccourcis de git stash apply; git stash drop. En cas de conflit, il faudra supprimer le cache manuellement
(3) Il est ensuite nécessaire de commiter (git commit -am "Add my_branch")

$ git stash list

    stash@{0}: WIP on master: 686b55d Add wolves.
    stash@{1}: WIP on gerbils: b2bdead Add dogs.

WIP est l’abréviation de Work In Progress.


Sous-modules

Les sous-modules sont des dépots à l’intérieur du dépot, utilisé principalement pour les librairies.
Cela permet

submodule

git submodule add git@example.com:css.gitAjoute un sous-module au dépot courant
git submodule initRécupère le contenu du sous-module
git submodule updateRécupère les modifications du sous-module (1)
git push --recurse-submodules=on-demandPush les commits du parent ainsi que des sous-modules si nécessaire (2)
git push --recurse-submodules=checkPush les commits du parent sauf si des sous-modules n'ont pas été pushés

(1) Une erreur “fatal : reference is not a tree” est levée si le parent a des modifications du sous-module non pushées
(2) Pour créer un alias git pushall : git config alias.pushall "push --recurse-submodules=on-demand"

Ajouter un sous-module :

git submodule add git@example.com:css.git
git commit -m "Add my submodule"
git push

Modifier un sous-module :

Un sous-module ne commence sur aucune branche en particulier, il est donc nécessaire de faire un checkout en premier

cd css
git checkout master

# Commite les modifications du sous-module
git commit -am "Init submodule"
git push

# Commite le parent
cd ..
git add css
git commit -m "Update submodule"
git push

En cas d’oubli de checkout sur une branche :

git checkout master
git merge b6bb78f
git push
# Puis commiter le parent

.gitmodules

Fichiers de configurations des sous-modules


Ressources :

Pour aller plus loin : Github