# Utilisation de git pour ScoDoc

Le dépôt est <https://scodoc.org/git/viennet/ScoDoc>

La branche `master` est celle de ScoDoc 9, d'où sont issues les paquets
distribués (*releases*). Les développements ont lieu sur d'autres branches
(`api`, `dev92`, `entreprises`, ...) avant d'être intégrés après tests. La
branche `Scodoc7` était l'ancienne (jusqu'à septembre 2021) version de ScoDoc.

Ci-dessous quelques pense-bête qui peuvent servir.

## Hot fixes (internes)

Pour les développeurs internes (écriture sur le dépôt master), un exemple
basique illustrant le cycle de développement:

```bash
  # Créer une branche
  #  si besoin (travail en cours), utiliser git stash avant
  git checkout master
  git branch hotfix
  git checkout hotfix
  ... dev, test ...
  git add ...
  git commit -m "fixed ..."
  git checkout master
  git merge hotfix
  git branch -d hotfix
  # publication

  # éventuellement: git stash pop
```

Dans la plupart des cas, on travaillera sur son propre dépôt (clone du dépôt
origine), et on proposera une *pull request* (PR, *demande d'ajout* en français).

## Mettre à jour votre branche

Quand vous travaillez dans votre branche `ma_branche`, pour lui appliquer les
mises à jour de `master` (remote), faire:

```bash
    git pull origin master
```

## Autre exemple

Vous travaillez sur un clone du dépôt principal ("origin"), obtenu par exemple via

```bash
git clone https://scodoc.org/git/ScoDoc/ScoDoc.git
```

remplacer par l'URL de votre dépôt sur gitea au besoin. Si vous avez votre
propre dépôt sur gitea, utilisez deux "remote": l'un pour votre dépôt gitea (ici
nommé `mon_origin`), l'autre pour le dépôt principal ScoDoc (ici nommé
`origin`).

```bash
git remote add origin https://scodoc.org/git/viennet/ScoDoc.git
git remote -v
mon_origin https://xxx.xxx (fetch)
mon_origin https://xxx.xxx (push)
origin https://scodoc.org/git/viennet/ScoDoc.git (fetch)
origin https://scodoc.org/git/viennet/ScoDoc.git (push)
```

Ensuite, tout est prêt, vous créez votre branche:

```bash
git checkout -b ma_branche
```

et la poussez sur votre dépôt: (remplacer `mon_origin`au besoin)

```bash
git push -u mon_origin ma_branche
```

Ajoutez au fur et à mesure vos commits comme d'habitude. Mais régulièrement
(chaque jour), mettez à jour pour éviter de diverger de la branche `master` (ou
autre suivant les cas) de ScoDoc:

```bash
git pull origin master
```

Vous pouvez alors à tout moment soumettre une PR propre.

## Commandes utiles, en vrac

- `git log -L:fonction_python:fichier.py`
- Commits locaux: `git log @{u}..`

### Refactoring

Lint tous les fichiers modifiés:

```bash
    git status | grep modified | grep .py | awk '{print $2}' | xargs pylint -E
```

Affiche les variables non définies dans un fichier:

```bash
   pylint --disable=all  -e E sco_parcours_dut.py | grep undefined-variable | awk '{print $4;}' | sort | uniq | tr -d \' 
```

Prépare un sed pour renommer les variables non définies:

```bash
   for f in *.py
   do
   pylint --disable=all  -e E "$f" | grep undefined-variable | awk '{print "sed -i .bak s/"$4"/scu."$4"/ '$f'";}' | sort | uniq | tr -d \'
   done
```

Restore les modes au besoin (SAMBA les changent parfois):

```bash
    git diff -p -R --no-color | grep -E "^(diff|(old|new) mode)" --color=never | git apply
```

Note pour travailler sur VirtualBox:

```text
  addgroup scodoc vboxsf
```

## Préparation d'une PR (Pull Request) (niveau avancé)

### Principes généraux

Les remarques de cette section visent à obtenir une relecture facile de votre
demande d'ajout (*pull request*, dite "PR"):

- Éviter les modifications de forme qui ne changent pas le sens du code. L'utilisation de
  [`black`](https://black.readthedocs.io/) est obligatoire : elle permet de normaliser la présentation
  du code. Cela évite de générer des différences ne représentant que des
  changements de mise en forme (indentation, passages à la ligne). Cela évite
  aussi au développeur d'avoir à y réfléchir, autant de temps gagné !

- Avoir un nombre d'étapes de validation faible (idéalement un seul commit pour
  les PR courantes - peu volumineuses).

- La PR doit toujours être énoncée par rapport au dernier commit de la branche
  que vous visez (en général `master` du dépôt original).

### Manipulations

Les manipulations sont décrites selon quatre phases du développement : l'installation,
la mise en place, le suivi et la livraison.

#### L'installation

Il est pratique d'avoir en ligne les deux dépôts git distants que vous pouvez
utiliser : votre dépôt personnel (`https://scodoc.org/git/<user>/<dépôt>.git`) et
le dépôt officiel (`https://scodoc.org/git/ScoDoc/ScoDoc.git`).

pour ajouter une référence (et lui donner un nom) vers un dépôt distant, entrez
la commande:

```bash
  git remote add nom_remote https://scodoc.org/git/ScoDoc/<dépôt>.git
```

Par la suite vous aurez donc une référence vers votre dépôt personnel (`perso`)
et une référence vers le dépôt officiel (`officiel`). Si vous avez initialement
cloné l'un des deux dépôts, la référence vers le dépôt d'origine existe et a pour nom
`origin`.

La commande vous exposant tous les dépôts connus est :

```bash
  git remote -v
```

### Mise en place

L'objectif de ce paragraphe est de créer une branche locale basée sur le master
du dépôt officiel et bien sur de lui donner un nom.

pour cela (**attention cela va écraser les éventuels fichiers modifiés**. Si
vous souhaitez conserver les modifications en cours, encadrez les lignes
suivantes par `git stash` (avant) et `git stash apply` (après) :

```bash
  git reset --hard officiel/master
  git checkout -b ma_modif 
```

À partir de là, vous pouvez modifier, tester, développer et commit votre travail.

### Suivi

Si votre développement prend plusieurs jours, il est probable que la branche
principale évolue pendant ce temps.

Pour garder la cohérence, il est nécessaire de réintégrer en local les
modifications de la branche principale. Ceci peut se faire de deux façons.

- Une fusion (`merge`) applique toutes les modifications en un seul commit).
 C'est la méthode couramment utilisée.

- Un `rebase` rejoue tous les commits de la nouvelle branche par dessus l'état
 le plus à jour de la branche principale (il en résulte un historique plus
 linéaire).

Les commandes git correspondantes :

```bash
  git pull officiel master
```

ou encore
```bash
  git fetch officiel
  git merge officiel/master
```

Pour un rebase (à éviter en temps normal):

```bash
  git fetch officiel
  git rebase officiel/merge
```

### La livraison

Ça y est. Vous avez terminé le développement. IL n'y a plus qu'à demander
l'intégration. Ceci se fait en plusieurs étapes (vous êtes bien sûr toujours sur
la branche locale `ma_modif` et toutes vos modifications ont été commitées).

#### Étape 1 : faire l'inventaire des fichiers impliqués

```bash
  git fetch officiel/master
  git diff --name-only officiel/master
```

#### Étape 2 : passer black sur les fichiers modifiés

Cette étape est automatique avec les bons réglages sous VSCode (pas trouvé
l'équivalent sous *pyCharm*).

À défaut les lignes suivantes réalisent le même travail :

```bash
  for fn in $(git diff --name-only officiel/master)
  do
    python3 -m black $fn
  done 
```

Faire une première lecture rapide pour vérifier qu'il ne reste pas de fichiers
modifiés accidentellement.

Pour obtenir la modification sur un fichier spécifique (`app/fichier.py` par
exemple):

```bash
  git diff officiel/master app/fichier.py
```

Utilisateurs Windows : Vérifiez bien que les réglages de fin de ligne suivent
bien les règles Linux: pas de retour chariot (noté CR ou `\r`) en fin de ligne
mais un seul caractère line feed (noté LF  ou `\n`). Le cas échéant, réglez
votre IDE pour cela.

À ce niveau là de la procédure, vous n'avez plus dans votre branche locale que
les différences strictement nécessaires à votre correctif.

#### Étape 3 : résumez tous les commits depuis le point de divergence en un seul commit

**Rarement nécessaire, uniquement si vous avez de nombreux petits commits.**

Repérez le point de divergence de votre branche locale avec officiel/master
(normalement `git merge-base HEAD officiel/master`)

Demander un `rebase` interactif depuis ce point :

```bash
  git rebase -i $(git merge-base HEAD officiel/master)
```

*Explications*: Le rebase interactif permet d'enregistrer un suite de
manipulation de commit dans un seul fichier texte. Le fichier texte qui reprend
tels quels tous les commits concernés (et donc qui ne fait rien) est préparé par
la commande `-i` de la commande_ `git rebase`.

Vous pouvez ensuite modifier ce fichier dans votre éditeur favori (ou pas) (à
régler par `git config`) pour décrire_ _votre intention (réordonner, changer le
message, fusionner, ...) sur l'ensemble des commits.

Quand votre édition est terminée, git reprend la main est exécute chacune de vos
opérations. Il est possible (bien que très rare) que des conflits apparaissent
à ce moment-là. Les commandes habituelles de correction accompagnées des
commandes :

```bash
git rebase --continue # pour poursuivre le processus
git rebase --abort # pour tout abandonner
```

*vous permettront de résoudre ces problèmes exceptionnels*.

Application:

```bash
git rebase -i $(git merge-base HEAD officiel/master)
```

Vous devez obtenir dans un éditeur de texte la liste des commits opéré depuis le
début du développement sous cette forme (c'est un exemple : le nombre de lignes
peut varier) :

```bash
pick eb8cbec modif 1
pick 83eb79e modif 2

# Rebase 5ffd074..83eb79e onto 5ffd074 (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified); use -c <commit> to reword the commit message
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
```

Vous pouvez réorganiser tous les commits (changer l'ordre, fusionner) en
changeant la commande pick au début de chaque ligne. L'idée ici est de fusionner
toutes les lignes avec la première en remplaçant le 'pick' à partir de la ligne
2 par `fixup`. Au besoin, vous pouvez reformuler le message de commit
(commande `reword` sur la première ligne).

Vous construirez par exemple :

```bash
reword eb8cbec Correctif: Api - gestion des formation
fixup 83eb79e modif 2
...
```

Quand vous sortez de l'éditeur, git effectue toutes les opérations demandées.

À ce niveau-là de la procédure :

- vous avez un seul commit pour l'ensemble du correctif proposé ;

- toutes les différences entre officiel/master et votre branche locale sont
  signifiantes.

#### Étape 4

Vous pouvez maintenant pousser votre branche locale sur votre dépôt personnel
(vers une branche de même nom):

```bash
  git push --set-upstream perso ma_branche
```

Si vous avez déjà fait cette opération auparavant il est possible que le push
soit refusé (car le rebase a modifié des commits qui avaient déjà été poussés).
Dans ce cas l'option `--force` du push vous permette de passer outre, mais
assurez-vous avant d'être le seul à travailler sur cette branche.

#### Etape 5 : La dernière étape se passe sur le site [scodoc.org/git](https://scodoc.org/git/)

- Identifiez-vous

- Placez-vous sur la branche nouvellement créée

- À l'aide de l'interface du serveur, vous pouvez comparer l'état de votre
  branche par rapport au master officiel, et si cela vous convient, il vous
  reste à formuler une demande d'intégration (*pull request*). En remplissant
  les informations demandées.