logo git

Cet article a pour première vocation d'être un aide-mémoire sur les commandes git que j'utilise et où trouver plus d'informations. Je l'écris au fur et à mesure de ma découverte de git…donc avec une approche (de moins en moins avec les années) de néophyte… dans tous les cas, j'essaie de rester le plus concret possible!

Il n'est donc pas exclu qu'il y ait des erreurs ou des commandes pouvant être mieux réalisées autrement! Avec le temps, je m'approprie ce puissant outil et détaille aussi des utilisations pour lesquelles l'aide est beaucoup plus rare comme les "detached head"…

Je vous conseilles dans tous les cas de réaliser des tests avec un faux projet minimaliste.

Liens pour en savoir plus, d'autres liens peuvent se trouver dans les paragraphes :

http://git-scm.com/book/fr/v2 (en français)

http://djibril.developpez.com/tutoriels/conception/pro-git/?page=page_4#LIV

http://www.miximum.fr/enfin-comprendre-git.html

À voir, animation des principales actions: https://onlywei.github.io/explain-git-with-d3

et un autre (en français): https://learngitbranching.js.org/

et encore un autre (en): https://agripongit.vincenttunru.com/

voir aussi pour utilisation sous windows : https://tortoisegit.org/

pour les workflows: https://danielkummer.github.io/git-flow-cheatsheet/index.fr_FR.html

Après avoir installé git, configurons-le :

nota : sous windows, il faut ajouter le chemin du répertoire bin de git dans la variable d'environnement Path…;C:\Program Files (x86)\Git\cmd

après installation, il faut créer le fichier .gitconfig en tapant :

$ git config --global user.name "prénom nom" (ou votre alias de programmeur)
$ git config --global user.email Cette adresse e-mail est protégée contre les robots spammeurs. Vous devez activer le JavaScript pour la visualiser. 

Vous pouvez ensuite éditer le fichier .gitconfig qui se trouve dans votre home (ou sous windows dans \Utilisateurs\nom_utilisateur)

il faut impérativement définir le user (si ce n'est déjà fait… cf. ci-dessus)

[user] 
    email = Cette adresse e-mail est protégée contre les robots spammeurs. Vous devez activer le JavaScript pour la visualiser.
    name = nom du programmeur 
[includeIf "gitdir:~/repertoire2travail-pro/*"]
path = .gitconfig-w # cf. https://stackoverflow.com/questions/8801729/is-it-possible-to-have-different-git-configuration-for-different-projects/54125961#54125961 [alias] 
    ci = commit 
ca = commit -a # si besoin lancer l'éditeur pour écrire un texte log avec une ligne (50 car max) et des explications détaillées
cam = commit -am
    co = checkout 
# ci-dessous alias pour log. L'option --format peut être utilisé pour formater afin d'enregistrer dans un fichier pour utiliser avec un autre logiciel… import dans une page web par exemple.
# solution 1 --> * 211e4d5 (tag: v2.05) ajout debug pr traiter pbm regeneration
l = log --oneline --graph --decorate
# solution 2 --> * 211e4d5 2015-07-29 (tag: refs/tags/v2.05)-ajout debug pr traiter pbm regeneration bcg2
lf = log --format="%C(auto)%h%' 'ad%' 'd-%s%' 'Cblue%' 'an" --graph --date=short
pso = push origin
    st = status
stu = status -uno # permet de ne pas afficher les 'untracked files'
    br = branch
bd = branch -d
d = difftool
t = tag
[log]
    decorate = full
[push]
    followTags = true

Les alias quant à eux permettent de raccourcir les commandes, exemple :  git st  au lieu de  git status  j'utiliserai ces alias dans le reste de l'article.

Pour le log.decorate, cela permet de voir les tags lors d'un git log

un exemple très complet de .gitconfig : https://gist.github.com/pksunkara/988716

et sur la page détaillant les options : https://www.kernel.org/pub/software/scm/git/docs/git-config.html

Premiers pas, créer son premier dépôt avec git :

pour créer le dépôt : se positionner dans le répertoire du projet puis

 git init 

ajouter les sources :

 git add repertoire/*.cpp repertoire/*.h 

ATTENTION : éviter le git add . ou similaire qui risque de versionner des fichiers qu'il vaut mieux ne pas versionner comme des dépendances, des fichiers projets, des fichies de cache…

Si on veut savoir quels sont les fichiers suivis:

 git ls-files

Pour ignorer les fichiers à ne pas prendre en compte : il faut créer un fichier .gitignore à la racine du dépôt,

pour cela, après avoit ajouter tous les fichier à prendre en compte puis fait le premier commit, j'ai fait un 

git status > .gitignore  puis j'ai modifier le contenu (il est heureux de remplacer une liste de fichier par un *.typ de fichier si cela est applicable)

commiter  - càd définir un point de sauvegarde, de retauration, de version en local :

pour commiter l'ensemble des fichiers… modifiés

 git ci -a -m "mon 1er commit"  (si vous n'avez pas définis les alias dans .gitconfig, ci est à remplacer par commit)

pour ne commiter que un ou plusieurs fichiers… cf. mon article sur l'index de Git :

 git ci -m "mon 2nd commit" fichier1.cpp fichier2.* 

nota : si vous ne préciser par le message, l'éditeur pris en compte par git (vim?) s'ouvre et vous demande de préciser votre commentaire. Le texte du commit ou «message de validation» contient souvent une simple phrase qui doit être courte (<50 caractères)… mais il est très intéressant d'ajouter aussi des explications détaillées qu'on sépare par une ligne vide. Il faut alors lancer un git commit -a ou git ca qui va lancer l'éditeur pour saisir le texte du commit. Voir aussi les Conventional Commits. Si vous utilisez un dépôt gitlab, il est aussi possible de spécifier le ticket (issue) associé, simplement en précisant #xxx ou xxx est le n° de ticket. Si en lien avec le ticket d’un autre projet, nom-du-projet#xxx. Une ligne sera alors automatiquement ajouté au ticket avec un lien vers le commit !

Si vous voulez modifier votre dernier commentaire de commit :

 git ci --amend 

optionnel mais souvent indispensable : ajouter des tags à votre commit :

git tag v2.04 b49f8

cela ajoute le tag "v2.04" au commit dont le code SHA commence par b49f8. Pour ajouter un tag à la version qui vient d'être commité, il n'est pas nécessaire de spécifier le code SHA.

Si on s'est trompé, il est possible de supprimer le tag :

git tag -d v2.04 il m'est arrivé ainsi de "déplacer" un tag en le supprimant et le créant à nouveau sur mon dernier commit qui apportait la petite correction oubliée.

Pour pousser sur le serveur (pour la branche en cours):

le tag:

git push origin v2.04

le(s) tags:

git push origin --tag

Retirer un fichier du dépôt, --cached permet de ne pas le supprimer !

 git rm -f --cached fichier 

pour voir la liste des commits : git log --decorate

--decorate permet de voir les tags (entre autre)

autre commande intéressante : git reflog --decorate liste les commit avec les 7 premiers caractères du code SHA du commit. Bien plus synthétique, notamment une fois qu'on a un certain nombre de commit ! Cf. le fichier .gitconfig

Une fois un grand nombre de commit (ce qui est fortement conseillé!), limité le log aux 10 ou 20 derniers peut-être utile:

git l -10

Prise en compte d'un fichier renommé :

git rm ancien_nom

git add nouveau_nom

Alternative, renommer le fichier avec la commande git suivante :

git mv ancien_nom nouveau_nom 

Je continue mon développement… et je veux retourner en arrière avant d'avoir commité :

git reset --hard HEAD

 

de la même façon, si on a ajouté un fichier qui ne doit pas être versionné (notamment si on utilise des caractères génériques wildcard), il suffit de taper

git reset nom-du-fichier

J'aime les arbres ! allez parlons de branches :

Je viens de commencer des modifications pour une nouvelle fonctionnalité ou une évolution importante d'une fonctionnalité existante :

git br nom_branche

attention, on pointe toujours sur master c'est à dire la branche principale !

pour basculer sur ma nouvelle branche :

git co nom_branche

on peut vérifier en faisant un git st dont le retour doit commencer par On branch nom_branche

Les 2 étapes git branche et git checkout peuvent se simplifier en une seule :

git co -b nom_branche

Lors de bascule entre branche, il faut faire un commit avant la bascule pour "mémoriser", ou un "remisage", cf. § la remise (stash) plus bas

si on refait un test sur la master sans modif, il peut être génant de mettre dans le suivi le fichier du projet… pour le supprimer du suivi (sans le supprimer réellement !)

git rm --cached projet.ide

…quelques "commit" plus loin, on "merge" :

se placer tout d'abord sur le master :

git co master

puis lancer le "merge" :

git merge nom_branche

et enfin effacer la "vielle branche" :

git br -d nom_branche

… et si on ne l'efface pas…. attention aux branches mortes ! pour connaître les branches existantes et mieux encore, connaître leur dernier commit :

git br -vv

et pour avoir les dates (relative ou local) … "ça va vieille branche !" :

git reflog --date=relative | grep <nom-branche>

Il peut-être intéressant de créer une branche à partir d'une branche, par exemple, créer une branche developpement sur laquelle on crée des branches pour des développements spécifiques. Il suffit pour cela de créer la "sous-branche" en étant sur la branche de base. Si on part de la branche master, cela donne :

git br -b developpement

puis

git br -b dev_specifique

Comment connaîre le rattachement d'une sous-branche?

si l'on va sur notre branche developpement puis que l'on tape :

git br --contains on visualise la liste des sous-branches.

Pour déplacer le pointer correspondant à une branche (exemple: master modifier et branche develop à la traîne!)

git br -f develop 03154b3

puis pour mettre le dépôt distant à jour:

git push origin develop

Nota: quand on vient de cloner, un

git br n'affiche que la branche master

git br -a affiche toutes les branches !

Pour renommer la branche active :

git br -m nouveau_nom

Comparer les branches et les fichiers avec git diff et difftool

Il est souvent utile de comparer le(s) fichier(s) sur le(s)quel(s) on travaille et ceux du master ou d'une autre branche.

git diff master permet de comparer la branche active avec le master… et même si c'est assez explicite, je trouve que c'est là que la ligne de commande trouve sa limite ! … sous windows, on peut utiliser TortoiseGit, sous linux, j'utilise vimdiff associé à git difftool. Pour cela, j'ai ajouté d = difftool dans .gitconfig.

Dans une même branche, pour comparer deux versions d'un même fichier avec plusieurs commit d'écart, 3 dans l'exemple suivant:

git d HEAD~3 HEAD -- repertoire/fichier.cpp

voir aussi mon § Voir une ancienne version d'un fichier

Oubliez de créer une branche, il n'est pas trop tard

À qui n'arrive-t-il pas de réaliser des modif dans un code et les modifications sont plus délicates que prévu… et on se dit qu'on aurais dû créer une branche.

Et bien c'est pas grave, surtout si on n'a pas encore commité, il suffit de créer la branche et de commiter !

Comment bien travailler avec les branches?

Il est important de définir une stratégie d'utilisation des branches. Elle doit être notamment adapté en fonction de l'utilisation pour un développement seul, à deux ou trois ou répartis sur plusieurs équipes!

Je vous conseille la lecture de l'article suivant :

http://nvie.com/posts/a-successful-git-branching-model/

et un document qui récapitule (en français) le lien ci-dessus et d'autres méthodologies d'utilisation des branches avec git:

https://aresu.dsi.cnrs.fr/IMG/pdf/Guide_d_utilisation_Git_-_branchements.pdf

J'en retiens, pour utilisation solitaire ou à quelques uns, sur des petits projets, une structure simplifiée :

  1. La branche master, évidemment pour les versions en production
  2. La branche develop qui constitue le tronc des développements duquel partent les branches de développement de fonctionnalités et sur lequel ils se fusionnent.
  3. Les branches de fonctionnalités dont la durée de vie est limité à ces développements spécifiques: feature/nom_feature1, feature/nom_feature2

Il est important d'avoir en tête qu'une branche n'est guère qu'un pointeur sur un commit !

Fusion avec git merge --no-ff

Il vaut mieux trop que pas assez de commit. Ceci dit, si on récupère dans la branche de développement ou master tous les commits, ça devient vite illisible.

La commande --no-ff oblige à créer un nouvel objet commit au lieu du "fast forward" "avance rapide" qui est par défaut... et ainsi de ne pas conserver les commits intermédiaires.

git merge --no-ff

…mais il vaut mieux un bon shéma qu'un long discours (extrait de https://nvie.com/posts/a-successful-git-branching-model/) :

merge without ff

voir aussi les workflows pour simplifier les questions ci-dessus:

https://danielkummer.github.io/git-flow-cheatsheet/index.fr_FR.html

Voir/Récupérer une ancienne version d'un fichier/répertoire :

Pour commencer, on peut vouloir connaître les fichiers qui ont été versionné par un commit:

git diff-tree --no-commit-id --name-only -r bd61ad98

ou

git show --pretty="" --name-only bd61ad98

Dès qu'on a compris que git modifie les fichiers du répertoire courant… on a l'impression de ne plus avoir accès aux anciennes versions des fichiers. Bien au contraire, l'accès est assez simple :

git show HEAD~3:repertoire/fichier.cpp

mais si on veut voir la version correspondante à un commit? C'est presque plus simple encore, au lieu de se référencé à HEAD, on se référencie au code SHA court ou mieux encore au tag associé… le numéro de version par exemple :

git show v2.0.18:repertoire/fichier.cpp ou git show a79cbd6:appareil/balance.cpp sont équivalents.

Et il suffit d'ajouter une redirection pour faire une copie dans un fichier sous un autre nom:

git show v2.0.18:repertoire/fichier.cpp > repertoire/fichier_v2018.cpp et il ne reste plus qu'à l'éditer avec votre éditeur préféré !-)

Et si on veut récupérer un fichier ou mieux encore un répertoire :

git co f9b95222 src/DataFixtures/ a récupérer le répertoire DataFixtures et son contenu (sous répertoire inclus)

Récupérer le fichier d'une autre branche :

J'ai une sous-branche (nommons-la new_feature) de develop où il y a de nombreuses modif. Entre temps, je suis revenu sur develop pour une petite correction. Je reviens sur ma sous-branche new_feature et lors de tests, j'aimerais disposer de la correction faite dans develop sans faire des commits ou remise, merge… juste récupérer tel ou tel fichier de la branche develop et qu'elle soit disponible dans ma branche new_feature. Depuis new_feature :

git checkout develop -- repertoire/fichier et le tour est joué

Supprimer les modifications d'un fichier :

après avoir attaqué les modifications d'un fichier (dans une branche), on se rend compte qu'on s'est fourvoyé! On réalise un diff (cf. § ci-dessus) et on décide de revenir en arrière Comment annuler les modifications sur un fichier :

 git co -- fichier.cpp ATTENTION les modifications seront perdues, il faut donc être sûr de ce qu'on fait !

Revenir en arrière, sur un commit antérieur

Il arrive qu'on s'aventure dans un chemin où on "s'embourbe" et qu'on préfère finalement revenir en arrière pour reprendre la route ou un autre chemin plus dégagé. Cette analogie de randonneur hors des sentiers battus pour dire que si on fait des commits, c'est bien pour, tôt ou tard, pouvoir revenir dessus. Oui mais commen fait-on? Après plus de deux ans d'utilisation de git, il est temps de se poser la question !-)

La réponse est la commande à tout faire dans git : checkout !

voici une bonne raison pour utiliser les tags :

git co v2.0.6

mais on peut évidemment faire

git co 334233c

Attention, on se retrouve sur une "tête détachée" "detached HEAD" (révolutionnaire, ce git… désolé, c'est un peut trivial!), il peut être utile de définir une branche associée :

git co -b nouvelle_exploration

Lien SO intéressant : https://stackoverflow.com/a/34519716/6614155

Si le retour doit être "définitif", on évitera la "tête détachée" en faisant un git reset --hard 334233c

Recoler la tête (on a des remords, citoyen?)

Si c'était juste pour voir/tester un ancien code de la branche en cours, il suffit ensuite d'exécuter git co ma_branche pour revenir sur le dernier commit de la branche.

J'avais un bug dans la version v2.0.25 que j'avais mis en test sur la machine de prod, et la v2.0.24 n'a pas ce bug mais entre ces 2 versions, j'avais un commit supplémentaire. J'ai donc réalisé un

git co v2.0.24

pour vérifier recompiler cette version et la tester. Elle n'a pas le bug recherché, donc je dois utiliser le fichier source de cette version pour vérifier où j'ai commis une erreur… cf. plus haut §voir une ancienne version d'un fichier

Et là, je fais un

git co v2.0.25 pour revenir … au lieu de faire un git co ma_branche et j'ai ainsi créé une "tête détachée" sur ma branche en cours. Je code ma correction, commit, tag en v2.0.26 et push ma_branche vers le serveur… la commande me retourne donc "Everything up-to-date" car ma branche locale et distance sont bien équivalente ! Un petit git st plus loin, je comprends mon erreur…

Je recole donc les morceaux en lançant :

git co ma_branche puis un git merge v2.0.26 et enfin mon push pour me synchroniser avec le serveur!

Il peut-être intéressant d'appeler git relog -10 pour visualiser les 10 sha des commit liés aux changements de HEAD. On verra dans ce cas le dernier commit, qui ne se voit plus avec git log.

La remise (stash)

Je suis en train de travailler sur ma branche (nom_branche)… et on me demande une modif sur le master (ou je veux voir comme c'était fait avant que je casse tout!). N'ayant pas fini mes modifs, je n'ai pas envie de faire un commit… pas de problème, c'est prévu, il y a la remise :

git stash

cela sauvegarde ma branche en cours

je bascule ensuite sur le master :

git co master

je fais ma modif, mon commit, livre mon patch du master et je veux revenir à mon developpement sur ma branche nom_branche :

git co nom_branche

git stash pop

qui récupère mes modifications non commitées et supprime le remisage (si vous voulez le conserver, on fait git stash apply)!

Si besoin de connaître s'il y a un (ou plusieurs remisage) :

git stash list

d'avantage de détails sur http://djibril.developpez.com/tutoriels/conception/pro-git/?page=page_6#LVI-C

Les sous-modules

J'ai travaillé sur deux projets ayant de nombreux points communs. Je souhaitais donc définir un ou des sous-modules afin d'arrêter d'avoir deux versions de ces parties de codes communes…

 

Hooks (crochets)

Pour versionner son projet, lire mon article Gestion numéro de VERSION avec git hook (crochet)

 

Git sur mon serveur local