Git ProTips – HoL @ Devoxx France 2016 – Avant de commencer…



Git ProTips – HoL @ Devoxx France 2016 – Avant de commencer…

1 1


devoxx-git-protips

HoL Git ProTips à Devoxx France 2016

On Github tdd / devoxx-git-protips

Git ProTips

HoL @ Devoxx France 2016

Christophe Porteneuve @porteneuve Delicious Insights

whoami

const christophe = {
  age:        38.459958932238195,
  family:     { wife: 'Élodie', son: 'Maxence' },
  city:       'Paris, FR',
  company:    'Delicious Insights',
  trainings:  ['Git Total', 'JS Total', 'Node.js'],
  gitSince:   '2008-03-28',
  claimsToFame: [
    'Git Attitude',
    'Learning GitHub (O’Reilly)',
    'Mastering GitHub (O’Reilly)'
  ]
}

Avant de commencer…

Petit sondage

Qui parmi vous… (main levée)

…n’a jamais fait de Git ?

…en ligne de commande ?

…ne fait pas de Git au boulot / en prod ?

…a surtout fait du Subversion ?

…n’a jamais fait de gestion de sources ?

…n’a pas Git installé sur sa machine ici ?

…n’a jamais fait de rebase ?

« Faire du SVN avec Git »

L’éternelle histoire du changement de paradigme…

Ou : trimballer sa culture existante dans son nouvel environnement.

Erreur tragique mais ô combien courante

  • Java ➜ Ruby
  • Java ➜ JavaScript / Node
  • SVN ➜ Git
  • SGBD/R ➜ CouchDB/MongoDB
  • etc.

Installer Git

Le guide FR qui va bien

Assurez-vous d’avoir une 2.0 minimum…

…ou en tout cas, vraiment, rien en-dessous de 1.8.5 !

La configuration de Git : globale vs. locale

Globale : ~/.gitconfig, ou ~/.config/git/config

Locale : ~/.git/config

git config [--global] [--replace-all|--unset-all] name [value]
git config [--global] --list

Ah, et juste un mot sur les GUI…

Objectifs & Philosophie

  • Maintenabilité
    • Graphe lisible + pertinent
    • Commits atomiques, homogènes, compréhensibles, peu défectueux
    • Workflow propre
  • Tolérance à l’erreur
  • Tolérance à l’humain 😉

1. Bien pondre un commit

Un meilleur statut

Votre meilleur ami. Affiche plein de trucs utiles.

Réflexe absolu avant un commit, suite à un conflit…

git config --global alias.st status
git config --global status.showUntrackedFiles all
git config --global color.ui auto
git st

Un meilleur diff

L’outil fondamental, mais grave sous-utilisé.

Intégré à de nombreux autres, options réutilisables (log, show…)

git diff
git config --global diff.mnemonicPrefix true
git config --global diff.renames true
git diff -w
git diff --word-diff
git config --global diff.wordRegex .
git diff --staged
git add -N path
git show :0:path
git diff --stat
git diff --dirstat[=n]

Un diff personnalisé

Allez, on y va on se lâche.

Z’avez remarqué comme c’est pas pratique pour le copier-coller ? Comme on voit mal les limites de fichier ?

Custom diffing to the rescue!

npm install -g diff-so-fancy
# ou, sur OSX avec Homebrew mais sans npm : brew update + brew install diff-so-fancy

git config --global color.diff always
git config --global color.diff.meta yellow bold
git config --global color.diff.frag magenta bold
git config --global color.diff.whitespace red reverse
git config --global pager.diff "diff-so-fancy | less --tabs=2 -RFX"
git config --global pager.show "diff-so-fancy | less --tabs=2 -RFX"

T’es comme le H de Hawaii…

Y’a tellement de motifs à ignorer sur un projet…

Rien qu’avec Visual Studio, JetBrains ou Rails, ça envoie du lourd…

Gitignore.io est juste trop cool.

…et on cale son .gitignore avec (idéalement dès le 1er commit)

# …
git add .gitignore
git commit -m 'Initial commit'
git add rules.log
The following paths are ignored by one of your .gitignore files:
rules.log
Use -f if you really want to add them.
git add -f rules.log

On me voit, on me voit pas, on me voit un peu.

Rappel critique : le stage / l’index.

On unstage avec un reset :

git reset path…

Le cas hyper fréquent du staging partiel de fichier :

git add -p path…

C’est (enfin) l’heure du commit !

Mais en vrai, soyons clairs :

Commit Often, Push Sparingly
git config --global alias.ci commit
git ci -m "message"
git show [--stat|--dirstat|-w] [ref]

Ah bah oui mais non.

L’inattention, tout ça…

git rm --cached path
git reset HEAD~1 path
git ci --amend [-m|--no-edit]
git config --global alias.oops 'commit --amend --no-edit'

2. Un meilleur log

Loguer quelles parties du graphe ?

LOG_FORMAT='%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%an %ad)%Creset'
git config --global alias.lg "log --graph --date=relative --pretty=tformat:'$LOG_FORMAT'"
git config --global log.abbrevCommit true
git config --global log.follow true

Le log remonte le temps à partir d’un ou plusieurs points.

Le plus récent d’abord (chronologique inverse).

git lg [ref…] [^neg-ref…]
git lg ref..
git lg [--branches[=glob]|--tags[=glob]|--all] [--exclude=glob]
git lg [--right-only|--left-only] ref1...ref2
git lg -n

Filtrages du log basés métadonnées

git lg --grep="message regex" [--invert-grep] [--all-match] [-i]
git lg --author="author regex" [-i]
git config --global grep.extendedRegexp true
git lg --[no-]merges

Filtrages du log basés contenu

git lg … [--] path…
git lg -S "texte en diff actif" [--pickaxe-regex]   # Mode pickaxe
git lg -G "regex en diff actif"
git lg -L ':regex:path'
git lg -L 'startline,endline:path'
git lg --diff-filter=pattern                        # Combo de A, C, D, M, R, T…

Le diff est intégré

…et toutes ses options habituelles sont dispo.

git lg -p [-w] [--word-diff]…       # Diff classique
git lg [--stat|--dirstat|--numstat] # etc.

3. Nettoyer une branche

Rebase interactif : le principe

Qu’est-ce qu’un rebase ?

À quoi sert le rebase interactif ?

Aparté : quels autres types de rebase ?

Lancer un rebase interactif

git rebase -i [latest-ok=@{u}]
git rebase -i --root

Quelles actions possibles ?

Verbe Intention Verbe Intention pick commit tel quel (cherry pick) reword ajustement message squash fusion code/message avec le commit précédent fixup fusion du code seul avec le commit précédent drop* commit ignoré edit ajustement manuel

Réordonner les lignes réordonne les commits. Et comme toujours, script vide (hors commentaires) = abandon de commande.

En cours de rebase interactif

Git nous donne la main lors d’un conflit :

git st
# résolution fichier par fichier, avec à chaque fois :
git add path
# et au final :
git rebase --continue
# ou si ça donne un arbitrage vide par rapport à la cible :
git rebase --skip

Git nous donne aussi la main sur un edit. On fait ce qu’on veut, et au final, pareil que pour un conflit.

Si on s’empêtre et qu’on veut juste lâcher l’affaire :

git rebase --abort

Attentifs, ensemble

curl -O http://tdd.github.io/devoxx-git-protips/playground.zip
unzip -q playground.zip
cd devoxx-git-protips-playground
git lg --all

On va se faire la plupart des besoins :

  • Réordonnancement
  • Suppression
  • Fusion
  • Changement de message
  • Découpage (boss de fin de niveau !)

Anticiper le rebase à venir

git commit --fixup=ref
git commit --squash=ref
git rebase -i --autosquash

4. Les pièges de push & pull

Côté push

Quels pièges ?

git config --global push.default upstream
git config --global push.followTags true
git push -u remote ref…
git branch set-upstream-to=remote/ref [branch]

Côté pull

fetch vs. pull

Quels pièges ?

git config --global pull.rebase preserve
git config --global fetch.prune false
git remote prune remote

Workflows et impact sur la dynamique de synchro.

« Sync » ?

5. Arbitrer un conflit

Ça ne fait pas mal

Il suffit de suivre la méthodologie qui va bien.

Elle est dans git help merge, je voudrais pas dire, hein…

En amont :

git config --global core.whitespace '-trailing'
git config --global merge.conflictStyle diff3
git config --global merge.log true # ou un nombre de commits, par défaut 25
git config --global merge.ff false

Puis dans les grandes lignes, ça donne :

git st                        # Quel est l’étendue des dommages ?
git mergetool path            # Si on a+veut un, pour le fichier concerné
git add path                  # Fichier par fichier, après l’avoir arbitré
git commit [--no-edit]        # Une fois que tout est staged

Arbitrer un fichier précis

Un conflit, ça se traite fichier par fichier.

# Qui est le prochain coupable ?
git st
# On édite le fichier en direct, ou si on a un bon outil dédié configuré…
git mergetool path
# Une fois que les conflits sont réglés, on le dit :
git add path
# Si on a mal arbitré (notammment mergetool) ou que le style de marqueurs nous gêne :
git checkout --conflict=(merge|diff3) path
# Besoin de voir un des 3 snapshots sans mergetool ?
git show (ref|MERGE_HEAD|HEAD|:1):path
# Envie de récupérer le fichier tel quel depuis une des branches, ou ailleurs ?
git checkout ref [--] path

rerere

REuse REcorded REsolution. OoOoOOOoh, ça vend du rêve.

git config --global rerere.enabled true
git config --global rerere.autoupdate true # seulement pour les gens attentifs !
git rerere remaining

En cas de besoin…

git rerere forget path
git rerere clear

Note de workflow : ça coupe bien l’herbe sous le pied au principal argument du « rebase tout le temps sur le dernier master »… Parlons donc des merges de contrôle.

Merci !

Et que Git soit avec vous

Christophe Porteneuve

@porteneuve

Les articles de fond qui vont bien

La formation qui tue tout

Les slides sont sur bit.ly/devoxx-git

#DevoxxFR / #Git

Git ProTips HoL @ Devoxx France 2016 Christophe Porteneuve @porteneuve Delicious Insights