Où en êtes-vous avec git ? Comment l'utilisez-vous ?
Un gestionnaire de version est un système qui enregistre l’évolution d’un fichier ou d’un ensemble de fichiers au cours du temps de manière à ce qu’on puisse rappeler une version antérieure d’un fichier à tout moment. Avantages et inconvénients : - Système centralisé (CVS, SVN) : unicité du point de panne, online, stockage des données comme des modifications - Système décentralisé (Git) : réplication, offline (local), stockage des données comme des instantanés => vitesse
Un gestionnaire de version est un système qui enregistre l’évolution d’un fichier ou d’un ensemble de fichiers au cours du temps de manière à ce qu’on puisse rappeler une version antérieure d’un fichier à tout moment. Avantages et inconvénients : - Système centralisé (CVS, SVN) : unicité du point de panne, online, stockage des données comme des modifications - Système décentralisé (Git) : réplication, offline (local), stockage des données comme des instantanés => vitesse
Ces commandes permettent de contribuer avec git localement (sur sa machine) à un projet déjà existant. - Obtenir une copie locale du dépôt (_repository_) distant d'un projet : `git clone <url>` - Enregistrer ses modifications dans le dépôt : - Vérifier l'état des fichiers : `git status` - Remplacer un fichier par sa dernière version validée (la version du dernier _commit_) : `git restore <file-or-directory>` :warning: Supprime les modifications locales du fichier - Comparer le contenu de son espace de travail (_working directory_) avec la zone d’index (_staging area_) : `git diff` - Indexer les fichiers modifiés ou de nouveaux fichiers pour validation : `git add <file-or-directory>`, `git add --all` - Désindexer un fichier : `git restore --staged <file-or-directory>` - Comparer les fichiers indexés et le dernier instantané (_snapshot_) : `git diff --staged` - Valider (_commit_) les modifications : `git commit`, `git commit --all` (<=> `git add -A && git commit`) - Visualiser l’historique des _commits_ : `git log`
Git ne stocke pas ses données comme une série de modifications ou de différences successives mais plutôt comme une série d’instantanés (*snapshots*) de l'espace de travail. Ces instantanés sont référencés par des **commits**.
Un **commit** est un pointeur vers un instantané (*snapshot*) - ou, plus exactement, un objet qui contient un pointeur vers un arbre (*tree*), des métadonnées et un pointeur vers son parent.
Notes : - Aucun parent pour le commit initial, plusieurs pour un commit de fusion. - Trois types d'objet : `commit`, `tree`, `blob`. - Un `tag` est un `commit` qui pointe vers un autre `commit` au lieu d'un `tree`.
Une **branche** est un pointeur vers un commit (le plus récent), qui avance automatiquement à chaque commit. `master` ou `main` est le nom de la branche par défaut. `HEAD` est un pointeur vers la branche courante. Ces pointeurs sont stockés dans des fichiers dans `.git/ref/heads`.
- Créer une nouvelle branche : `git branch <branch_name>` - Basculer sur une branche (<=> déplacer `HEAD` + restaurer les fichiers de l'espace de travail dans l'état du dernier instantané de la branche) : `git switch <branch_name>` - Basculer sur une nouvelle branche : `git switch --create <branch_name>` (<=> `git branch <branch_name> && git switch <branch_name>`)
Fusionner la branche `iss53` dans la branche `master`. **Merge** réalise une fusion entre les deux derniers instantanés de chaque branche (C4 et C5) et l’ancêtre commun le plus récent (C2) en créant un commit résultant de cette fusion (_merge commit_).
Dans certains cas simples, git déplace simplement le pointeur de la branche (_fast-forward_). Ce peut être évité en utilisant l'option `--no-ff`.
Réappliquer sur `master` (C3) les _patches_ des modifications introduites sur la branche `experiment` (C4). **Rebase** rejoue les modifications d'une branche sur une autre. **Rebase** crée de nouveaux commits : similaires en contenu mais différents en identité.
- Identifier le(s) fichier(s) en conflit (_unmerged_) : `git status` - Modifier le(s) fichier(s) : Au-dessus de la ligne ``=======`` se trouve la version dans `master`(`HEAD`), en-dessous se trouve la version de la branche `iss53`. Pour résoudre le conflit, il faut choisir une partie ou l’autre ou bien fusionner les contenus (en effaçant les lignes `<<<<<<<`, `=======` et `>>>>>>>`).
- Marquer le(s) fichier(s) comme résolu(s) : `git add <file>` - Pour poursuivre en cas de merge : `git commit` - Pour poursuivre en cas de rebase : `git rebase --continue`
**Clone** crée un pointeur `master` sur une branche locale et un pointeur `origin/master` sur la branche `master` distante. `origin` est le nom par défaut pour un dépôt distant (_remote_).
- Récupérer les nouvelles modifications du dépôt distant : `git fetch origin` (correspond à déplacer le pointeur `origin/master` et les pointeurs des éventuelles autres branches distantes) - Appliquer ces nouvelles modifications dans son espace de travail, sur la branche `master` : - En fusionnant la branche distante `origin/merge` dans sa branche locale `master` : `git merge origin/master` - En rebasant sa branche locale `master` sur la branche distante `origin/master` (à préférer pour un historique plus clair puisqu'évite des commits de fusion non significatifs) : `git rebase origin/master` - Pousser ses propres modifications sur le dépôt distant : `git push origin master` Les commandes restent les mêmes pour n'importe quelle autre branche et n'importe quel autre dépôt distant.
Il existe la commande `git pull` qui permet à la fois de récupérer les modifications et de les appliquer. L'utiliser avec l'option `--rebase` pour rebasage (<=> `git fetch origin && git rebase origin/master`).
Ensemble de bonnes pratiques, recommendations.
Il s'agit du workflow le plus commun sur les forges telles que GitHub et GitLab. D'autres workflows : - Centralized Workflow - Dictator and Lieutenants Workflow - Envoi de patchs par email
- `git config --global user.name "John Doe"` - `git config --global user.email johndoe@example.com` - Configurer le comportement de la commande `pull` (pour rebaser automatiquement :innocent:) : `git config --global pull.rebase preserve` (preserve les commits de merge locaux) => `git pull` <=> `git pull --rebase` - Configurer les caractères de fin de ligne (notamment dans le cas d'un collaboration entre développeurs Unix/Mac et Windows) : - Pour les développeurs Unix/Mac : `git config --global core.autocrlf input` - Pour les développeurs Windows : `git config --global core.autocrlf true`
- Configurer un template de message de commit (_[+ hook de validation]_) : `git config --global commit.template ~/git-template/.gitmessage.txt`
Correction : Résultat visible sur la branche[`feature/answer`](https://github.com/hjonin/formation-git-exercice/tree/feature/answer). 1. `git clone https://github.com/hjonin/formation-git-exercice/` 2. `git switch feature/todo` 3. `git revert HEAD` 4. `git commit --amend` 5. `git cherry-pick <commit_id>` 6. `git add --patch`, commande `s` (pour _split_) pour rafiner le découpage des modifications, puis `git commit` 7. `git stash && git rebase --interactive HEAD~2`, commande `s` (pour _squash_) 8. `git rebase main` 9. `git stash apply` ou `git stash pop` pour supprimer les modifications de la pile 10. `git reset --hard` 11. `git tag the.end`