2 votes

Pourquoi les commits abandonnés d'un rebasement git provoquent-ils des conflits de fusion ?

Mon problème

J'ai travaillé sur un dépôt git. Avant de publier mes modifications, je voudrais supprimer certains commits - a023b43 , 315424b y 7b51754 - car ils ne reflètent pas de changements significatifs. Les seuls points importants dans le temps sont les premiers ( 70f72ca ) et le dernier ( 6937ddd ) commits.

Qu'est-ce que j'ai essayé ?

git rebase -i 4c802c1

J'ai choisi :

pick 70f72ca Remove repetitions from Makefile
d a023b43 Separate target for image conversion
d 315424b Remove image conversion (#1)
d 7b51754 Fix Makefile
pick 6937ddd CV 2019.01

L'objectif final est d'avoir un journal git qui ressemble à ça :

6937ddd CV 2019.01
4c802c1 Initial commit

Ce qui n'a pas marché

Auto-merging src/Adam_Matan.tex
CONFLICT (content): Merge conflict in src/Adam_Matan.tex
error: could not apply 6937ddd... CV 2019.01

Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".

Could not apply 6937ddd... CV 2019.01

Si je comprends bien, les deux commits sont juste des instantanés de mon répertoire de travail. Je veux avoir deux instantanés - le commit d'origine et le CV 2019.01 un. Je ne veux pas les fusionner ou les combiner de quelque manière que ce soit. Pourquoi est-ce que je reçois un message de conflit de fusion ?

Ma question

Comment puis-je supprimer tous les commits intermédiaires d'une branche, et ne laisser que le premier et le dernier commits ?

Mise à jour

L'écrasement crée également des conflits de fusion :

git rebase -i 4c802c1
Auto-merging Makefile
CONFLICT (content): Merge conflict in Makefile
error: could not apply 315424b... Remove image conversion (#1)

3voto

Mark Adelsberger Points 20504

Le problème est que git rebase utilise une terminologie contradictoire avec la façon dont git dans son ensemble définit les commits. Un commit est un instantané, mais rebase traite un commit comme un patch (un ensemble de changements) entre son commit parent et lui-même.

Donc si vous dites rebase pour "supprimer un commit", il pense que vous voulez annuler les changements appliqués par le patch de ce commit.

Si vous voulez supprimer certains instantanés tout en conservant les instantanés ultérieurs, vous pouvez dire à rebase à squash les engagements. Dans rebase En termes simples, cela signifie que vous combinez les changements de plusieurs commits dans le dernier de ces commits.

3voto

Edward Thomson Points 21899

Rebase agit en prenant une série de commits et en les ré-appliquant, peut-être sur un objet "de base" différent. Il le fait soit en créant un patch pour chaque commit, et en les ré-appliquant, soit en les sélectionnant dans l'ordre.

Quel que soit le mécanisme, en enlever un commit de la liste des instructions de rebasement, vous avez demandé ces changements pas à inclure.

Imaginez que vous ayez un fichier, et que dans sa révision initiale, il comporte une seule ligne avec le contenu suivant one . Imaginez que dans le prochain commit, vous modifiez one à two . Dans le commit suivant, two à three . Ensuite, three à four et enfin four à five . Vous avez donc maintenant cinq commits, chacun modifiant cette ligne.

Imaginez que dans chaque commit vous lui donniez un message indiquant le changement de contenu, et que magiquement l'ID du commit (hash) reflète aussi le contenu. Exécution de rebase -i --root vous donnerait ce script :

pick 1111111 add one
pick 2222222 change one to two
pick 3333333 change two to three
pick 4444444 change three to four
pick 5555555 change four to five

En d'autres termes, le premier commit prend le changement de rien à one . La deuxième sélectionne le changement de one à two etc jusqu'à ce que la dernière instruction choisisse le changement à partir de four à five .

Si vous supprimer certaines de ces lignes, en vous donnant :

pick 1111111 add one
pick 5555555 change four to five

Vous aurez deux choix de cerises. Le premier ajoutera un fichier avec le contenu one . Le second essaiera de changer ces contenus de four à five .

Hélas, ces contenus ne sont pas four . Vous avez donc un conflit.

Si votre objectif est de passer de one à five sans commits intermédiaires, alors vous voulez squash ou les marquer comme fixup s'engage.

pick 1111111 add one
pick 2222222 change one to two
squash 3333333 change two to three
squash 4444444 change three to four
squash 5555555 change four to five

Dans ce cas, le premier changement sera sélectionné, donc le fichier one sera créé. Les modifications ultérieures seront choisies au hasard, de sorte que le contenu sera changé de one à two . Et les modifications suivantes seront sélectionnées, mettant à jour le fichier, mais ces modifications seront "écrasées" dans le commit précédent. Ainsi vous obtiendrez le contenu complet, mais vous n'aurez que deux commits.

(Si vous les aviez marqués comme fixup vous obtiendrez le même contenu, mais leurs messages de commit ne seront pas inclus dans votre message de commit suggéré.

-1voto

Ben Points 950

Pour des situations comme celle-ci, je dois créer mes propres commits à la main.

L'objectif final est d'avoir un journal git qui ressemble à ça :
6937ddd CV 2019.01
4c802c1 Engagement initial

  1. obtenir le SHA1 complet du premier commit. Vous en aurez besoin plus tard.
    git rev-parse 70f72ca
  2. Récupère l'objet commit du dernier commit dans un fichier texte.
    git cat-file commit 6937ddd > commit.txt
  3. Modifiez le fichier commit.txt. Changez le parent ligne de 7b51754.... à la sortie de 1)
  4. Ecrire l'objet commit (personnalisé) dans votre base de données git. cat commit.txt | git hash-object -t commit --stdin -w
  5. Tag/branch/reset --hard le SHA1 que la commande ci-dessus imprime. Vous devriez voir l'historique souhaité.

La raison pour laquelle cela fonctionne est que vous avez déjà créé les états désirés de votre repo avec votre chaîne de commit actuelle. Dans un objet commit, l'état du repo est défini par l'élément tree donc ici, tout ce que vous faites est de définir explicitement quel état de repo suit un autre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X