
---
title: "PdP: Git"
date: 2024-01-17
tags: ["Internet", "Débit"]
categories: ["Projet de programmation", "Cours"]
mathjax: true
---

Les systèmes de contrôles de versions permettent de garder l'ensemble des
changement effectués sur le code source, à plusieurs dévellopeurs de collaborer
(via des opérations de *merge*) et de naviguer dans l'historique d'un projet.
Ils existent depuis 1972 avec l'apparition de *SCCS*, il a été suivi de
plusieurs autres logiciels depuis, open-sources ou propriétaires.

## Quelques définitions

## Gestion de l'historique

Il y a deux façons de voir l'historique dans un système de version:

 1. Organisé chronologiquement, immuable dans le sens ou il ne peut être 
    réinscrit(les erreurs aussi...);
 2. Comme partie intégrante de la documentation. Ainsi il est possible de
    comprendre comment et pourquoi on en est arrivé là. Il est alors permis de
    le réécrire afin de le clarifier par exemple.

### Définitions

**Dépôt**  (*repository*): endroit ou est stockée l'historique complet d'un 
projet. Il contient l'ensemble des métadonnées du projet.

**Copie de travail** (*working copy*): une *snapshot* prise depuis le *dépôt* et
sur laquelle travailler.

## Les paragigmes : centralisé ou distribué.

Dans un environnement centralisé ou le serveur regroupe toutes les sources et
faisant office de référentiel.

Dans un environnement distribué ou *décentralisé*, un système de connexion pair
à pair est utilisé. Ainsi le dépôt est présent sur les machines des dévellopeurs
et éventuellement su run ou plusiers serveurs.

## git

C'est le système de gestion de version le plus utilisé. Il a été créé par Linux
Torvald, créateur mainteneur du noyau Linux.

L'utilisation de *git* se fait d'abord en ligne de commande :

```shell
$ git --stat --after="yesterday" bugfix 
#     <options                 > <args>
```

La commande contient des options et des aguments en fonction des actions à
effectuer. *Git* permet tout un ensemble de commandes, en voici quelques
exemples: 

```shell
# initialiser un nouveau dépôt
$ git init 

# ajouter un fichier
$ git add moncode.c

# effectuer un commit
$ git commit -m "Ajour de la fonctionnalité X"
```

*Git* propose une aide en ligne accessible via la commande `help` ou des manuel
via lacommande `man`:

```shell
$ git help <commande>
# ou
$ git <commande> --help

# Et via les manuels
man git-<commande>
```

Il est possible de configurer git globalement (pour tous les projets) ou par
projet. La configuration se fait en ligne de commande :

```shell
# configuration dans un projet, locale
$ git config --local user.name "John Doe"
$ git config --local user.email j.doe@anonymous.de
$ git config --local core.editor nvim

# configuration blobale
$ git config --global user.name "John Doe"
$ git config --global user.email j.doe@anonymous.de
$ git config --global core.editor nvim
```

La configuration **locale prend le pas sur celle globale**. La configuration est
elle même stockée dans un fichier texte au format *ini*. La liste de toutes les
clés de configuration définie s'obtient avec la commande `git config --list`.

### L'index

Physiquement, l*index* est un fichier stocké dans le répertoire `.git` à la
racine du projet. Il contient une liste de chemin vers des ficiers avec leurs
permissions et le condensat *SHA1* de l'objet en question.

Les fichiers stockés dans notre index on 6 états répartis en 3 catégories :

 1. La copie de travail:
   * *Untracked Files*: les fichiers non suivis par git;
   * *Unmodified Files*: les fichiers suivis par git mais non modifiés;
   * *Modified Files*: les fichies suivis et modifiés.
 2. Métadonnées:
   * `staging area`: stocke l'ensemble des modifications temporaires et
     plannifiées pour le commit 
     (dans `.git/index`); 
   * `local repository`: garde l'intégralité de l'historique (dans 
     `.git/objects`).
 3. Le réseau
   * `Remote Repository`: l'endroit où l'ensemble du travail des dévellopeurs
     est synchronisé.


L'état courant de l'index s'obtient avec la commande `status`. Il est possible
de vois les 3 états de notre *copie de travail*, voici un exemple sur le dépôt
de mes notes de cours:

```shell
$ git  status
On branch main
Your branch is up to date with 'origin/main'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	content/projet_programmation/

nothing added to commit but untracked files present (use "git add" to track)
```

Les deux commandes principales interagir avec l'index en ajoutant du contenu 
sont `add` et `mv` comme le montre le diagramme ci-dessous:

```
+-----------+ | +------------+ | +----------+ | +---------+ | +------------+ | +-------------+
| Untracked | | | Unmodified | | | Modified | | | Staging | | | Local repo | | | Remote repo |
+-----------+ | +------------+ | +----------+ | +---------+ | +------------+ | +-------------+
              |                |              |             |                |
      >-----------------(git add)-------------------->      |                |
              |                |              |             |                |
              |                |     >---(git add)--->      |                |
              |                |              |             |                |
              |       >-----------(git mv)----------->      |                |
              |                |              |             |                |
              |                |     >----(git mv)--->      |                |
              |                |              |             |                |
```

Les commandes `rm` et `reset` permettent de supprimer du contenu. 
`git rm -r <dir>` permet de supprimer récusivement un repertoire et  
`git rm --cached <file>` supprime un  élément des fichiers suivis sans pour
autant supprimer le supprimer du disque:

```
+-----------+ | +------------+ | +----------+ | +---------+ | +------------+ | +-------------+
| Untracked | | | Unmodified | | | Modified | | | Staging | | | Local repo | | | Remote repo |
+-----------+ | +------------+ | +----------+ | +---------+ | +------------+ | +-------------+
              |                |              |             |                |
      <-----------------(git rm)---------------------<      |                |
              |                |              |             |                |
      <--------(git rm --force)--------<      |             |                |
              |                |              |             |                |
      <----(git rm)---<        |              |             |                |
              |                |              |             |                |
              |                |      <--(git reset)--<     |                |
              |                |              |             |                |
              |        <------(git reset --hard)------<     |                |
```

### Ignorer des fichiers

Un projet peut contenir un ensemble de fichiers que ne doivent pas être suivi
comme par exmeple les fichiers object issus de la compilation ou encore les
binaires.

Git propose un mécanisme pour ignorer ces fichiers via un fichier que se soit
globalement via `~/.config/git/ignore` ou par projet avec `.gitignore`  racine.

Voici un exemple:

```.gitignore
# All .o files
*.o

# all the content into build/
**/build

# but the text file mus be tracked
!build/*.txt
```

Il est trs important **de maintenir une liste à jour des fichiers ignorés** afin
de laisser le dépôt le plus propre possible. On évite ainsi le bruit initile
dans notre dépôt et ainsi d'oublier de mettre dans le stage un fichier importan
noyé dans la liste des fichier non-suivi.

### Gestion de l'historique

Chaque *commit* est identifié par un condensat `SHA1` calculé sur l'arbre des
chagements (arbre de Merkel). L'historique des changements effectués est
accessible par la commande `git log`. Voici quelques exemples:

```shell
# version courte ...
$ git log --oneline

# version courte avec des stats
$ git log --oneline --stat

# en version patch
$ git log --patch
```

Écrire des messages de commit cohérent permet de garder un historique propre et
de documenter les changements effectués. Un commit peut contenir plusieurs
lignes cependant la première doit être inférieure à 50 lignes, il est conseillé
de limiter les suivantes à 72 caractères.
doivent avoir 72 caractères

#### Se balader dans l'historique

La commande `checkout` permet de se ballader dans l'historique alors que 
`commit` permet de rajouter un point dans l'historique. *git c

```
+-----------+ | +------------+ | +----------+ | +---------+ | +------------+ | +-------------+
| Untracked | | | Unmodified | | | Modified | | | Staging | | | Local repo | | | Remote repo |
+-----------+ | +------------+ | +----------+ | +---------+ | +------------+ | +-------------+
              |                |              |             |                |
              |                |              |      >--(git commit)-->      |
              |                |              |             |                |
              |                |     >-------(git commit --all)------->      |
              |                |              |             |                |
              |       <-----------(git checkout HEAD)-----------------<      |
              |                |              |             |                |
              |       <--------(git checkout)--------<      |                |
              |                |              |             |                |
```

#### Les branches, tags

Pour les branches, ous en utilisons une les dès que nous initialisons notre
dépôt, souvent appelée `master` ou `main`. Elle représentent une référence vers
une suite de commits. Cette référence évolue lors de l'ajout de commit.

Un tag est lui une référence vers un commit précis qui n'évolue pas.
