Git en 5 min

Git est un gestionnaire de code source initialement créé et développé par Linus Torvarlds. Même si la signication du mot Git est plutôt péjorative, il s’agit très puissant qui propose plus de fonctionnalités que la plupart des autres gestionnaires de code source.

Comparaison avec les autres gestionnaires de code source

Il est rare d’aborder Git sans jamais avoir touché à un autre gestionnaire de code source (i.e. VCS pour Version Control System). Toutefois même on étant aguérri à l’utilisation de logiciels de gestion de source, il est préférable d’aborder Git sans essayer de transposer ses connaissances aux commandes. Par exemple même si certaines commandes sont communes entre SVN et Git, leur fonction est pratiquement toujours très différente.

Il faut donc aborder Git en essayant d’avoir un œil neuf et s’intéresser à la fonction réelle de chaque commande.

Les principales caractéristiques de Git sont:

  • Git est un système de contrôle de version distribué: chaque client qui se connecte à Git possède une copie locale du dépôt distant. Si ce dernier disparait, il peut être restoré à partir d’une des copies des clients.
  • Le stockage des fichiers est différent de la plupart des autres VCS. Au lieu de stocker des copies différentes des fichiers pour chaque version, Git stocke des versions de fichier seulement s’ils sont modifiés. Chaque version du dépôt est un “instantané” contenant un ensemble de fichiers dans une version précise. Il n’y a pas de copies inutiles lorsqu’un fichier n’est pas modifié.
  • La plupart des actions de l’utilisateur sont exécutées localement, ce qui limite la propagation d’erreurs dans le versionnement. Il est facile de revenir en arrière puisque la plupart de ces commandes sont locales et n’affectent pas d’autres utilisateurs.

La plus grosse différence entre Git et les autres VCS, est que le répertoire de travail ne change pas si on passe d’une branche à l’autre.

Par exemple, dans SVN si on souhaite avoir la branche principale (i.e. le trunk) et une autre branche, il faut faire un checkout dans 2 répertoires différents. Avec Git, on travaille toujours dans le même répertoire, quelque soit la branche sur laquelle on travaille. C’est Git qui modifie les fichiers de ce répertoire en fonction de la branche sur laquelle on travaille.

Quelques éléments sont caractéristiques de Git:

  • Clé SHA-1: tous les commits sont identifiés avec une clé SHA-1 unique. Cette clé peut être utilisée pour récupérer un commit particulier.
  • HEAD: il s’agit d’une référence vers le nœud vers lequel pointe le répertoire de travail.
  • Dépôt distant (i.e. remote repository): il s’agit du répertoire de travail distant qui est partagé. Chaque client possède une copie locale de ce répertoire. Les clients ne travaillent pas directement à partir du dépôt distant, ils travaillent sur le dépôt local. Au moment de la livraison d’une modification sur le code source, le développeur envoie ces modifications sur le dépôt distant de façon à partager la modification avec d’autres développeurs.
  • Dépôt local (i.e. local repository): il s’agit de la copie locale du dépôt distant. Le développeur travaille généralement sur cette copie. A la différence des autres VCS, le développeur peut effectuer des commits, modifier ses commits, créer des branches sur son dépôt local. Ces modifications sont locales et ne sont pas visibles des autres développeurs. Au moment de la livraison de son travail, le développeur peut décider d’envoyer une partie de ses modifications de son dépôt local vers le dépôt distant, et ainsi partager son travail.

Installation

Sur windows, on peut installer Git for windows qui propose un bash pour utiliser les commandes Git et quelques outils graphiques accessibles avec le bouton droit de la souris (une fenêtre pour effectuer des commits, une fenêtre pour changer de branche, affichage des logs, etc…).

Git for windows se télécharge sur: https://git-for-windows.github.io/.

Premières étapes

Renseigner quelques paramètres

Il peut être nécessaire de préciser certains paramètres qui seront utilisés par la suite pour l’exécution de chaque commande: nom de l’utilisateur, adresse mail, coloration syntaxique du bash etc…

Pour préciser ces paramètres, on utilise la commande git config:

$ git config --global user.name "Robert Mitchoum" 
$ git config --global user.email robertmitchoum@caramail.com

Ces paramètres sont rajoutés dans le fichier .gitconfig. Pour savoir où se trouve ce fichier, on peut taper:

$ git -c 'user.cmdline=true' config --list --show-origin

On peut lister tous les paramètres qui se trouvent dans ce fichier en faisant:

$ git config --list

Pour indiquer la valeur d’un paramètre:

$ git config user.email 

Initialiser le répertoire de travail (git init)

Comme indiqué plus haut, avec Git, on reste toujours dans le même répertoire quelque soit la branche, le tag ou la version sur laquelle on travaille.

Pour initialiser le répertoire de travail:

$ mkdir working_dir 
$ cd working_dir 
$ git init 
Initialized empty Git repository in /Users/mitchoum/working_dir/.git/ 

A ce stade le répertoire est presque vide. En réalité il contient un répertoire appelé “.git” qui contient les fichiers de travail de Git.

Cloner un dépôt distant (git clone)

Dans le cas où un dépôt distant existe et contient déjà des fichiers versionnés, on peut récupérer le contenu de ce dépôt sur son dépôt local en effectuant une étape de “cloning” (cette étape est l’équivalent du checkout sur SVN). Cette étape effectue seulement une copie du dépôt distant sur le dépôt local.

On se place dans le répertoire de travail (après avoir exécuté git init) et on exécute l’instruction suivante:

$ git clone [adresse du dépôt distant]  

L’adresse du dépôt distant est généralement du type: https://github.com/repository/test.git

Par exemple:

$ git clone https://github.com/repository/test.git 
$ Cloning into 'test'... 
$ remote: Counting objects: 12, done. 
$ remote: Compressing objects: 100% (4/4), done. 
$ remote: Total 12 (delta 1), reused 11 (delta 0), pack-reused 0 
$ Unpacking objects: 100% (12/12), done. 

Afficher l’état du dépôt (git status)

L’état du dépôt est une information importante car elle permet d’indiquer dans quelle branche on se trouve et l’état des fichiers qui s’y trouvent.

Pour afficher l’état, on écrit:

$ git status 
On branch master 
 
Initial commit 
 
nothing to commit (create/copy files and use "git add" to track) 

Effectuer des commits

Quand le dépôt distant est copié localement (cf. git clone), le developeur peut modifier le code source et ensuite commiter ses modifications. A la fin de cette étape, les modifications restent locales et ne sont pas partagées.

Workflow pour effectuer un commit

Le workflow n’est pas tout à fait similaire aux autres VCS. Pour commiter dans Git, il faut effectuer les étapes suivantes:

  • Ajouter les fichiers à commiter: cette étape ajoute les fichiers à versionner mais elle est aussi nécessaire pour livrer une modification. Elle consiste à indexer certains fichiers (i.e. Stage) pour les commiter ensuite dans le HEAD.
  • Commiter les fichiers sur le dépôt local.

Ajouter les fichiers à commiter (git add)

Cette étape est nécessaire pour indiquer les fichiers à versionner mais elle permet aussi d’indiquer quels sont les fichiers à commiter lorsqu’ils sont déjà versionnés. En pratique, cette étape ajoute un fichier dans un “index”. Dans l’arbre Git, un “index” est un espace de transit où les fichiers sont stockés provisoirement avant d’être commités.

git add [chemin du fichier à ajouter] 

Enlever un fichier des fichiers à commiter

Si on a exécuté un git add par mégarde pour un fichier, on peut revenir en arrière en faisant:

git reset HEAD [chemin du fichier] 

Commiter des fichiers “stagés”

A la fin de cette étape, le commit est local et non visible des autres développeurs:

  • git commit pour commiter et ouvrir un éditeur de texte pour indiquer un commentaire.
  • git commit -a pour commiter tous les fichiers se trouvant dans le répertoire (il n’est pas nécessaire de les “stager” avant.
  • git commit -m pour commiter directement avec un message.

Supprimer des fichiers versionnés

Il faut d’abord supprimer le fichier de Git:

git rm [chemin du fichier à supprimer] 

Il faut ensuite commiter pour que la suppression de Git soit effective. Si le fichier a été modifié depuis sa version commitée, pour le supprimer, il faut forcer sa suppresion:

git rm -f [chemin du fichier à supprimer] 

On peut aussi supprimer un fichier parmi les fichiers “stagés” sans le supprimer complétement:

git rm --cached [chemin du fichier] 
Utilisation de vi

Pour chaque commande où il est nécessaire d’ajouter des commentaires (comme git commit ou git rebase) ou pour les commandes affichant beaucoup de lignes (comme git log), le bash ouvre un fenêtre vi. L’utilisation de cet éditeur n’est pas toujours facile.
Quand vi est ouvert, il est en mode commande, il faut indiquer des commandes pour modifier le fichier:

  • Pour passer en mode édition et modifier le fichier: taper "i", effectuer les modifications et taper “Echap” pour sortir du mode édition.
  • Pour passer en mode remplacement: taper "R", effectuer le remplacement et taper “Echap” pour sortir de ce mode.
  • Pour supprimer un ligne: taper "dd". "D" permet de supprimer jusqu’à la fin du fichier.
  • Pour annuler les dernières modifications: taper "u".
  • Pour chercher dans le fichier: taper "/" suivi de la chaîne à chercher. En tapant "n" on peut passer à la chaine suivante.
  • Pour enregistrer les modifications: taper "w".
  • Pour quitter sans enregistrer: taper ":q" si le fichier n’a pas été modifié sinon ":q!".
  • Pour quitter en enregistrant: taper ":wq".
  • Pour faire un copier/coller: taper "Y" permet de copier une ligne dans le tampon. "nY" permet de copie "n" lignes. "P" colle les lignes avant le curseur et "p" colle les lignes après le curseur.

Sur linux-france, on peut avoir une liste plus complête des commandes de vi.

Afficher les modifications d’un fichier avant de commiter (git diff)

Lorsqu’on modifie un fichier versionné et que l’on souhaite vérifier les modifications apportées avant de le “stager” ou de le commiter, on peut utiliser git diff de cette façon:

git diff [chemin du fichier]

Avec la commande précédente, on affichera les modifications pour un fichier n’étant pas encore “stagé”.

On peut aussi comparer les modifications par rapport au HEAD du dépôt local:

git diff HEAD [chemin du fichier]

On peut comparer le contenu de l’index avec le dépôt local:

git diff --cache [chemin du fichier]

Au lieu d’afficher les modifications pour un seul fichier, on peut aussi le faire pour tous les fichiers qui se trouvent dans le répertoire de travail. La plupart du temps, l’affichage pour tous les fichiers n’est pas forcément très lisible. Pour afficher ces modifications, il suffit de ne pas préciser le chemin d’un fichier, par exemple:

git diff

Afficher l’historique (git log)

On peut afficher l’historique en utilisant git log:

$ git log 
[22a0ff3ccccdd9a94f5e0fb59b6307ebee0c8d98] 
Author: Robert Mitchoum 
Date:   Sat Feb 11 19:02:29 2017 +0100 
    2e commit 
 
[24d49ab46717545a0c0f9f94ee83ac0d60808d3f] 
Author: Robert Mitchoum 
Date:   Sat Feb 11 18:57:36 2017 +0100 
    1er commit

Les logs sont affichés avec la clé SHA-1 et le nom de la personne qui a commité.

Par exemple, si on commence avec un dépôt local à jour par rapport au dépôt distant, le graphe se présente de cette façon:

Après 2 commits, le graphe devient:

On peut afficher les 5 derniers commits avec:

$ git log -5

On peut afficher l’historique sous forme de graphe simple:

$ git log --graph --oneline 

On peut complêter l’historique de la branche en cours avec les autres branches:

$ git log --graph –oneline --all 

Envoyer ses modifications vers le dépôt distant

Pour envoyer des modifications effectuées sur le dépôt local vers le dépôt distant, il faut exécuter:

$ git push origin master

origin est le nom du dépôt distant. Il s’agit du nom par défaut généralement utilisé lorsqu’on a qu’un seul dépôt distant.
master est le nom de la branche sur laquelle on travaille. master est l’équivalent du trunk sous SVN.

Pour que Git accepte de pousser les modifications vers le dépôt distant, il faut que le dépôt local soit à jour (avec git pull par exemple).

A la fin de cette étape, les modifications sont partagées et d’autres développeurs peuvent y accéder.

Si le dépôt local ne provient pas d’une copie d’une dépôt distant (cf. Clone), on peut connecter son dépôt local au dépôt distant en faisant:

git remote add origin [nom du serveur]  

Si la branche est nouvelle, on peut indiquer que cette branche est la branche “upstream” c’est-à-dire la branche par défaut lorsqu’on précise pas de paramètres avec git push.
Pour indiquer que la branche est la branche “upstream”, on exécute:

git push --set-upstream origin [nom de la branche] 

Après il suffit d’exécuter sans argument la commande suivante pour pousser dans la bonne branche:

git push 

Dans le cas où le message suivant s’affiche quand on essaie de pousser:

fatal: The current branch master has no upstream branch. 
To push the current branch and set the remote as upstream, use 
   git push --set-upstream origin master

On est pas obligé de paramétrer la branche en tant que branche “upstream”. On peut aussi pousser en indiquant dans quelle branche:

git push origin master

Dans l’exemple précédent, si on pousse 2 modifications sur le dépôt distant, le graphe devient:

Branche

Les branches sous Git sont similaires aux branches dans les autres VCS. Toutefois il est courant et plus pratique d’utiliser des branches sous Git car:

  • Elles ne sont pas coûteuses, seules les modifications sont sauvegardées, il n’y a pas de copie complète de tous les fichiers du projet.
  • D’autres part, on peut facilement travailler sur une branche localement sans livrer son travail sur une branche dans le dépôt distant. Les branches locales permettent de travailler sur des sujets différents en même temps (par exemple corrections de bugs ou implémentations de nouvelles fonctionnalités) et de livrer sur la branche principale ou une autre branche sur le dépôt distant.
  • Enfin on peut facilement modifier ces commits sur sa branche locale, les supprimer ou en modifier les commentaires (avec commit amend ou rebase).

Créer une branche et passer directement dessus:

git checkout -b [nom de la branche]

Répercuter la création de la branche sur le dépôt distant (le cas échéant):

git push origin [nom de la branche]  

Passer sur une branche lorsqu’elle est déjà créée:

git checkout [nom de la branche]  

Créer une branche sans passer dessus:

git branch [nom de la branche]  

Lister les branches locales:

git branch --list

Lister les branches sur le dépôt distant:

git branch --list -r

Lister toutes les branches (locales et sur le dépôt distant):

git branch --list -a

Si on crée la branche “new_branch” sur le dépôt local, le graphe est:

Après 2 commits dans la branche “new_branch”, le graphe devient:

Si on retourne dans la branche principale (cf. master) et qu’on effectue 2 commits dans la branche principale:

Supprimer une branche

D’abord supprimer la branche localement en faisant:

git branch --delete [nom de la branche]  

Ensuite pour pousser la suppression sur le dépôt distant (le cas échéant):

git push origin :[nom de la branche]  

Renommer une branche

D’abord localement:

git branch -m [nom existant de la branche] [nouveau nom de la branche]  

Pousser le renommage sur le dépôt distant:

git push origin :[ancien nom de la branche]  

Effectuer des merges

Mettre à jour le dépôt local (git pull)

La façon la plus simple de rapatrier localement les modifications se trouvant sur le dépôt distant est d’exécuter:

git pull
PULL = FETCH + MERGE

Cette étape ne récupére pas seulement les modifications sur dépôt distant, elle effectue un merge des branches distantes avec les branches locales. Ces merges modifient les fichiers locaux et peuvent mener à des conflits. Avant de pouvoir commiter, il faudra résoudre ces conflits.

Une méthode plus progressive pour mettre à jour le dépôt local est d’effectuer:

git fetch

Cette commande récupére seulement les modifications distantes sans effectuer de merge.

Après un git fetch, on peut soit merger en exécutant git merge. On peut aussi utiliser git rebase qui une commande plus sure pour éviter les conflits.

Merger 2 branches (git merge)

Pour fusionner 2 branches, on peut les merger comme avec les autres VCS (ce n’est pas la seule méthode, on peut aussi utiliser rebase qui est une méthode provoquant moins d’erreurs et moins de conflits).

Pour merger 2 branches:
Il faut se placer sur la branche de destination (avec git checkout) puis exécuter:

git merge [nom de la branche à merger]

Des conflits peuvent résulter de ce merge, il faut les résoudre avant de commiter. Les conflits sont indiquées dans les fichiers avec des chevrons comme pour les autres VCS. Pour résoudre les conflits, il faut supprimer les chevrons avec un éditeur de texte en indiquant la partie du fichier à conserver.
Après résolution du conflit, on peut ajouter le fichier à l’index pour le commiter en faisant:

git add [nom du fichier]  

Dans l’exemple précédent, si on merge la branche principale (cf. master) avec la branche “new_branch”:

Tags

Les tags sont aussi très similaires aux tags des autres VCS. Ils permettent d’apporter un nom à un commit particulier. Il faut avoir en tête que tous les commits dans Git sont identifiés avec une clé SHA-1.

Il existe 2 types de tag:

  • Les tags légers (i.e. lightweight tag): ce sont des tags censés être provisoires. Ils ne contiennent qu’une clé SHA-1 vers un commit.
  • Les tags annotés (i.e. annotated tag): ils contiennent un SHA-1 comme les tags légers et aussi un objet qui est une copie de ce qui est taggué.

Tags légers

Créer un tag léger (i.e. lightweight tag):

git tag [nom du tag] [clé SHA-1 du commit à tagguer]

Pour tagguer le dernier commit:

git tag [nom du tag] -1

Pour “pousser” le tag créé vers le dépôt distant:

git push origin –tags

Supprimer un tag local:

git tag –d [nom du tag] 

Supprimer un tag distant (après l’avoir supprimé localement):

git push origin :refs/tags/[nom du tag]

Liste les tags existants:

git tag --list 

Récupérer un tag se trouvant sur le dépôt distant:

git checkout tags/[nom du tag] 

Récupérer un tag et créer une branche (locale):

git checkout tags/[nom du tag] -b [nom de la branche]

Tags annotés

Créer un tag annoté (i.e. Annotated tag):

git tag –a [nom du tag] -m [commentaire sur le tag] 

Pour “pousser” le tag annoté vers le dépôt distant:

git push origin [nom du tag] 

Voir le détail d’un tag:

git show [nom du tag] 

Pour récupérer un tag annoté se trouvant sur dépôt distant, la syntaxe est la même que pour les tags légers:

git checkout tags/[nom du tag]

Rebase

git rebase est une commande très puissante capable d’effectuer de nombreuses opérations sur le dépôt local:

  • Modifier des commits
  • Réunir 2 branches

Le grand intérêt de git rebase est de faire en sorte d’avoir un historique plus linéaire et d’éviter de laisser apparaître des commits inutils qui n’apportent pas d’informations.

Modifier des commits

Modifier des commits localement

git rebase peut être utilisé d’abord pour modifier facilement des commits dans son dépôt local. L’intérêt est de ne laisser apparaître que les commits utiles.

Par exemple, si on effectue un premier commit dans son dépôt local modifiant un fichier et qu’on se rende compte d’une erreur avant d’effectuer un git push. Les modifications sont encore dans le dépôt local. On corrige cette erreur et on effectue un nouveau commit. Sachant que les 2 modifications concernent la même fonctionnalité, il n’y a pas d’intérêt que les 2 commits apparaissent de façon distincte. Un seul commit suffit.

Ainsi, si on affiche l’historique d’un fichier en affichant les 3 derniers commits:

$ git log -3 
[e70d0a17f93e2609462db5593046ff1b2a7eb738] 
Date:   Sat Feb 11 19:05:05 2017 +0100 
    2e commit 
 
[2f222d1ad01a36ac02a8e9cc9359a81217644f69] 
Date:   Sat Feb 11 19:02:29 2017 +0100 
    1er commit 
 
[24d49ab46717545a0c0f9f94ee83ac0d60808d3f] 
Date:   Sat Feb 11 18:57:36 2017 +0100 
    Modification

Si on veut modifier les 2 derniers commits pour qu’ils n’apparaissent que sur la forme d’un seul commit:

$ git rebase -i HEAD~2 

Cette commande permet de modifier les 2 derniers commits par rapport à la référence (HEAD~2) en utilisant l’invite de commande interactive (avec "-i").
La commande ouvre une fenêtre vi qu’il est possible d’éditer pour écrire des instructions:

pick 2f222d1 Autre modif 
pick e70d0a1 Modif dans le master 
 
# Rebase 24d49ab..e70d0a1 onto 24d49ab (2 commands) 
# 
# Commands: 
# p, pick = use commit 
# r, reword = use commit, but edit the commit message 
# e, edit = use commit, but stop for amending 
# s, squash = use commit, but meld into previous commit 
# f, fixup = like "squash", but discard this commit's log message 
# x, exec = run command (the rest of the line) using shell 
# d, drop = remove commit 
# 
# These lines can be re-ordered; they are executed from top to bottom. 
# 
# If you remove a line here THAT COMMIT WILL BE LOST. 
# 
# However, if you remove everything, the rebase will be aborted. 
# 
# Note that empty commits are commented out
Ordre d’affichage inversé

Les commits sont indiqués du plus vieux au plus récent (l’ordre est inversé).

Les pick sont des commandes à effectuer sur les lignes de commit. D’autres instructions sont possibles:

  • pick: permet de garder le commit inchangé,
  • reword: permet d’indiquer qu’on souhaite modifier le commentaire du commit. Il n’est pas nécessaire de modifier le texte directement dans cette fenêtre. Il faut juste remplacer pick par reword. Après fermeture de la fenêtre vi, une autre fenêtre vi s’ouvrira pour modifier le commentaire.
  • squash: le commit sera regroupé avec le commit précédent. Il n’y aura pas de pertes dans le contenu des fichiers. Au lieu d’avoir 2 commits, il n’en restera qu’un seul. Il n’est pas nécessaire de modifier le texte directement dans cette fenêtre. Il faut juste remplacer pick par squash. Après fermeture de la fenêtre vi, une autre fenêtre vi s’ouvrira pour modifier le commentaire.
  • fixup: même modification que squash sans la prise en compte du commentaire.
  • drop: supprime le commit. Les modifications effectuées par ce commit seront perdues.
  • edit: cette commande permet d’afficher par la suite une invite de commande qui sera appliquée seulement au commit pour lequel on a indiqué edit.

Pour réunir le 2e commit au 1er, on remplace pick par squash. On passe en mode édition dans vi en tapant "i", on replace les termes et on enregistre en tapant ":wq":

pick 2f222d1 1er commit 
squash e70d0a1 2e commit 
 
# Rebase 24d49ab..e70d0a1 onto 24d49ab (2 commands) 
# 
# ...

Un écran suivant propose de modifier le commentaire puis:

[detached HEAD 22a0ff3] 1er commit 
 Date: Sat Feb 11 19:02:29 2017 +0100 
 1 file changed, 2 insertions(+) 
Successfully rebased and updated refs/heads/master.

Si on affiche l’historique:

$ git log -2 
Seulement 2 lignes apparaissent: 
[22a0ff3ccccdd9a94f5e0fb59b6307ebee0c8d98] 
Date:   Sat Feb 11 19:02:29 2017 +0100 
    Autre modif 
    Modif dans le master 
 
[24d49ab46717545a0c0f9f94ee83ac0d60808d3f] 
Date:   Sat Feb 11 18:57:36 2017 +0100 
    Commentaire de test.

Changer l’histoire dans le dépôt distant

Si on modifie l’histoire en utilisant git rebase, on peut pousser les modifications sur le dépôt distant en forçant avec "-f":

$ git push -f

Cette étape peut compliquer les merges futures pour les autres développeurs puisqu’on peut changer l’historique du dépôt distant.

Réunir 2 branches

Branche ayant un nœud commun avec le master

Le terme “réunir” est utilisé içi à la place de merger pour éviter confusion avec git merge. git rebase est complêtement différent de git merge:

  • git merge réunit 2 branches en mergeant les fichiers. Dans l’historique, la branche principale (i.e. branche “master”) et la branche mergée dans la branche principale sont visibles (cf. git merge).
  • git rebase ne merge pas 2 branches mais rejoue les commits de la branche à la suite du dernier commit du “master”. Git cherche alors, le nœud commun entre la branche et le master puis il rejoue les commits à la suite du master à partir de ce nœud. L’historique apparaît linéaire puisque les commits sont simplement ajoutés à ceux du master.

Si on considère le master avec une branche appelée “featureBranch”, on a un graphe de ce type après quelques commits:

Dans un premier temps, on se synchroniser par rapport à la branche master et on rejoue les commits à la fin du master:

$ git checkout featureBranch 
$ git rebase master

Le graphe devient:

Ensuite, on se place dans la branche master:

$ git checkout master

Le graphe est alors:

On merge la branche “featureBranch” dans le master qui ne va rajouter que les nouveaux commits:

$ git merge featureBranch 

Le graphe devient:

Comme on peut le voir, l’historique de la branche “master” reste linéaire et les commits sont ajoutés à la fin des commits du master.
Pour réunir 2 branches, il est préférable d’utiliser git rebase par rapport à git merge pour minimiser les conflits et avoir un historique plus clair.

Branche séparée du master

Dans le cas où la branche est complêtement séparée du master c’est-à-dire qu’il n’y a pas des nœuds commun avec le master. L’utilisation de git rebase est un peu différente car il ne pourra pas trouver de nœud commun avec le master.

Par exemple, si on considère les branches suivantes:

La branche “otherBranch” n’a pas de nœud commun avec le master. “otherBranch” a un nœud commun “featureBranch”.

Pour effectuer un “rebase” sur le master, il faut rajouter l’argument --onto:

 $ git rebase –onto master featureBranch otherBranch 

Le graphe devient:

Il suffit ensuite de merger la branche “otherBranch”:

$ git checkout master

Le graphe est:

Enfin après le merge:

$ git merge otherBranch

Le graphe après le merge:

Si on supprime la branch “featureBranch”, l’historique de la branche “master” est linéaire:

$ git branch –d featureBranch

Le graphe après suppresion de la branche “featureBranch”:

Annuler des modifications

Il est possible d’annuler la plupart des opérations effectuées avec Git.

Avant d’effectuer un commit

Si un fichier a été modifié et qu’on ne désire pas le commiter, on peut annuler les modifications en exécutant:

git checkout -- [chemin du fichier]
Les modifications seront perdues

Après cette étape, les modifications effectuées sur le fichier seront perdues.

On peut aussi ramener un fichierà un commit précédent:

git checkout [SHA-1 du commit] -- [chemin du fichier]

Pour supprimer tous les fichiers qui ne sont pas trackés (i.e. fichiers non versionnés):

git clean -f

Pour annuler, supprimer tous les changements locaux et récupérer les modifications effectuées sur le dépôt distant:

git fetch origin 
git reset --hard origin/master 

Modifier le dernier commit

Il est possible de modifier facilement le dernier commit effectué même dans le cas il a été poussé vers le dépôt distant avec la commande git commit --amend.

Par exemple pour modifier pour le commentaire utilisé pour le commit:

git commit --amend –m [nouveau commentaire du commit]

On peut pousser la modification vers le dépôt distant en faisant:

git push origin master –f 

On peut aussi effectuer cette étape en utilisant git rebase.

On peut utiliser plus généralement git commit –amend par exemple pour rajouter un fichier au dernier commit:

$ git commit -m [commentaire du premier commit] 
$ git add [fichier à rajouter au commit] 
 
$ git commit --amend

La dernière ligne va commiter l’ajout du fichier.

Annuler un commit

On peut annuler un commit, en replaçant le HEAD sur un commit particulier en utilisant la clé SHA-1 de ce commit:

git reset [label ou SHA-1 du commit]

Pour pousser, il faut ajouter l’argument “-f” pour forcer:

git push –f 

Il est préférable que personne n’ait récupéré le dépôt distant avant d’avoir effectué cette étape. Si un développeur a effectué un git pull entre le dernier commit et le git push, il travaillera avec un HEAD qui n’est plus le HEAD du dépôt distant.

Cherry-pick

git cherry-pick est une commande utile pour récupérer un 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. On utilise la clé SHA-1 pour identifier ce commit et on l’applique sur la branche courante. Cette opération peut mener le cas échéant à des conflits.

Pour utiliser git cherry-pick, il suffit de:

  • se placer dans la branche sur laquelle on veut appliquer le commit,
  • il faut avoir le SHA-1 du commit

On exécute ensuite:

$ git cherry-pick [SHA-1 du commit]

Il n’est pas nécessaire d’effectuer un commit après cette étape, git cherry-pick effectue lui-même un commit.

Stash

git stash permet de mettre de coté des fichiers lorsqu’ils sont modifiés mais qu’ils ne sont pas placés dans l’index et qu’ils ne sont pas encore commités. Dans une branche donnée, on va donc mettre ces fichiers de coté de façon à effectuer d’autres modifications. On pourra ensuite récupérer les fichiers mis de coté. L’historique n’est pas affecté lorsqu’on utilise git stash. D’autre part, cette opération s’effectue seulement sur le dépôt local.

Si le dépôt local comprends des fichiers modifiés:

$ git status 
On branch master 
Your branch is up-to-date with 'origin/master'. 
Changes not staged for commit: 
  (use "git add <file>..." to update what will be committed) 
  (use "git checkout -- <file>..." to discard changes in working directory) 
 
modified:   test.txt 
 
no changes added to commit (use "git add" and/or "git commit -a")

Si on met de coté les modifications en “stashant”:

$ git stash 
Saved working directory and index state WIP on master: e9bc55e Merge branch 'master' of https://github.com/repository/test 
HEAD is now at e9bc55e Merge branch 'master' of https://github.com/repository/test 

Le répertoire ne contient plus de modifications:

$ git status 
On branch master 
Your branch is up-to-date with 'origin/master'. 
nothing to commit, working tree clean 

On peut voir la liste des stash en exécutant:

$ git stash list 
 stash@{0}: WIP on master: e9bc55e Merge branch 'master' of https://github.com/repository/test 

Le nom du stash est “stash@{0}”.

On peut appliquer le premier stash sans le supprimer de la liste en exécutant:

$ git stash apply

Pour appliquer un stash particulier sans le supprimer de la liste:

$ git stash apply [nom du stash] 

Pour appliquer le premier stash en le supprimant de la liste, on peut exécuter:

$ git stash pop 

De même pour appliquer un stash particulier en le supprimant de la liste:

$ git stash pop [nom du stash]
Références

Pour s’exercer:

2 responses... add one

Leave a Reply