diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 355b17f..6205e51 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,6 +28,7 @@ jobs: - drf-initiation - django-initiation - python-perfs + - git-initiation secrets: deploy_key: ${{ secrets.deploy_key }} known_hosts: ${{ secrets.known_hosts }} diff --git a/git-initiation/Makefile b/git-initiation/Makefile new file mode 100644 index 0000000..c8699df --- /dev/null +++ b/git-initiation/Makefile @@ -0,0 +1,34 @@ +.PHONY: help +help: + @echo "Usage:" + @echo " make static # to build static version." + @echo " make test # to run tests." + @echo " make rsync # rsync to prod" + @echo " make clean" + +.PHONY: static +static: output/index.html + cp -a static/ output/ + +.PHONY: check +test: + python test.py *.md + +%.html: %.md + mdtoreveal $< --output $@ + +output/index.md: git.md + mkdir -p output + cat $< > $@ + +.PHONY: rsync +rsync: static + rsync -vah --delete output/ mdk_fr@mdk.fr:/var/www/mdk.fr/git-initiation/ + +.PHONY: clean +clean: + rm -fr output/ + +.PHONY: serve +serve: + python -m http.server -d output/ diff --git a/git-initiation/README.md b/git-initiation/README.md new file mode 100644 index 0000000..a967f74 --- /dev/null +++ b/git-initiation/README.md @@ -0,0 +1 @@ +# GIT diff --git a/git-initiation/git.md b/git-initiation/git.md new file mode 100644 index 0000000..ae20da3 --- /dev/null +++ b/git-initiation/git.md @@ -0,0 +1,646 @@ +# Initiation Git + +par + +Julien Palard + +https://mdk.fr + +::: notes + +Introduce yourself! + + +# Git + +Git est un gestionnaire de code source distribué. + + +## Git : qui l'utilise ? + +- Le kernel Linux +- Google, Microsoft, Facebook, Netflix, ... +- PostgreSQL +- Python, perl, Go, swift, typescript, rust, Julia, scala, ... +- ... + + +## Git : qui l'utilise ? + + +![](static/stats.png) + +::: notes + +Où plutôt qui ne l'utilise pas encore ? + +https://insights.stackoverflow.com/trends?tags=git%2Csvn%2Cmercurial%2Ctfs + + +## Historique et alternatives + +- SCCS (1972) (un fichier, local) +- RCS (1982) (un fichier, local) +- CVS (1986) (projet, client-server) +- Subversion (2000) +- BitKeeper (2000) +- Bazaar (2005) +- Mercurial (2005) +- git (2005) +- Fossil (2007) + +::: notes + +Et bien d'autres ! + + +## Git est décentralisé + +Il n'y a ni serveur, ni client : tout le monde est « au même niveau » : + +- On peut travailler seul sur sa machine sur son projet. +- On peut travailler une journée sans connexion à internet. + + +::: notes + +Désambiguer git vs github. J'aime l'analogie : « Git est a Github ce +qu'une photo est a Dropbox ». + + +# Introduction + +Commençons par imaginer un projet : l'écriture d'un livre. + + +## Introduction + +Dans notre projet, `git` va nous simplifier deux choses : + +- La collaboration. +- La journalisation des changements (qui a écrit quoi, quand, pourquoi). + + +## Collaborer + +Sans git : + +> Moi je modifie le fichier les jours pairs et toi les jours impairs. + +Sinon quand on sauvegarde on écrase les modifications de l'autre ! + +::: notes + +Imaginons deux auteurs qui travaillent sur un livre, partagé via +Dropbox dans un format propriétaire binaire... + + +## Conserver l'historique + +Sans git, très vite on se retrouve avec : + +- mybook.tex +- mybook-2.tex +- Copy of mybook-2.tex +- mybook-2-alternative-end.tex +- mybook-2-alternative-end-old.tex +- mybook-final.tex +- mybook-final-1.tex +- mybook-final-final.tex +- mybook-final-preprint.tex +- mybook-final-preprint-corrected.tex +- mybook-final-preprint-corrected-final.tex + + +## Conserver l'historique + +> Pourtant hier soir, quand je suis parti, ça marchait… + +ou : + +> Mais que fait ce code ? Pourquoi il est là ? Qui l'a écrit ? Pourquoi ? + +::: notes + +Leur montrer le premier commit de Python par exemple (dédramatiser sur +la ligne de commande). + + +## Conserver l'historique + +On peut même demander à `git` de nous trouver automatiquement quelle +modification à introduit un bug. + +::: notes + +À condition de pouvoir lui dire : + +- Comment reproduire le bug +- Quelle version est connue pour fonctionner. +- Quelle version est connue pour ne plus fonctionner. + + +## Introduction + +Comment, sans `git`, pourraît-on résoudre ces deux problèmes ? + + +## Analogie + +En prenant une photo du projet (entier) à chaque modification. + +Au dos de la photo on pourrait même rajouter des meta-données : + +- Nom de celui qui a fait la photo. +- Date à laquelle la photo à été prise. +- Pourquoi on a pris la photo à ce moment là. +- Et pourquoi pas une signature ! + + +::: notes + +Le projet c'est par exemple l'assemblage d'un moteur de fusée... + +Attention pas de "zoom sur une morceau", on prend toujours l'objet entier. + + +## Analogie + +Est-ce que ces photos résolvent nos problèmes ? + + +## Analogie + +Pour conserver l'historique, oui, c'est gagné ! + +::: notes + +On aura même un gros carton de photos avec à chaque fois qui à fait +quoi, quand, pourquoi, c'est parfait 50 ans plus tard pour faire de +l'archeologie. + + +## Analogie + +Pour collaborer on va devoir s'envoyer les photos par pigeon voyageur. + +- À chaque fois que je reçois une photo, j'applique la modification à ma version. +- À chaque fois que j'ai terminé une étape, j'envoie une photo de ma progression. + + +## Analogie + +Parfois deux photos vont se contredire, par exemple : + +- Un collègue à essayé de nouveaux injecteurs en cuivre. +- Vous avez essayé de nouveaux injecteurs imprimés en 3D. + +::: notes + +Ça s'appelle un `conflit`, et ce n'est pas grave, ça se résoud, mais +pas toujours automatiquement : il faut parfois qu'un humain choisisse +ce qu'il considère être le mieux. + + +## Analogie + +Je vais devoir prendre une décision : + +- Prendre ma version. +- Prendre sa version. +- Prendre un peu des deux. + +Le résultat sera un nouvel état, encore plus avancé du projet, vite, +je le photographie, je commente au dos de la photo les raisons de ce +choix, et je l'envoie au collègue. + +::: notes + +Ça s'appelle un `merge`. + + +## Analogie + +Mais souvent les séries de photos ne vont pas se contredire : chacun travaille sur sa partie. + +::: notes + +Par exemple vous recevez 42 photos d'un collègue qui a passé une +semaine à travailler sur la turbopompe, il était seul à travailler +dessus, vous appliquez son travail sans conflit. + + +## Analogie + +Catastrophe, une pile de photo est tombée, et personne ne sait comment +remettre ça dans l'ordre. + +Qu'aurai-on pu faire pour ne pas que ça arrive ? + +::: notes + +Écrire au dos la photo "parente" : l'état précédent, c'est ce que fait git. + +Notez qu'une photo peut avoir 0 (initial commt), 1 (normal commit), ou +deux (merge commit) parents. + + +# Avec git + +- « Prendre une photo » c'est « un `commit` », en français c'est « valider ». +- Envoyer des photos c'est « un `push` ». +- Un conflit c'est « un `conflit` ». +- La photo de résolution d'un conflit c'est « un `merge` ». + + +## Avec git + +Comme avec une photo on peut aussi choisir ce qu'on prend, ça +s'appelle le « staging area », la scène dans notre analogie. + + +## Avec git + +Typiquement on ne va pas photographier nos outils, la servante, ... + +Ils ne font pas vraiment partie du projet : on les pousse hors champ à +chaque photo. + +(C'est le `.gitignore`.) + +::: notes + +De toute façons, sur un même projet, on utilise pas tous les mêmes outils. + +## Avec git + +Mais typiquement on va ajouter à la photo chaque nouvelle pièce, dès +sa réception, même si elle n'est pas encore bien branchée. + +(C'est un `git add`.) + + +## Avec git + +Comme avec nos photos, chaque `commit` permet de connaitre l'état +**complet** du projet au moment où il à été fait. + +::: notes + +Insister sur le fait qu'il n'est **pas** nécessaire d'avoir les +commits précédents. + + +# Petite parenthèse CLI + +`git` n'est pas graphique : il nous faut un bon terminal et un bon +shell. + +## Le terminal + +Tout émulateur de terminal sur MacOS et Linux fera l'affaire. + +::: notes + +Oui c'est un abus de langage mais je ne vais pas dire "émulateur de +terminal" à chaque fois. + + +## Le terminal + +Sur Windows : + +- Oubliez `cmd.exe`, ça n'est plus maintenu depuis plus de 10 ans. +- `PowerShell` : sa syntaxe n'est pas standard, mais ça fonctionne. +- WSL : Ça vous ouvrira d'autres portes intéressantes. +- git-for-windows : L'émulateur de terminal est correct, et ça vous apporte bash. + + +## Le shell + +Tout bon shell (bash, zsh, fish, tcsh, ...) fera l'affaire. + +Les caractéristiques d'un bon shell, pour moi, c'est : + +- De bons raccourcis clavier. +- Une bonne auto-complétion. + +::: notes + +Une démo s'impose, au moins Ctrl-e, Ctrl-a, Ctrl-k, Ctrl-y. + + +## Le shell + +Les commandes indispensables : + +- `git` : pour versionner ses projets. +- `ls` : pour `list` directory entries. +- `cd` : pour `change directory`. + +::: notes + +Dans l'ordre dans lequel je les utilise le plus souvent, #truestory. + +Une démo s'impose. + + +## Le shell + +Les commandes utiles : + +- `rm` : pour supprimer. +- `mv` : pour renommer ou déplacer. +- `mkdir` : pour créer un dossier. +- `man` : pour lire le manuel. + +::: notes + +Une démo s'impose. + + +# Installation de git + +Utilisez votre gestionnaire de paquets habituel : + +```text +apt install git # sur Debian +dnf install git # sur Fedora +pkg install git # sur FreeBSD +brew install git # sur MacOS avec homebrew +port install git # sur MacOS avec MacPorts +... +... +``` + +::: notes + +J'aimerai un git >= 2.23. + + +## Installation de git + +Sinon, sur Windows, téléchargez git via : + +https://git-scm.com/download/ + +ou git, un émulateur de terminal, et un shell via : + +https://gitforwindows.org/ + + +## Installation de git + +Est-ce que ça fonctionne chez vous ? + +```text +git --version +``` + + +# Travailler localement + +## `git config` + +Git aura vite besoin de savoir qui vous êtes, on va commencer par lui dire : + + git config --global --edit + + +## `git init` + +À exécuter en étant dans le dossier racine du projet à versionner. + +Cette commande ne fait qu'une chose : créer le dossier `.git`. + + +## Le dossier `.git` + +C'est là où `git` range l'historique du projet, un peu de +configuration, quelques méta-informations, ... + +Petite exploration. + + +## `git clone` + +Sert à télécharger un projet (via HTTP, HTTPS, SSH, ...), essayez : + +```bash +git clone https://github.com/python/cpython +cd cpython +``` + + +## `git log` + +Nous permet de consulter le journal, l'historique du projet : + +- Chercher le travail d'un auteur en particulier. +- Chercher le travail sur un fichier en particulier. +- Chercher les modifications qui contiennent un mot spécifique. +- ... + + +## `git log` + +Trouvez-moi la date du dernier commit de Python 2 via: + +```text +$ git log v2.7.18 +``` + +## `git log` + +Lisez les dernières modifications de cPython via : + +```text +$ git log +``` + + +## `git log` + +Jettez un œil aux dernières modifications de la documentation de Python via : + +```text +$ git log Doc/ +``` + + +## `git log` + +Trouvez-moi le commit le plus récent de Guido van Rossum en utilisant `--author`. + +```text +$ man git log +``` + + +## `git status` + +Vous pouvez abuser de cette commande, elle vous donnera même de bons +conseils : + +```text +$ git status +Sur la branche main +Fichiers non suivis: + (utilisez "git add ..." pour inclure dans ce qui + sera validé) + main.py + README.md +``` + +## `git add` + +Dans notre analogie de la photo c'est
« ajouter à la scène ». + +Ajouter un fichier, ou ajouter une modification, mais ajouter. + +::: notes + +Insister qu'après avoir modifié deux choses, on peut, si on le +souhaite, n'en ajouter qu'une pour faire un premier commit, puis +ajouter la seconde pour faire un second commit. + + +## `git commit` + +Dans notre analogie de la photo, c'est « prendre une photo ». + +Comme dans un manuel pour monter un meuble : on veut prendre une photo à chaque étape +du projet. + + +::: notes + +Rappelez-vous qu'au dos de la photo on a des informations: qui, quand, un commentaire, ... + + +## `git commit` + +Une autre analogie pour `git commit` :
+c'est la sauvegarde dans un jeu vidéo. + +::: notes + +Il vaut mieux en faire un peu trop qu'oublier d'en faire. + +On pourra toujours revenir à une sauvegarde en cas de problème. + + +## `git grep` + +Un substitut à `grep`, cherchons la définition de la fonction `random` : + + git grep "def random(" + +Rapide, pour chercher dans ~200k lignes de code ? + + +## `git diff` + +Dans notre analogie de la photo, `git diff` joue aux sept différences : + +- Entre notre travail et la `staging area`. +- Entre la `staging area` et le dernier commit. +- Entre deux commits. + +::: notes + +Insister sur le fait que git ne stocke pas les différences, il stocke +bien l'état complet du projet, tel une photo. `git diff` nous calcule +donc les différences à la demande. + + +## `git diff` + +Jettons un œil aux différences entre Python 3.10.0rc2 et Python 3.10.0 : + +```text +git diff v3.10.0rc2 v3.10.0 +``` + +## `git show` + +Dans notre analogie de la photo, `git show` nous montre une photo... + +...ou pas : nous afficher le projet entier serait monumental. + + +## `git show` + +`git show` montre surtout les métadonnées, et la différence apportée +par un commit. + +Regardons ce commit : + +```text +git show e1d455f3 +``` + +## Mise en pratique + +Créez un dossier, initialisez git, et dans un fichier disons +`git-day-1.md` prenez des notes. + +Créez quelques commits pour apprivoiser : + +- git add +- git commit +- git log +- git show +- git grep + + +## `git branch` et `git switch` + + + +## `git tag` +## `git switch` +## `git checkout` +## `git rebase -i` +## `git tag` +## `git reset` +## `git mv` + +Ou `git rm` / `git add` ? + +C'est pareil. + +::: notes + +Bonne pratique : Soit on modifie, soit on déplace, mais pas les deux en même temps. + +Commençons avec un seul fichier, seul sur ma machine. + +::: notes + +Faire un `git init`, et quelques `git add` / `git commit`, et beaucoup +de `git status`. Ne pas utiliser ses alias. KISS. + +Puis leur faire faire. + + + +# Travailler ensemble + +## `git remote` + +## `git fetch` + +## `git push` / `git pull` + +## TODO + +- Technique : blob, tree, commit, tag. +- Technique : graphe orienté sans cycles. +- Technique low level : En partant des commandes de base. +- Analogie "photo" : un commit est une photo (donc immuable) de l'état du projet. + - Envoyer la photo par la poste c'est "git push" : une fois poussé il n'y a plus moyen de rien changer. + - Tant qu'elle n'est pas postée, on peut toujours la déchirer. + + +Ajouter https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRMWWzVbPEKe6JITYNNJ0AqsqWQlIxV2KDHxQ&usqp=CAU dans une slide :) diff --git a/git-initiation/static/stats.png b/git-initiation/static/stats.png new file mode 100644 index 0000000..684e96f Binary files /dev/null and b/git-initiation/static/stats.png differ diff --git a/git-initiation/test.py b/git-initiation/test.py new file mode 100755 index 0000000..7d22261 --- /dev/null +++ b/git-initiation/test.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 + +import argparse +from string import ascii_letters, digits +import re +import doctest +from tempfile import NamedTemporaryFile +import importlib.util + +parser = argparse.ArgumentParser() +parser.add_argument("file", nargs="+") +parser.add_argument("-v", "--verbose", action="store_true") +parser.add_argument("-d", "--debug", action="store_true") +args = parser.parse_args() + +for file_to_test in args.file: + with open(file_to_test) as f: + source = f.read() + + with NamedTemporaryFile(mode="w", suffix=".py") as f: + for example_match in re.finditer("```python.*?```", source, re.S): + example = example_match.group() + example = "\n".join(example.split("\n")[1:-1]) + lineno = source[:example_match.start()].count("\n") + 1 + function_name = ''.join(letter if letter in ascii_letters + digits else '_' for letter in file_to_test[:-3]) + "_line_" + str(lineno) + if example.startswith(">>> "): + if '"""' in example: + f.write(f"""def _{function_name}():\n r'''""" + example + """\n'''\n\n""") + else: + f.write(f'''def _{function_name}():\n r"""''' + example + '''\n"""\n\n''') + else: + f.write(example + "\n\n") + f.flush() + if args.debug: + with open(f.name) as py_source: + print(py_source.read()) + spec = importlib.util.spec_from_file_location("to_test", f.name) + to_test = importlib.util.module_from_spec(spec) + spec.loader.exec_module(to_test) + doctest.testmod(to_test, verbose=args.verbose)