Christophe Porteneuve @porteneuve
Delicious Insights
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)'
]
}
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 ?
L’éternelle histoire du changement de paradigme…
Ou : trimballer sa culture existante dans son nouvel environnement.
Erreur tragique mais ô combien courante
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…
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
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]
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"
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
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…
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]
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'
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
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
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…
…et toutes ses options habituelles sont dispo.
git lg -p [-w] [--word-diff]… # Diff classique
git lg [--stat|--dirstat|--numstat] # etc.
Qu’est-ce qu’un rebase ?
À quoi sert le rebase interactif ?
Aparté : quels autres types de rebase ?
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.
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
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 :
git commit --fixup=ref
git commit --squash=ref
git rebase -i --autosquash
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]
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 » ?
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
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
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.
Christophe Porteneuve
Les articles de fond qui vont bien
Les slides sont sur bit.ly/devoxx-git
#DevoxxFR / #Git