Comprendre git
image/svg+xml
Comprendre git
Jérémy Soulary
Résolution de
conflits de merge
Remotes
Sens des merges
Recap
Bases
Des questions ?
Naviguer dans
le graphe git
Index / WT
snapshot du FS
lien vers le
commit parent
Metadata
(commentaire,
auteur, date)
31b2d8
31b2d8
Les branches origin/*
develop de Bob
vu depuis chez Bob
(develop)
develop de gitlab
vu depuis chez Bob
(origin/develop)
= dernier états connus des
branches du serveur.
Pour les mettre à jour,
il faut faire un fetch (ou un pull)
develop de Bob
vu depuis chez Bob
(develop)
develop de gitlab
vu depuis chez Bob
(origin/develop)
develop de gitlab
vu depuis gitlab
(develop réel)
origin/develop est maintenant
un ancêtre de develop de Bob
Bob peut maintenant push develop
Le fast forward est ce qu'il se passe lorsque on pull
quand on n'a pas fait de nouveaux commit
origin/develop
develop
origin/develop
develop
pull de develop
V
C
V
C
Fast Forward de C sur V
Si l'on souhaite merger V dans C,
au lieu de créer un nouveau commit de merge,
on peut avancer le pointeur de C sur V
= avancer un pointeur de branche
vers un commit descendant
Fast Forward
Remote
develop de Bob
develop de Nat
develop de gitlab
develop de Pat
Car git est décentralisé
Tout le monde a des branches différentes !
Mais les mêmes commits !
garantie par les SHA-1
=> la branche develop du serveur pointera
sur le même commit de develop de Bob
develop de Bob
vu depuis chez Bob
(develop)
develop de gitlab
vu depuis chez Bob
(origin/develop)
develop de gitlab
vu depuis gitlab
(develop réel)
la branche origin/develop
de Bob est aussi mise à jour
push = déplacer la branche du serveur
sur le même commit que notre branche
develop de Bob
vu depuis chez Bob
(develop)
develop de gitlab
vu depuis chez Bob
(origin/develop)
develop de gitlab
vu depuis gitlab
Réalité
Avant le fetch :
merge de origin/develop dans develop
develop de gitlab
vu depuis gitlab
develop de Bob
vu depuis chez Bob
(develop)
develop de gitlab
vu depuis chez Bob
(origin/develop)
Après le fetch :
fetch
develop de Bob
vu depuis chez Bob
(develop)
develop de gitlab
vu depuis chez Bob
(origin/develop)
develop de gitlab
vu depuis gitlab
(develop réel)
Il doit alors pull pour:
-se mettre à jour
-merger origin/develop dans develop
pull
pull = fetch + merge
Un commit pourra être perdu si :
- plus atteignable depuis une branche ou un tag
- créé il y a plus de 2 semaines
Si Bob veut push (son) develop,
le serveur va refuser le push.
push possible seulement si
la branche du serveur est un ancêtre
develop de Bob
vu depuis chez Bob
(develop)
develop de gitlab
vu depuis chez Bob
(origin/develop)
develop de gitlab
vu depuis gitlab
=> si on peut l'atteindre en
suivant les flèches
push = déplacer la branche du serveur
sur le même commit que notre branche
Si l'inverse était autorisé, nous pourrions perdre des commits :
develop de Bob
vu depuis chez Bob
(develop)
develop de gitlab
vu depuis gitlab
Récap
-git = gestion de graphe
-commit = snapshot, garantie par le SHA-1
-branche et tags = pointeurs
-un merge = commit comme les autres
-diff d'un merge => par rapport au 1er parent
-origin/* = derniers états connus du serveur
-push = déplacer la banche du serveur
-sens des merges important pour la lisibilité
-on ne modifie que la branche courante
Lorsqu'il y a un conflit,
git ajoute des marqueurs spécifiques
dans les fichiers en conflit
Pour résoudre le fichier et le mettre dans un état correct,
il faut utiliser des outils
un bête éditeur de texte
très hasardeux, et peu pratique !
Un outil dédié aux merges, à 3 vues
Version
de la branche
courante
Version
commune
Version
de la branche
à merger
Comme celui d'IntelliJ :
Version
de la branche
courante
Version
commune
Version
de la branche
à merger
Version mergée
Un outil dédié aux merges, à 4 vues
Comme P4Merge :
Un outil de diff classique à 2 vues
Comme celui d'Eclipse (par exemple) :
Version
de la branche
courante
Version
de la branche
à merger
Mais 2 vues ne suffisent pas
surtout si on ne connaît pas le code à merger.
Ce qui arrive facilement si nous sommes plusieurs
à travailler sur une branche de feature par exemple.
Comment savoir ici quel est la version garder de ces lignes ?
p4merge résout les conflits "évidents",
seuls ceux en rouge sont à résoudre à la main.
Version
de la branche
courante
Version
commune
Version
de la branche
à merger
Version mergée
graphe qui rappelle
les versions affichées
Il faut éditer les parties en rouge,
et choisir quelle version garder,
ou écrire à la main la version mergée
}
Code couleur et forme des différentes parties.
Un clic sur les icônes conservera ou supprimera
les parties concernées.
partie modifiée
dans la branche
courante
partie modifiée
dans la branche
à merger
4 parties ça fait beaucoup ! mais on comprend ce qu'il s'est passé...
Et donc, concrètement ?
Si un merge rencontre un conflit, le merge est stoppé
il faut lancer mergetool (depuis l'IDE ou la CLI)
p4merge va ouvrir chaque fichier en conflit
une fois résolu, il faut commiter le merge
Lorsque les fichiers en conflits ont été résolus,
il faut commiter pour terminer le merge
snapshot
du FS
lien vers les
parents
Metadata
(commentaire,
auteur, date)
Le merge a "mélangé" les 2 commits,
pour créer un nouveau commit qui
va lui aussi stocker une snapshot, tout
comme n'importe quel commit
Ce n'est pas un patch qui est stocké !
Note : pour résoudre un merge correctement,
il peut être nécessaire de modifier d'autres
fichiers que ceux qui sont en conflit.
Par ex, un refactor d'un coté, un ajout de l'autre
Si nous reprenons l'exemple concret, plus haut :
Ici, on peut comprendre que c'est dans la branche à merger (à droite), que les noms
de variables ont été modifiés. C'est la version verte que l'ont doit conserver.
Les 2 commits ci dessous sont identiques (leur FS)
mais leur diff est très différent :
Mais cela impact pas mal la lisibilité de l'historique
Le sens des merges
=> presque le même commit
seul l'ordre des parents sera différent
En effet : diff avec le premier parent dans les outils
tout le vert que
n'avait pas bleu
tout le bleu que
n'avait pas vert
Pourquoi il ne faut pas travailler dans develop...
... si l'on souhaite garder une lisibilité de l'historique
develop
origin/develop
merge créé après un pull
Le premier parent de
ce merge est à nous
Le diff du merge correspondra
alors à tous les commits que
nous n'avons pas écrit.
le diff d'un merge affiche tout ce que
la branche mergée apporte,
on souhaite ajouter du travaille
dans le develop du serveur, pas l'inverse
Nous sommes l'auteur de ce commit de merge
mais pas des modifications affichées dans le diff !
on ne saura pas facilement quand notre travail a été intégré dans develop (du serveur) :
car notre merge n'appraraitra pas dans l'historique des fichiers que l'on a modifiés.
(cas de bug commité en aout, intégré en septembre dans dévelop, dans le mauvais sens)
Autres choses importantes à comprendre
- on ne modifie que la branche courante (sauf cmd particulières)
=> par ex : l'on merge dans la branche courante
- des commandes sont ambiguës : reset et checkout
avec fichiers
sans fichiers
checkout
reset
changer la version
d'un fichier
changer de branche
changer la version
d'un fichier dans l'index
déplacer le pointeur
de la branche
rebase
bisect
L'index
Plein d'autres choses à découvrir
cherry-pick
log
difftool
gitlab-ci
git-svn
submodule
stash
revert
reflog
gc & prune
filter-branch
blame
archive
imerge
Comprendre git !
pour mieux travailler avec
Auteur: Jérémy Soulary
snapshot du FS
lien vers le
commit parent
Metadata
(commentaire,
auteur, date)
d95148
=
→ pointeur vers la référence actuelle
généralement une branche, sinon on est en "tête détachée"
Commit sur master
Commit en mode "tête détachée"
FeatureB
master
HEAD
FeatureB
master
HEAD
FeatureB
FeatureB
HEAD
master
HEAD
master
HEAD = master
HEAD = master
HEAD = d3a4b
HEAD = 4cae6
d3a4b
d3a4b
4cae6
d3a4b
d3a4b
4cae6
git prune
à la demande ou automatiquement tout les 30 jours, git fait le menage
Tous les commits non atteingnables depuis une référence sont supprimés
HEAD, branches et tags
master
HEAD
FeatureB
Commit perdus
"Commit atteingnable" = en suivant les flêches
checkout
master
HEAD
FeatureB
master
HEAD
FeatureB
git checkout FeatureB
HEAD = master
HEAD = FeatureB
reset
master
HEAD
FeatureB
FeatureB
git reset FeatureB
HEAD = master
HEAD = master
master
HEAD
L'index
Le répertoire de travail
Les outils
status
stash
cherry-pick
diff
rebase
log
blame
amend
revert
"fix develop 1"
par @toto
le 09/
11
/2017
à 09:12:44
master
HEAD
FeatureB
- les SHA-1 : 4cae6, d3a4b
- les branches : master, FeatureB
- les branches distantes : origin/master
- les tags : v2.0.4
- HEAD
- les parents <ref>^N : master^1, 4cae6^2
- les ancètres <ref>~N : master~4, 4cae6~6
d3a4b
4cae6
d3a4b
Les références
master~4
4cae6^2
master^1
- la ligne de commande
- outils intégrés aux IDE
- gitk
- git gui
- gitkraken
- gitg
- git-cola
- ...
= état du projet qui sera sauvegardé dans le prochain commit
on peut l'imaginer comme un zip créé en se basant sur l'état
du projet lors du dernier commit
"git add fileA.txt" → on ajoute un nouveau fichier ou des modifications
"git rm fileA.txt" → on supprime le fichier
"git reset master -- fileA.txt" → on utilise la version de master pour ce fichier
Lorsque l'on change de branche, l'index est mis à jour
pour correspondre à l'état stocké par le commit pointé
Lorsque l'on change de branche, le répertoire de travail est
mis à jour pour correspondre à l'état stocké par le commit pointé
"git checkout master -- fileA.txt" → on utilise la version de master
pour ce fichier dans le répertoire de travail
= état du projet "visible" dans notre exporateur de fichier
Présentation créée avec Sozi
t
Bouton
droit
Bouton
gauche
Bouton
du milieu
Gitlab
P4Merge
Gitlab affiche l'essentiel mais le contexte peut etre utile :
il ne faut pas choisir la bleue ici avec Gitlab, car il aura
supprimé automatiquement la ligne 14, qui est utilisée
dans la version bleue
Résolution des conflits depuis Gitlab
Résultat en
choisissant
"theirs"
P4Merge
Intellij
vous
êtes
ici
HEAD
= changer de branche
= téléporter la branche
Pour résoudre le merge,
git cherche le premier
ancêtre communs
aux commits à merger
Sinon, il y a alors un conflit :
il faut résoudre ce conflit manuellement
Si, aucun fichier n'a été modifié
sur ces 2 chemins en même temps,
git fait le "mélange" des 2 commits
automatiquement
merge = commit normal mais avec
plusieurs commits parents
il contient donc également :
-une snapshot du FS
-des metadata (commentaire, ...)
v2.0.3
tag = pointeur vers commit
le pointeur d'un tag ne
changera jamais
(sauf suppression)
Seulement aussi !
develop
FeatureB
FeatureA
branche = pointeur vers commit
nouveau commit
=> le pointeur avance
Seulement !
Nous pouvons créer une branche
n'importe quand et sur
n'importe quel commit
acbcf4cb6c
e7797bf6df
25594c6fc5
396fe55278
.git/refs/heads/develop
31b2d8
"fix develop 2"
par @toto
le 09/
11
/2017
à 09:12:44
701e96
SHA-1 = le code
barre du commit
d95148
le SHA-1 est calculé en fonction de
tout ce qui compose le commit
un commit
=
Même si un seul fichier est modifié,
tout l'état du FS est sauvegardé.
Un commit n'est pas un patch !
701e96
Flèches vers le(s)
commit(s) parent(s)
git = gestion de graphe
Temps
commiter = créer un nouveau noeud + lien(s) vers le(s) parent(s)
État du projet
1
Titre
Graphe
commit
SHA1
rewrite
branch
tag
merge
resol
conflit
text
2vues
2vueszoom
3vues
4vues
gitlab-conflits1
gitlab-conflits2
gitlab-conflits3
p4merge
p4mergezoom
conflit-resol
conflit-resol-zoom
merge-resume
sens-merge
sens-merge-diff
develop-checkout
head
prune
checkout
reset
references
remote
origin
avant-fetch
apres-fetch
push-refused
push-refused-detail
pull
pull-dezoom
push-ok
push-after
ff
ff-pull
index
wt
outils
div
recap
next
questions