Table des matières
GIT
Ah, git… tout est dit, non ?
C'est un bel outil, puissant parait-il, complexe, très complexe, trop complexe ?
Enfin, ça semble plus qu'adéquat pour faire la sauvegarde et le versioning de mes petits projets, alors allons-y !
On trouve pas mal de “super tutos”, des cheat-list, etc… pour savoir comment utiliser git en ligne de commande : j'ai jamais rien compris et nombreux sont ceux qui peuvent témoigner du nombre de jurons que j'ai pu proférer contre git. Alors cette page n'a pas pour vocation d'être un autre de ces tutos, mais elle revient plutôt à l'essence même de ce site : être mon aide mémoire. C'est à dire exposer les commandes dont j'ai besoin et les qques concepts qu'il faut connaître pour les comprendre.
La plupart des explications de cet article viennent de http://git-scm.com/doc
Outils pratiques :
- gitList, une sorte de visionneuse web du dépôt git (fichiers, historiques des commit…)
- gogs qui en plus des fonctions de visionneuse rajoute des métadonnées autour du projet (wiki, issue/milestone list..) et une dimension sociale si on code à plusieurs /!\ il faut passer par le protocole https: et non ssh: pour que gogs soit au courant des modifs dans le dépot git
- gitlab, self-hostable, open source, mais un peu lourd en besoin de ressources, je lui préfère gogs
CONCEPTS
Principes de base
Git est un gestionnaire de version, il sert à conserver la version d'un fichier entre chaque changement, et permet de récupérer à tout moment l'état d'une version antérieure.
Git est décentralisé, ça signifie que chacun des clients possède l'intégralité de l'historique du dépot. Entre autres, on peut avoir plusieurs dépôts distants pour synchroniser son dépot local.
Les 3 états, les 3 zones
Git possède 3 états dans lesquels peuvent être les fichiers :
- modifié (modified), c'est à dire dans le système de fichier classique (working directory)
- indexé (staged), c'est à dire dans la zone d'index (staging area)
- validé (commited), c'est à dire bien au chaud, rangés dans le dossier .git (git directory / git database)
Un fichier doit passer par la case staged avant d'aller dans la case commit.
On va voir par la suite comment fonctionner avec ces états et comment passer de l'un à l'autre.
Comprendre
Commit
Un commit contient un index (liens vers les fichiers), un message de commit, et un lien vers l'ancien commit. Il peut donc y avoir une succession de commit en remonttant de l'un à l'autre.
ça peut paraître obscur ou incomplet (ça l'est surement), mais ces histoires de pointeurs est à approfondir, elles permettent de vraiment comprendre comment git stocke les choses, et comment elles s'articulent entre elles, et donc de bien utiliser git (ce qui est quand même l'objectif).
Stockage des fichiers
Git conserve chaque fichier intégralement. Si entre 2 commit un fichier ne change pas, le fichier n'est pas sauvegardé mais un lien vers le fichier du commit précédent est fait. Cela permet de ne pas consommer trop de place en gardant une vitesse de traitement convenable (contrairement aux autres systèmes qui ne conservent que des delta entre les différentes versions, certes utile pour optimiser l'espace, par contre pas rapide du tout).
Obtenir de l'aide
# Par exemple sur la fonctionnalité config : git help config
CONFIG
# On peut voir la config avec git config --list ## A la première execution de git, il faut règler qques petits aspects de config : # Identité git config --global user.name "John Doe" git config --global user.email johndoe@example.com # Editeur par défaut git config --global core.editor vim # Pour ne pas retaper le mot de passé tout le temps : # enregistre les infos creditential qques minutes git config --global credential.helper cache
CREER UN DEPOT
# Créer un dépôt git dans un dossier existant : git init # Cloner un dépôt existant : git clone https://github.com/libgit2/libgit2 [dossierDestination] # cloner crée aussi un lien vers le dépôt distant nommé ''origin'' # cloner crée en local une branche appelée ''master'' qui suit ''origin/master''
TENIR UN DEPÔT (en fait une branche) A JOUR
Toutes les commandes qui suivent permettent de tenir à jour la branche actuelle. Voir le paragraphe suivant pour la gestion des branches.
# pour savoir où on en est : git status # Pour indexer (et suivre) des nouveaux fichiers : git add FILE_NAME # Si on a changé un fichier suivi et qu'on veut sauvegarder son état actuel # dans la staging area (git status nous donne la commande à utiliser) : git add FILE_NAME # NOTE : le fichier sera ensuite gelé tel quel dans la staging area, # si on le modifie de nouveau par la suite AVANT un commit, il faudra ré-utiliser # la commande ''git add'' pour mettre à jour la staging area avec # la nouvelle version du fichier # Pour Unstage un fichier qu'on aurait ajouté par erreur avec ''git add file'' # (comme souvent la commande est suggérée par ''git status'' ) git reset HEAD FILE_NAME # NOTE : le fichier reste modifié dans le working directory, # il est juste sorti du stage area (et donc du prochain commit) # Pour archiver/valider les modifications présentes dans la staging area # (et non celles dans le working directory) git commit # NOTE : Cela va ouvrir notre éditeur de texte par défaut pour # qu'on puisse laisser un message décrivant le commit. # Version courte : git commit -m "bugfix #185 : speed request improved" # pour rajouter un truc à un commit qu'on aurait fait trop vite : git add FICHIER_OUBLIE git commit --amend # Pour enlever des fichiers, # il faut les enlever de la staging area et faire un commit. ... # OU en 1 commande: git rm FILE_NAME # NOTE : ''git rm'' supprime aussi le fichier du working directory # permet de ne plus suivre un fichier mais de le laisser dans le working directory git rm --cached FILE_NAME # Pour déplacer des fichiers : # de base, git ne suit pas les déplacements effectués via le système de fichier, # il faut les lui signaler explicitement, ou utiliser la commande magique : git mv FILE_FROM FILE_TO # NOTE : git effectue aussi les actions dans le système de fichier ($> rm FILE_NAME) # Pour annuler une modification d'un fichier du working directory, # c'est à dire récupérer la version du dernier commit : # (encore une fois ''git status'' nous suggère la commande) git checkout -- FILE_NAME # NOTE : il est préférable de faire un commit dans une nouvelle branche # puis de récupérer l'ancienne branche que d'écraser ainsi # un fichier qui serait définitivement perdu !
Astuces
Pour ignorer des fichiers, il faut mettre leur nom dans le fichier .gitignore
à la racine du dépôt. Différentes syntaxes (regex ?) peuvent être utilisées comme filtre :
blabla --- A REMPLIR
Tag
# liste des tag (filtrée ou non) git tag [-l "REGEX"] # créer un tag "léger" sur le dernier commit # (c'est un simple pointeur vers un commit particulier) git tag TAG_NAME # Transférer un tag vers un dépôt distant (par défaut, ils sont uniquement locaux) git push REMOTE_NAME TAG_NAME # faire un checkout sur un tag particulier git checkout -b BRANCH_NAME TAG_NAME # on peut faire d'autres choses : # - des tag annotés # - des tag sur un commit particulier déjà dépassé # ...
GESTION DES DEPÔTS DISTANTS
La commande git remote
permet de lister les dépôts distants, d'en ajouter ou d'en retirer :
# afficher la liste des dépôts distants et leur alias git remote -v # ajouter un nouveau dépôt distant git remote add REMOTE_NAME URL # pour link un dépot distant avec une branche distante (pour avoir que git push et git pull) git push -u REMOTE_NAME BRANCH_NAME # enlever un dépot distant git remote remove REMOTE_NAME # pour inspecter la config (et les branches) d'un dépôt distant git remote show REMOTE_NAME # pour plus d'info git help remote # récupère les données du dépôts distant # mais ne bascule pas sur ces données ni n'écrase le working directory git fetch REMOTE
BRANCHES
Comprendre
Une branche n'est en réalité qu'un pointeur vers un commit.
HEAD
fait référence à la branche actuelle, soit au dernier commit de cette branche.
Gestion des branches
# voir les branches ( git branch git branch [-v] # avec les messages des derniers commit git branch [--merged] [--no-merged] # filtres # créer une branche (donc un pointeur vers le dernier commit de la branche actuelle) git branch BRANCH_NAME # basculer sur une autre branche # !! ça change le working directory et charge les données de la branche !! # !! normalement, si ce n'est possible de le faire proprement : # (aka il y a des modifs non commit), git ne change pas de branche git checkout BRANCH_NAME # merge : # on se place dans la branche de destination # on appelle la commande merge avec le nom de la branche à ramener # cela crée un commit dans la branche de destination et avance les pointeurs commit git checkout BRANCH_DEST git merge --no-ff BRANCH_NAME # => utile : l'option --no-ff # ne crée pas tous les commit intermédiaires de la branche à finir # dans la branche de destination, juste le commit du merge. # Pratique si on considère qu'une branche sert une fonctionnalité # et qu'on veut pas que les autres branches/fonctionnalités parasitent # la branche de destination # supprimer une branche git branch -d BRANCH_NAME # git switch et git restore # Le but de ces 2 commandes est de différencier les 2 activités de `git checkout` git switch <branch_name> # can now be used to change branches, as git checkout <branchname> does git restore <file_path> # can be used to reset files to certain revisions, as git checkout -- <path_to_file> does
Conflits
Lorsqu'il y a conflit, le merge n'aboutit pas et git annote les fichiers pour resolution manuelle du conflit, il faut ensuite marquer le conflit comme résolu à l'aide d'un git add FILE_NAME
aller plus loin : git mergetool
Branches distantes
Elles sont references sous la forme REMOTE/BRANCH.
Elles ne bougent pas sans synchro extérieure (git fetch BRANCH_NAME
← met à jour la database locale de git)
Par exemple : master (local)
# Partager son travail avec le monde extérieur # cela va mettre à jour la branche du depot distant avec la branche de notre dépôt git push REMOTE_NAME BRANCH_NAME # pour récupérer le travail d'autrui git fetch AUTRUI git checkout BRANCH_NAME git merge AUTRUI/BRANCH_NAME # pour supprimer une branche distante git push REMOTE --delete BRANCH
Branches suivies (tracking)
Ce sont des branches qui ont un lien direct avec une branche d'un depot distant. Ce qui permet d'utiliser git pull
.
## creation d'une tracking branche # tracking de REMOTE/BRANCH depuis la branche actuelle git branch -u REMOTE/BRANCH # OU # crée master et origin, master suit origin/master git clone ... # OU # créer une branche locale qui track remote/origin, et bascule sur cette branche git checkout -b BRANCH_NAME REMOTE/BRANCH # voir les branches qui sont suivies git branch -vv # recupère les changements depuis le depot distant # mais NE modifie PAS le working directory git fetch
RACCOURCIS
En vrac, pour toutes les sections :
# créer une branche et basculer vers celle-ci git checkout -b BRANCH_NAME # = git branch BRANCH_NAME git checkout BRANCH_NAME # récupérer le contenu distant et le mettre dans son working directory git pull # = git fetch git merge # Si on a foutu le bazar dans le repertoire mais qu'on a pas commit, # retrouve le répertoire à l'état du dernier commit git reset --hard HEAD # Est-ce que ça touche aux fichiers non indexés ? Non
MODELE DE DEVELOPPEMENT
C'est un condensé de cet article, que je trouve bien fait.
En gros :
- la branche
master
représente l'état production du projet, c'est sensé tourner en permanence - la branche
dev
contient les nouvelles fonctionnalités, encore à l'état instable. Lorsque l'état est suffisamment stable, le contenu peut etre placé dans la branchemaster
- au besoin, des branches temporaires :
feature
, pour le developpement de fonctionnalités particulières dont l'avenir est incertain ou particulièrement instable,- ou
hotfix
pour ajouter un bugfix à la branchemaster
sans ajouter de nouvelles fonctionnalités (potentiellement instables)
!!! les merge se font avec l'option –no-ff
, pour éviter de reporter les commit de développement d'une fonctionnalité particulière dans les branches principales (master
, dev
)
PROTOCOLES
On peut utiliser plusieurs protocoles selon ce qui est mis à dispo par le serveur pour synchroniser un dépôt git :
# il y a ssh git clone ssh://ID@HOTE:PATH # PATH peu être relatif ou absolu # ssh + port spécifique git clone ss://ID@HOTE:PORT/ABS_PATH # il y a https (peut être utile pour mettre à jour un service de suiv comme github, gogs ou gitlab git clone https://URL