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 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 : http://code.google.com/p/tortoisegit/

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  [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 
    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 

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

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 le(s) tags sur le serveur (pour la branche en cours):

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

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

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.

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 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.

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 http:/nvie.com) :

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 une ancienne version d'un fichier :

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 :

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é !-)

Supprimer les modifications d'un fichier :

après avoir attaqué les modifications d'un fichier (dans un 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

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

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!

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

Et on monte encore d'un cran : git sur mon serveur local :

Actuellement, on privilégiera plutôt une implémentation de gitlab community editions (ce)

liens utiles pour cette partie :

https://www.sheevaboite.fr/articles/installer-serveur-git-auto-heberge-partie-1

http://git-scm.com/book/fr/v2/Git-sur-le-serveur-Mise-en-place-du-serveur

après avoir commencé à utiliser git sur ma machine, je souhaite maintenant déposer les sources sur un serveur de fichier pour en avoir une copie au chaud… et éventuellement partager ce projet.

sur le serveur (ubuntu, debian like)
sur le(s) client(s) (ubuntu, debian like)

Les lignes ci-dessous s'inspire largement du tutoriel sur sheevaboite.fr (cf. lien ci-dessus):

installation :

sudo apt-get install git-core

création d'un utilisateur spécifique pour l'utilisation de git :
sudo adduser --system --shell /bin/bash --group --disabled-password --home /var/git/ git

Création du répertoire pour le(s) certificat(s):

sudo mkdir /var/git/.ssh

Copie du certificat du developpeur 1 :
sudo scp developpeur1@nom_pc_client:/home/developpeur1/.ssh/id_rsa.pub /var/git/.ssh/

cd /var/git/.ssh/

Création du fichier authorized_keys avec la clé publique du 1er développeur
sudo mv id_rsa.pub authorized_keys

Création d'un dépôt :

cd ~
mkdir depot.git
cd depot.git/
git init --bare

installation

sudo apt-get install git

Génération des clés privée/publique pour l'accès au serveur

ssh-keygen -t rsa

Copie de la clé publique sur le serveur

scp .ssh/id_rsa.pub git@nom_serveur_git:/var/git/.ssh/

Extraction du dépôt vide créé sur le serveur

git clone git@nom_serveur_git:depot.git

Se placer dans le répertoire du projet

cd depot/

Création d'un fichier de code source
cat > test1.c

Ajout du fichier précédemment créé
git add test1.c

Configuration de git

git config --global prenom.nom "p.nom@serveur_email.fr"
git config nom_developpeur.email "p.nom@serveur_email.fr"

Edition du fichier de config de git pour ajout des raccourcis (cf. tout haut de cet article)

vim ~/.gitconfig

Premier commit
git ci -m "1er ci" test1.c

Synchronisation avec le serveur
git push origin master

Création d'un second fichier source
cat > test2.c

Ajout d'un second fichier

git add test2.c 
git ci -a -m "add test2"
git push origin master

Visualisation du log
git log ou git log --decorate

On se repositionne plus
cd ..

on crée un nouveau répertoire et on s'y positionne

mkdir test
cd test/

Puis on récupère à nouveau le projet pour vérifier qu'on y retrouve ce qu'on vient de créer (test1.c et test2.c)

git clone git@nom_serveur_git:depot.git
ls depot/

vous devez retrouver votre dépôt copié sur votre machine !

… à part tous les fichiers et répertoires qui se trouvent dans .gitignore.

Si vous souhaitez néanmoins que votre dépôt les inclus, il faut recourrir à git annex

… ou bien les enlever de .gitignoreet donc les ajouter… en y ajoutant au besoin des attributs de fichier binaire :

voir .gitattributes …à suivre….

Dans la pratique, on a généralement un projet existant qu'on veut déposer sur le serveur.

On commence par créer le dépôt vide sur le serveur, comme ci-dessus :

sudo mkdir depot_projet_existant.git
cd depot_projet_existant.git/
sudo git init --bare

 

 Si le projet existe bien, en revanche, il n'est pas associé à un dépôt distant. Le fichier .git/config ne doit pas

contenir de [remote "origin"].

Pour créer ce lien, on tape :

git remote add origin git@nom_serveur_git:depot_projet_existant.git

et maintenant, on peut pousser notre projet dans le dépôt vide :

git push origin master

 

Et maintenant, nous sommes deux développeurs (ou 1 avec 2 machines, ça fait presque pareil):

PC développeur 1

qui travaille sur la branche master

PC développeur 2

qui développe sur la branche future_version

Rien de bien particulier ici, ce développeur fait ces commits en local puis de temps à autre un

git push origin master

Le développeur 1 doit maintenant aussi travailler sur la future_version

git fetch origin

ce qui va récupérer sur son poste "la branche" future_version déposée par le développeur 2…

j'ai mis des guillemets autour de la branche car elle n'est pas vraimment créée,

donc pour la créer, on tape :

git co -b future_version origin/future_version

 ou si conserve le même nom de branche (comme ci-dessus), alors on peut taper à la place:

git co --track origin/future_version

C'est ce qui s'appelle une branche de suivi, et nous permet d'utiliser les commandes

git push et git pull pour se synchroniser avec la branche distante.

au début, il faut récupérer le projet :

git clone git@nom_serveur_git:depot.git

on crée la branche future_version :

git co -b future_version

on ajoute, on enlève des fichiers… bref, presque comme sur un nouveau projet, on commit, on tag…

et on "push" la branche sur le serveur (et le(s) tag(s) éventuel(s)) :

git push origin future_version

git push origin --tag

Si ce développeur doit fair une modif sur la branche master, il se repositionne sur le "tronc"

git co master

puis, pour récupérer les modif faites par le developpeur 1:

git pull origin master

et pour récupérer les tags ajoutés:

git pull origin --tag

 

 

 

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)