Mise en place

La première chose à faire avant d'utiliser git est de le configurer. Ceci n'est à faire qu'une seule fois.

Dans la ligne de commande, tapez le code suivant, ligne par ligne, en remplaçant les #<XXXX># par des informations personnelles correspondantes:

git config --global user.name #<UN USERNAME>#
git config --global user.email #<VOTRE EMAIL EPFL>#

Ensuite, si vous aimez la couleur sur le terminal, vous pouvez ajouter :

git config --global color.diff auto
git config --global color.status auto
git config --global color.branch auto

Ajoutons quelques alias. Pour cela, créez/éditez un fichier ~/.gitconfig (i.e. fichier nommé « .gitconfig » à la racine de votre répertoire personnel.
Sur les machines du CO, pensez à le recopier dans votre myfiles). Mettez y les lignes suivantes :

[alias]
    lg = log --graph --abbrev-commit --decorate --date=relative --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all
    glog = log --graph --decorate --oneline --all
unstage = reset HEAD --
    last = log -1 HEAD

Concepts de base

Avant de se lancer dans l'utilisation de git, il faut en comprendre le but et la logique :

  • le but est de travailler en commun sur du contenu partagé et, pour cela, archiver les différentes versions (git est un « gestionnaire de versions ») ;

  • la logique est d'avoir trois niveaux :

    1. un dépôt commun de référence (appelé « repo » ou « server principal ») ;
    2. un dépôt local propre à chacun, servant de référence locale (que vous n'avez pas voir) ;
    3. un répertoire de travail local dans lequel on peut faire et défaire des choses sans conséquences tant qu'on ne les a pas validées au niveau 2 ; c'est ce répertoire que vous avez récupéré/allez récupérer avec git clone et dans lequel vous allez travailler (chacun(e)).

Le niveau 2 est en fait assez « abstrait » au sens où vous ne le voyez pas concrètement ; il est totalement géré par des commandes git.

Pour faire passer quelque chose du niveau 1 aux niveaux 2 et 3 en même temps (c.-à-d. récupérer quelque chose mis à disposition par d'autres dans le server principal), on fait (toutes ces commandes dont détaillées ci-dessous ; ici on se concentre sur les concepts) :

git pull

Je vous conseille de le faire assez régulièrement et en tout cas systématiquement avant vos commit/push (expliqués ci-dessous).

Pour valider quelque chose de local, c.-à-d. passer du niveau 3 au niveau 2 uniquement :

  • soit, pour valider une nouvelle version d'un fichier déjà connu :

      git commit -m "MESSAGE" FICHIER
    

    veuillez à chaque fois mettre un message pertinent ;

    p.ex., pour valider une nouvelle version du fichier core.c :

      git commit -m "correction du bug de calcul" core.c
    
  • soit, pour ajouter un nouveau fichier (tout ceci est repris en détails ci-dessous) :

      git add FICHIER
      git commit -m "ajout de EXPLICATION"
    

    p.ex. pour ajouter le nouveau fichier io.c :

      git add io.c
      git commit -m "ajout des entrées/sorties"
    

    Note : pas besoin de remettre le nom du fichier au commit suivant un ou des add ; cela permet en fait d'ajouter plusieurs fichiers d'un coup ; par exemple :

      git add io_core.c
      git add io_errors.c
      git add io.h
      git commit -m "ajout des entrées/sorties"
    

Pour publier en commun ses validations locales, c.-à-d. faire passer du niveau 2 au niveau 1 :

git push

Nous insistons donc sur le fait que pour publier à tous une modification locale, il faut bien faire DEUX choses :

git commit

puis

git push

Reprennons tout ceci (et plus) en détails.

Git garde tous les états successifs (snapshots)

Voyons le premier principe de git : archiver le travail effectué.
Pour cela créez un répertoire et allez-y :

mkdir alice
cd alice

Ajoutez y un fichier :

echo "This is a README file" > README.md

puis archivez dans git l'état courant de ce répertoire :

git add .
git commit -m "Initial commit: README"
  • git add vous permet de proposer (sans que ce soit encore confirmé) un changement ;
    ici nous avons mis tout le répertoire courant avec son nom court : « . », mais on aurait aussi pu ne mettre qu'un fichier, par exemple :

      git add README.md
    

    Nous vous DÉCONSEILLONS d'ailleurs de faire des git add . car cela ajoute souvent plein de mauvaises choses : tous les fichiers du répertoire courant, y compris des fichiers temporaires, brouillons, etc.

    Nous vous conseillons par ailleurs de faire un git status AVANT de faire vos git commit pour bien vérifier ce que vous ajoutez.

  • git commit confirme l'enregistrement (local) des changements proposés ;
    il est fortement recommandé (si ce n'est obligatoire ;-)) de commenter ses changements en ajoutant un message au commit; c'est ce que nous avons fait avec l'option -m.

Si quelque chose est modifié, Git peut vous le dire:

echo "A second line for the README" >> README.md
git status

On pourrait proposer d'ajouter cette nouvelle modification pour un commit futur:

git add README.md

Cette façon de procéder en deux (puis trois, comme nous verrons tout à l'heure) étapes peut parraître fastidieuse, mais c'est une bonne protection contre les bêtises et un bon moyen de faire les choses petit à petit, une à une.

Une fois que vous êtes prêt à enregistrer (localement) vos modification, faites un commit...
...sans oublier d'ajouter un commentaire pertinent avec -m :

git commit -m "Adding a second line to the README file"

Avec Git, on peut voir tout le états enregistrés (snapshots) et même se déplacer de l'un à l'autre (mais c'est plus avancé et vous ne devriez pas en avoir besoin) :

git log

ou:

git lg  # si vous avez défini l'alias plus haut...

Pour se déplacer (en guise ici d'illustration, mais ce n'est pas nécessaire de comprendre cette partie au niveau de ce cours) :

git checkout 5d340  # Mettez un numéro de commit approprié, ancien
cat README.md

Voyez que c'est une ancienne version. Revenons à l'état courant :

git checkout master
cat README.md

Vous avez maintenant compris la notion d'états archivés par Git (snapshots), et donc la différence entre le répertoire de travail courant et l'archive.

Le deuxième concept qu'il faut bien comprendre c'est les DEUX archives qui existent.
Git permet en effet de travailler à plusieurs (cf section suivante) et utilise pour cela deux archives différentes :

  • une locale, que nous avons utilisée jusque maintenant
  • une globale, sur le server Git choisi et où toutes les personnes travaillant sur un même projet vont regrouper leurs modifications.

Pour « pousser » vos changements enregistrés localement (avec des commit) vers le server central, il faut faire :

git push

Je vous recommande grandement de faire au préalable un

git pull

avant chacun de vos

git push

Le pull permet de synchroniser dans l'autre sens : aller chercher les modifications enregistrées dans le server et les appliquer localement.

Plus de détails dans la suite...

Git peut gérer différent états « concurrents »

Git est avant tout un outil de travail collaboratif que beaucoup de gens utilisent justement pour travailler « en parallèle ». Il est donc prévu pour faciliter la gestion de modifications « concurrentes » (ou en tout cas « parallèles » ;-)).

Supposons que Alice a un collaborateur, Bob, sur son projet, et qu'il ait fait des modifications de son coté :

echo "Hey, this is a line added by Bob" >> README.md
git commit -m "Add greeting from Bob" README.md

pendant, qu'en parallèle, Alice continuait aussi à travailler :

sed -i 's/This is a README file/This is a README file with a twist/' README.md
git commit -am "Add a twist to the first line of the README"

Où en sommes nous ? Quels sont les états de Git ?

A partir du dernier état commun (dernier pull des 2 cotés), il y a en fait deux commit bien séparés :

  • un localement chez Bob (qui ne voit pas encore le nouveau d'Alice) ;
  • et un localement chez Alice (qui ne voit pas encore le nouveau de Bob).

Jusque là, pas de confusion possible, donc.

Alice et Bob peuvent maintenant collaborer en partageant leurs contributions. Supposons que Bob « pousse » le premier (pas besoin de pull avant ici) :

git push

Quand Bob fait cela, le server central reçoit et enregistre la modification de Bob. Pas de problème ici.
Un git lg du coté Bob montre que origin/master (celui du server) et master (celui local) sont maintenant les mêmes.

Alice de son coté ne sait pas que le changement de Bob a été propagé au server central. Quand elle essaye de « pousser » ses modifications vers le server :

git push

elle rencontre un problème : un message lui dit que son push a échoué et qu'elle doit d'abord fetch(= récupérer) les modifications enregistrées sur le server central...
Ce qu'elle fait docilement :

git fetch

Avec un

git lg

de son coté, Alice voit qu'elle a maintenant deux commit: un appelé origin/master, qui correspond à celui de Bob et un autre appelé master ou HEAD qui correspond au sien.
Si Alice veut « pousser » ses modifications vers le server central, elle doit d'abord fusionner/regrouper (= merge) ces 2 états différents. Si il n'y a pas de conflit (modifications parrallèles non concurrentes), cela se fait simplement comme suit :

git merge
# Enter a commit message...

Elle peut vérifier l'état :

git lg

puis « maintenant » pousser ce nouvel état, résultant de la fusion des deux modifications :

git push

A partir de là, Alice et le server central sont synchronisés. Pour que Bob soit aussi synchronisé, il lui faut aussi faire un

git fetch

ou plus simplement un

git pull

Cette commande (git pull) permet de faire un fetch puis un merge d'un seul coup.

Les « tags »

Un « tag » (étiquette) est simplement un nom donné à un état mémorisé (snapshot).
Contrairement aux années précédentes (pour ceux qui auraient connu), nous ne les utiliserons pas spécialement. Mais vous pouvez les utilisez, si vous le souhaiter, pour marquer une version particulière de votre projet, typiquement pour vous souvenir d'une version stable. Mais c'est un détail.

Pour donner une étiquette à l'état courant, il suffit simplement de faire :

git tag -a NOM_DU_TAG -m "message"

Par exemple, si vous voulez nommer l'état courrant « version1.1 », vous faites :

git tag -a version1.1 -m "Version 1.1 stable"

La commande

git tag

donne simplement la liste de tous vos « tags ».

Pour voir à quoi correspond un « tag » donné : git show NOM_DU_TAG ; par exemple :

git show version1.1

Pour pousser le tag vers GitHub, ajoutez --tags au push:

git push --tags

Pour aller plus loin

Si vous souhaitez en apprendre plus sur Git et GitHub, vous pouvez aller voir ce tutoriel (en anglais).