2
votes

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

Mon problème

J'ai travaillé sur un dépôt git. Avant de publier mes modifications, j'aimerais supprimer certains commits - a023b43 , 315424b et 7b51754 - car ils ne reflètent aucun changement significatif. Les seuls points importants dans le temps sont le premier ( 70f72ca ) et le dernier ( 6937ddd ) commits.

What Have I Tried

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

J'ai choisi:

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

Le but final est d'avoir un journal git qui ressemble à:

6937ddd CV 2019.01
4c802c1 Initial commit

1 commentaires

À quoi ressemblait le script lorsque vous avez eu des conflits avec le squash?


3 Réponses :


3
votes

Le problème est que git rebase utilise une terminologie contradictoire sur 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 modifications) entre son commit parent et lui-même.

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

Si vous souhaitez supprimer certains instantanés tout en conservant les mêmes instantanés ultérieurs, vous dites à rebase de écraser les validations. Dans les termes de rebase , cela signifie que vous combinez les modifications de plusieurs commits dans le dernier de ces commits.


0 commentaires

3
votes

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

Quel que soit le mécanisme, en supprimant un commit de la liste des les instructions de rebase, vous avez demandé que ces modifications ne soient pas incluses.

Imaginez si vous aviez un fichier, et dans sa révision initiale, il a une seule ligne avec le contenu un . Imaginez que lors du prochain commit, vous changez un en deux . Dans le commit suivant, deux à trois . Puis trois à quatre , et enfin quatre à cinq . Vous avez donc maintenant cinq commits, chacun modifiant cette ligne.

Imaginez que dans chaque commit vous lui ayez donné un message indiquant le changement de contenu, et que, par magie, l'ID de commit (hachage) reflétait également le contenu. Lancer rebase -i --root vous donnerait ce script:

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

En d'autres termes, le premier commit choisit le changement de rien à un . Le second sélectionnera le changement de un à deux , etc., jusqu'à ce que la dernière instruction choisisse le changement de quatre à cinq .

Si vous supprimez certaines de ces lignes, vous donnez:

pick 1111111 add one
pick 5555555 change four to five

Vous avoir deux choix de cerises. Le premier ajoutera un fichier avec le contenu one . Le second essaiera de changer ces contenus de quatre à cinq.

Hélas, ces contenus ne sont pas quatre . Donc, vous avez un conflit.

Si votre objectif est de passer de un à cinq sans commits intermédiaires, alors vous voulez écrasez-les ou marquez-les comme fixup commits.

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

Dans ce cas, le premier changement sera sélectionné, donc le le fichier one sera créé. Le changement suivant sera choisi avec soin, donc le contenu passera de un à deux . Et les modifications ultérieures seront sélectionnées avec soin, mettant à jour le fichier, mais ces modifications seront "écrasées" dans le commit précédent. Ainsi, vous obtiendrez l'intégralité du contenu, mais vous n'obtiendrez que deux commits.

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


0 commentaires

-1
votes

Pour de telles situations, je dois créer mes propres commits

Le but final est d'avoir un journal git qui ressemble à:
6937ddd CV 2019.01
4c802c1 Commit initial

  1. récupère le SHA1 complet du premier commit. Vous en aurez besoin plus tard.
    git rev-parse 70f72ca
  2. Récupérez l'objet de validation de la dernière validation dans un fichier texte.
    git cat-file commit 6937ddd> commit.txt
  3. Modifiez commit.txt. Remplacez la ligne parent de 7b51754 .... par la sortie de 1)
  4. Écrivez l'objet de validation (personnalisé) dans votre base de données git chat commit.txt | git objet de hachage -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 souhaités de votre dépôt avec votre chaîne de commit actuelle. Dans un objet de validation, l'état du dépôt est défini par la ligne tree , donc ici, tout ce que vous faites est de définir explicitement quel état de dépôt suit un autre.


0 commentaires