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 tutorial plus rapide:

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

Un guide interessant:

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

Un petit jeu si vous vous ennuyer pendant ce cours ou plustard:

https://learngitbranching.js.org

Structure et opetations git

La structure d'un dépôt et les opérations applicables
Adapted from the git book

Installation et Configuration

Installation

Configuration

Il y a plusieurs niveau de configuration dont:

  • local (default): la configuration est appliqué au dépôt uniquement
  • global: la configuration sera appliqueé 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 peu 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éer un dépôt dans un dossier "recettes"
  • Créer un fichier readme.md contenant
  • 
    # Mes recettes
    Chaque fichier contient des recettes que j'apprécie
    							
  • Ajouter ce fichier au dépôt
  • Modifier le fichier readme.md pour qu'il contienne:
  • 
    # Mes recettes
    Chaque fichier contient une recette que j'apprécie
    							
  • Ajouter au dépôt un fichier recette contenant une recette de votre choix
  • Modifier cette recette et ajouter la modification au dépôt
  • Utilisez la commande "git log" pour voir le résultat (a discutter ensemble)

Observer et comparer

  • Observer l'historique:
    $ git log
  • Regarder ce qui pourrais ê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érence 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:

  • Ajouter du sel à votre recette
  • Ajouter le changement dans l'index puis le retirer de l'index
  • Retirer ce changement de votre fichier en utilisant git uniquement
  • Ajouter une liste de recettes à votre fichier readme.md; organiser cette liste en séctions.
  • 
    # 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
    							
  • Ajouter 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

  • Modifier le dernier commit:
    • Observer votre dernier numéro de commit
    • Modifier votre dernier message de commit
    • Observer votre dernier numéro de commit

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

Suppression de fichiers

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

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

  • Changer le nom d'une recette (i.e. nom du fichier)
  • Observer le statut du dépôt
  • Avec la méthode précédente supprimer l'ancien nom du fichier puis ajouter 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 compares

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éer une nouvelle branche dont le but sera de transformer toutes les listes d'ingrédients en listes numérotées. Nommer cette branche intelligement.

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

  • Revenir dans la branche master et y ajouter une recette
  • Regarder le status, que conclure ?
    Commiter la nouvelle recette seulement

  • Revenir dans la branche de numérotation et finaliser 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'outils 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 peu 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

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

  • Effectuer 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 ce complique...

Dans ce cas il est recommendé 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 favoris avec un usage intensif de git status

Merger des modications "complexes"

  • Créer une nouvelle branche auteur
  • Dans cette branche ajouter un auteur sur la première ligne de vos recettes et effectuer un commit
  • Revenir à la branche master
  • Ajouter du sel comme premier ingrédient à une des recettes (avec commit)
  • Effectuer un merge de la branche auteur vers la branche master. S'aider de l'IDE.

Visualizer les branches

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

Un modèle de branchage: 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 connecter sur gitlab.unige.ch
  • Ajouter sa clef publique ssh dans le menu de configuration
  • Créer un bare repository vide
  • L'ajouter comme remote localement
  • Transfrérer tous les fichiers vers le remote
  • Visualiser le resultat sur la plateforme web

Collaboration sur un dépôt

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

Collaboration par fork

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