Git

un version control system (VCS)



Guillaume Chanel

Qu'est-ce qu'un VCS?

Une définition

Système permettant de gérer des changements effectués sur des documents, fichiers sources, sites web, etc.

Gérer c'est pouvoir:

  • revenir à une version précédente (i.e. historique et correction)
  • travailler sur plusieurs versions en parallèle (i.e. travail de groupe et "features")

Gérer des historiques

Une solution standard:


	monfichier.txt
	monfichier.txt.old  # cpold
	monfichier.v2.txt
	monfichier.v2.3txt
	monfichier.v2.3.GC.txt
	monfichier.v2.3.TR.txt
	monfichier.v2.3.GC.TR.20190624.txt
	monfichier.vFinal.txt
	monfichier.vFinal.withmodif.txt
								
Dropbox logo

Problèmes:

  • nommage collaboratif pas homogène
  • qui a effectué un changement ?
  • quand a-t-il été effectué ?
  • qu'est ce qui a été changé et pourquoi ?
  • difficile de partager cet historique
  • pas de backup
  • ne monte pas en charge facilement (scalability)

Travailler en parallèle

Cela suppose d'automatiser au maximum les phases suivantes:

  • Partager le contenu facilement
  • Travailler sur sa version séparément des autres personnes et versions
  • Fusionner (merge) les versions

Quelques VCS

courverture livre CVS
Subversion logo
Git logo
Mercurial logo

Centralisésl'historique complet du dépôt est sur le serveur. Chaque client possède uniquement un instantané

Distribuéschaque copie locale contient tout l'historique

Nous ne vous disons pas tout...

Le livre en ligne git est extrêmement bien fait et vous apprendra tout ce que nous n'avons pas le temps de vous montrer:

https://git-scm.com/book

Un tutoriel plus rapide:

https://www.atlassian.com/git/tutorials

Un guide intéressant:

https://hacker-tools.github.io/version-control

Un petit jeu si vous vous ennuyez pendant ce cours ou plus tard:

https://learngitbranching.js.org

Structure et operations git

La structure d'un dépôt et les opérations applicables
Adapté du git book

Installation et Configuration

Installation

Configuration

Il y a plusieurs niveaux de configuration dont:

  • local (default): la configuration est appliqué au dépôt uniquement
  • global: la configuration sera appliquée pour tous vos dépôt

Note: la configuration reste locale à la machine, elle n'est pas associé au dépôt (mais c'est possible si nécessaire)

On peut lister la configuration actuelle avec:

$ git config --list

Configuration

Avant d'utiliser git il faut toujours configurer au moins deux variables:


$ git config --global user.name "Prénom Nom"
$ git config --global user.email "Prénom.Nom@email.cc"
						

Note: la configuration peut être locale (i.e. ne pas utiliser --global) si l'on veut utiliser des identifiants différents pour un ou plusieurs dépôt

Dépôt local (repository)

Objectif du dépôt

L'objectif du dépôt est de stocker des recettes de cuisines.

Chaque recette prendra ce format:


- ingrédient 1
- ingrédient 2
- ingrédient 3

Première opération
Deuxième opération
Cuisson
etc.
						

Commandes utiles

Initialiser un dépôt

$ git init

Ajouter les modifications à l'index (un fichier ou tous les fichiers modifiés)


							$ git add fichier
							$ git add . # tous les fichiers
						

Ajouter toutes les modifications de l'index au dépôt

$ git commit

Un premier dépôt

A chaque étape observer le status du dépôt:

  • Créez un dépôt dans un dossier "recettes"
  • Crée un fichier readme.md contenant
  • 
    # Mes recettes
    Chaque fichier contient des recettes que j'apprécie
    							
  • Ajoutez ce fichier au dépôt
  • Modifiez le fichier readme.md pour qu'il contienne:
  • 
    # Mes recettes
    Chaque fichier contient une recette que j'apprécie
    							
  • Ajoutez au dépôt un fichier recette contenant une recette de votre choix
  • Modifiez cette recette et ajoutez la modification au dépôt
  • Utilisez la commande "git log" pour voir le résultat (à discuter ensemble)

Observer et comparer

  • Observer l'historique:
    $ git log
  • Regarder ce qui pourrait être ajouté à l'index (working directory vs. index):
    $ git diff [filename]
    Modifier une recette et utiliser la commande ci-dessus
  • Regarder le travail effectué depuis un commit (working directory vs. commit):
    $ git diff commit-hash [filename]
    Regarder les modifications effectuées depuis la modification du fichier readme.md
  • Regarder le travail effectué entre deux commits (commit vs. commit, attention à l'ordre):
    $ git diff hash-ancien hash-recent [filename]
    Regarder les différences entre les deux versions du fichier readme.md

Oups...

  • "Je voudrais supprimer tous les changements de mon working tree pour revenir à l'état de mon dernier commit"
    $ git checkout -- .

  • "J'ai ajouté un fichier de trop dans l'index (staging area) et je ne le veux pas dans le commit"
    $ git reset filename

  • "Zut... j'ai fait trop de changements dans mon fichier et je voudrais les insérer en plusieurs commit"
    $ git add -p; git commit

Corriger working tree et staging area

Vérifier le statut après chaque opération:

  • Ajoutez du sel à votre recette
  • Ajoutez le changement dans l'index puis le retirer de l'index
  • Retirez ce changement de votre fichier en utilisant git uniquement
  • Ajoutez une liste de recettes à votre fichier readme.md; organiser cette liste en sections.
  • 
    # Mes recettes
    Chaque fichier contient des recettes que j'apprécie
    
    ## Entrées
    - salade niçoise
    - scalade de chèvre
    
    ## Plats d'hiver (ou pas)
    - fondue
    - tartiflette
    							
  • Ajoutez ces sections en plusieurs commits, vérifier avec git diff avant chaque commit.

Rhaaaaaaaa... au secours !

  • "J'ai oublié de mettre un fichier dans mon commit / j'ai fait une erreur dans le message de commit"
    $ git commit --amend

  • "Mais pourquoi j'ai fait ce commit !!!! Je veux le supprimer."
    $ git revert commit-hash  # "git reset commit-hash" pour "revenir en arrière" mais c'est à éviter

Corriger les commits

  • Modifiez le dernier commit:
    • Observez votre dernier numéro de commit
    • Modifiez votre dernier message de commit
    • Observez votre dernier numéro de commit

  • Supprimez les changements effectués par un commit:
    • Observez votre historique
    • Annulez le commit qui changeait votre première recette (voir exercice précédent)
    • Annulez le précédent commit d'annulation
    • Observez votre historique

Suppression de fichiers

  • Observez le status du dépot tout au long de ces opérations

  • Si ce n'est pas déjà fait ajoutez plusieurs recettes
  • Supprimez un fichier de la manière habituelle
  • Observez le statut du dépôt et trouvez une solution pour effectuer un commit de ce changement

  • Changez le nom d'une recette (i.e. nom du fichier)
  • Observe le statut du dépôt
  • Avec la méthode précédente supprimez l'ancien nom du fichier puis ajoutez toutes les nouveautées à l'index

  • Conclure à partir de l'évolution du statut du dépôt

Quel contenu mettre dans les dépôts ?

OUIPeut-êtreNON
Des fichiers textes

Des petits fichiers binaires de type assets(icones, images, etc.)

Des fichiers binaires de taille plus importantes (voir git lfs et git media)

Librairies

Tout fichier n'appartenant pas au projet (e.g. .DS_Store, Thumbs.db, .vscode, etc.)

Fichier générables (executables, résultats de calculs etc.)

Mots de passe

Il existe une solution pour eviter d'ajouter des fichiers non voulus dans l'index: .gitignore

Les branches

Problème habituellement rencontré

Le boss: "Hey j'ai eu une super idée pour notre programme... Ca te prendra combien de temps pour le faire ?"

Le programmeur: "Ben... une semaine..."

Deux jours plus tard...

Le boss: "Aie... on a un client qui a trouvé un bug il faut le corriger de toute urgence !"

Le programmeur: "Ben je peux pas, je suis en train de modifier le code pour la nouvelle feature et du coup rien n'est stable !"

... advienne que pourra ...

Problème habituellement rencontré

Pas la peine d'avoir un boss ou de travailler en équipe pour faire face à ce genre de problème

Le programmeur: "J'ai deux idées pour ajouter cette fonctionnalitée à mon programme... je me demande laquelle est la meilleure ?"

Bonne vielle solution: je copie mon dossier deux fois et je compare les solutions (défauts déjà discutés)

Nouvelle solution: je crée deux branches et je les compare

Création et utilisation d'une branche

La commande suivante permet de créer une branche:

git branch new-branch-name

On se déplace d'une branche à une autre en utilisant:

git checkout la-branche-de-destination

Note: la commande checkout peut aussi être utilisée pour se rendre vers un commit passé

Note: le déplacement peut modifier le working tree

Pour connaitre la liste des branches:

git branch

Utiliser des branches

  • Créez une nouvelle branche dont le but sera de transformer toutes les listes d'ingrédients en listes numérotées. Nommer cette branche intelligement.

  • Commencez à numéroter les listes dans la nouvelle branche sans finir le travail (pas de commit)

  • Revenez dans la branche master et ajoutez une recette
  • Regardez le status, que conclure ?
    Commitez la nouvelle recette seulement

  • Revenez dans la branche de numérotation et finalisez la numérotation (avec commit)

Aide visuelle

Lorsque l'on travaille avec git il est utile d'avoir une aide visuelle nous indiquant dans quelle branche on est.

Pour bash, si l'outil bash-completion est installé vous pouvez configurer votre prompt avec la fonction __git_ps1:


export GIT_PS1_SHOWDIRTYSTATE=1
export PS1='\w$(__git_ps1 " (%s)")\$ '
							

Stash: une commande utile

"Zut, je voudrais changer de branche mais j'ai fait des changements que je ne veux pas commiter..."

La commande stash est idéale pour cela:


$ git stash                    # place les changements dans une liste (cachette) → "a clean working directory"
$ git stash list               # liste le contenu de la cachette
$ git stash apply [stash-name] # applique les modifications sur le commit courant sans le retirer de la liste
$ git stash pop [stash-name]   # applique les modifications sur le commit courant en le retirant de la liste

							

Un peu plus sur checkout

git checkout peut s'appliquer sur:
  • commits / branches, dans ce cas le commit en question est placé en working directory
  • git checkout commit
  • fichiers, dans ce cas le fichier est remplacé par le contenu de l'index (i.e. la tête si l'index est vide)
  • git checkout -- fichier
    vous comprenez maintenant: git checkout -- .

Intégrer une branche dans une autre: merge

Le but d'une branche est souvent d'être intégrée à sa branche de départ. Pour intégrer la branche new dans la branche old il faut:


git checkout old
git merge new
						
Il existe dans la communité une divergence d'opinion sur l'utilisation de git rebase au lieu de git merge. Nous vous montrons la commande merge car rebase peu être desctructive et poser cetains problèmes

Intégrer une branche dans une autre: merge

Deux cas de figure possibles:

  • Le merge se fait en fast foward → pas de commit




  • La branche old a reçu des commits depuis la création de newmerge commit





Merger / Combiner les branches

  • Effectuez le merge de votre branche contenant les numérotations vers la branche master. De quel type de merge s'agit-il?

  • Effectuez de vous même un merge de type fast-forward en créant une nouvelle branche.

C'est vraiment super facile de faire un merge !?

Oui, tant que les branches ont modifié des fichiers différents, autrement cela se complique...

Dans ce cas il est recommandé d'utiliser une interface graphique (e.g. un IDE) ou de faire appel à la commande

git mergetool

Dans l'exerice suivant nous utiliserons notre IDE favori avec un usage intensif de git status

Merger des modifications "complexes"

  • Créez une nouvelle branche auteur
  • Dans cette branche ajoutez un auteur sur la première ligne de vos recettes et effectuez un commit
  • Revenez à la branche master
  • Ajoutez du sel comme premier ingrédient à une des recettes (avec commit)
  • Effectuez un merge de la branche auteur vers la branche master. S'aider de l'IDE.

Visualiser les branches

Il faut ajouter des options à la commande habituelle:
git log --graph --all

Un modèle de branches: gitflow

modèle gitflow

Les remotes: enfin du GIT collaboratif !

remote workflows

dépôt local dépôt bare and remote remote add push clone push / pull fork clone push / pull pull request clone(ssh / local)

Backup sur remote

remote add push
  • Se connectez sur gitlab.unige.ch
  • Ajoutez sa clef publique ssh dans le menu de configuration
  • Créez un bare repository vide, ayant pour titre "recette-votrenomprenom"
  • Ajoutez le comme remote localement
  • Transférez tous les fichiers vers le remote
  • Visualisez le résultat sur la plateforme web

Collaboration sur un dépôt

remote add push clone push / pull
  • Configurez le dépôt bare pour donner accès (modifications) à la personne se situant à votre droite
  • Clonez le dépôt qui vous a été assigné dans un dossier différent de votre dépôt personnel
  • Observez les remotes après la commande clone
  • Ajoutez une recette dans la branche master de ce dépôt et la pousser sur le remote
  • Retournez dans votre dépôt initial et récupérez la recette qui a été ajoutée par un collègue

Collaboration par fork

remote add push fork clone push / pull pull request
  • Effectuez un fork du dépôt de la personne à votre gauche
  • Clonez ce nouveau dépôt localement
  • Modifiez une recette dans une autre branche que master et la pousser sur le remote
  • Effectuez une pull request depuis l'interface web de votre remote
  • Acceptez la pull request qui vous a été envoyée
  • Obtenez cette modification localement