Nous avons un dépôt Git avec plus de 400 commits, dont les deux premières douzaines ont été beaucoup d'essais et d'erreurs. Nous voulons nettoyer ces commits en en réduisant un grand nombre en un seul. Naturellement, git-rebase semble être la voie à suivre. Mon problème est que cela aboutit à des conflits de fusion, et que ces conflits ne sont pas faciles à résoudre. Je ne comprends pas pourquoi il devrait y avoir des conflits du tout, puisque je ne fais qu'écraser des commits (sans les supprimer ou les réarranger). Très probablement, cela démontre que je ne comprends pas complètement comment git-rebase fait ses écrasements.
Voici une version modifiée des scripts que j'utilise :
repo_squash.sh (c'est le script qui est réellement exécuté) :
rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
GIT_EDITOR=../repo_squash_helper.sh git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
repo_squash_helper.sh (ce script est utilisé uniquement par repo_squash.sh) :
if grep -q "pick " $1
then
# cp $1 ../repo_squash_history.txt
# emacs -nw $1
sed -f ../repo_squash_list.txt < $1 > $1.tmp
mv $1.tmp $1
else
if grep -q "initial import" $1
then
cp ../repo_squash_new_message1.txt $1
elif grep -q "fixing bad import" $1
then
cp ../repo_squash_new_message2.txt $1
else
emacs -nw $1
fi
fi
repo_squash_list.txt : (ce fichier est utilisé seulement par repo_squash_helper.sh)
# Initial import
s/pick \(251a190\)/squash \1/g
# Leaving "Needed subdir" for now
# Fixing bad import
s/pick \(46c41d1\)/squash \1/g
s/pick \(5d7agf2\)/squash \1/g
s/pick \(3da63ed\)/squash \1/g
Je laisse le contenu du "nouveau message" à votre imagination. Initialement, j'ai fait cela sans l'option "--strategy theirs" (c'est-à-dire en utilisant la stratégie par défaut, qui si je comprends bien la documentation est récursive, mais je ne suis pas sûr de la stratégie récursive utilisée), et cela n'a pas non plus fonctionné. De plus, je dois signaler que, en utilisant le code commenté dans repo_squash_helper.sh, j'ai sauvegardé le fichier original sur lequel le sed script fonctionne et j'ai exécuté le sed script contre lui pour m'assurer qu'il faisait ce que je voulais qu'il fasse (il le faisait). Encore une fois, je ne sais même pas pourquoi il y a serait Il n'y a pas de conflit, donc la stratégie utilisée ne semble pas avoir beaucoup d'importance. Tout conseil ou avis serait utile, mais je veux surtout que l'écrasement fonctionne.
Mis à jour avec des informations supplémentaires provenant d'une discussion avec Jefromi :
Avant de travailler sur notre "vrai" référentiel massif, j'ai utilisé des scripts similaires sur un référentiel de test. C'était un référentiel très simple et le test a fonctionné proprement.
Le message que j'obtiens lorsqu'il échoue est le suivant :
Finished one cherry-pick.
# Not currently on any branch.
nothing to commit (working directory clean)
Could not apply 66c45e2... Needed subdir
C'est le premier choix après le premier engagement du squash. Exécution de git status
donne un répertoire de travail propre. Si je fais ensuite un git rebase --continue
J'obtiens un message très similaire après quelques commandes supplémentaires. Si je le refais, j'obtiens un autre message très similaire après quelques dizaines de commits. Si je le fais encore, cette fois-ci, il passe par une centaine de commits, et donne ce message :
Automatic cherry-pick failed. After resolving the conflicts,
mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'
Could not apply f1de3bc... Incremental
Si je lance ensuite git status
j'obtiens :
# Not currently on any branch.
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: repo/file_A.cpp
# modified: repo/file_B.cpp
#
# Unmerged paths:
# (use "git reset HEAD <file>..." to unstage)
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: repo/file_X.cpp
#
# Changed but not updated:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: repo/file_Z.imp
La partie "les deux modifiés" me semble bizarre, puisque c'était juste le résultat d'un choix. Il est également intéressant de noter que si je regarde le "conflit", il se résume à une seule ligne avec une version commençant par un caractère [tab], et l'autre par quatre espaces. Cela semblait être un problème avec la façon dont j'ai configuré mon fichier de configuration, mais il n'y a rien de tel. (J'ai bien noté que core.ignorecase est réglé sur true, mais il est évident que git-clone l'a fait automatiquement. Je ne suis pas complètement surpris par cela étant donné que la source originale était sur une machine Windows).
Si je corrige manuellement file_X.cpp, il échoue peu de temps après avec un autre conflit, cette fois entre un fichier (CMakeLists.txt) qu'une version pense devoir exister et une autre version pense ne pas devoir exister. Si je corrige ce conflit en disant que je veux ce fichier (ce que je fais), quelques commits plus tard j'obtiens un autre conflit (dans ce même fichier) où maintenant il y a des changements plutôt non triviaux. Il ne s'agit encore que d'environ 25% du chemin à travers les conflits.
Je dois également souligner, puisque cela peut être très important, que ce projet a commencé dans un dépôt svn. Cet historique initial a très probablement été importé de ce dépôt svn.
Mise à jour n°2 :
Sur un coup de tête (influencé par les commentaires de Jefromi), j'ai décidé de faire le changement de mon repo_squash.sh pour être :
rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
Et puis, j'ai juste accepté les entrées originales, telles quelles. C'est-à-dire que le "rebase" n'aurait pas dû changer grand-chose. Cela a abouti aux mêmes résultats que ceux décrits précédemment.
Mise à jour n°3 :
Sinon, si j'omets la stratégie et remplace la dernière commande par :
git rebase -i bd6a09a484b8230d0810e6689cf08a24f26f287a
Je n'ai plus les problèmes de rebasement "rien à commettre", mais je reste avec les autres conflits.
Mise à jour avec un dépôt de jouets qui recrée le problème :
test_squash.sh (c'est le fichier que vous exécutez réellement) :
#========================================================
# Initialize directories
#========================================================
rm -rf test_squash/ test_squash_clone/
mkdir -p test_squash
mkdir -p test_squash_clone
#========================================================
#========================================================
# Create repository with history
#========================================================
cd test_squash/
git init
echo "README">README
git add README
git commit -m"Initial commit: can't easily access for rebasing"
echo "Line 1">test_file.txt
git add test_file.txt
git commit -m"Created single line file"
echo "Line 2">>test_file.txt
git add test_file.txt
git commit -m"Meant for it to be two lines"
git checkout -b dev
echo Meaningful code>new_file.txt
git add new_file.txt
git commit -m"Meaningful commit"
git checkout master
echo Conflicting meaningful code>new_file.txt
git add new_file.txt
git commit -m"Conflicting meaningful commit"
# This will conflict
git merge dev
# Fixes conflict
echo Merged meaningful code>new_file.txt
git add new_file.txt
git commit -m"Merged dev with master"
cd ..
#========================================================
# Save off a clone of the repository prior to squashing
#========================================================
git clone test_squash test_squash_clone
#========================================================
#========================================================
# Do the squash
#========================================================
cd test_squash
GIT_EDITOR=../test_squash_helper.sh git rebase -i HEAD@{7}
#========================================================
#========================================================
# Show the results
#========================================================
git log
git gc
git reflog
#========================================================
test_squash_helper.sh (utilisé par test_sqash.sh) :
# If the file has the phrase "pick " in it, assume it's the log file
if grep -q "pick " $1
then
sed -e "s/pick \(.*\) \(Meant for it to be two lines\)/squash \1 \2/g" < $1 > $1.tmp
mv $1.tmp $1
# Else, assume it's the commit message file
else
# Use our pre-canned message
echo "Created two line file" > $1
fi
P.S. : Oui, je sais que certains d'entre vous grimacent quand ils me voient utiliser emacs comme éditeur de repli.
P.P.S. : Nous savons que nous devrons détruire tous nos clones du dépôt existant après le rebasement. (Dans la lignée de "tu ne rebaseras pas un référentiel après qu'il ait été publié").
P.P.P.S : Est-ce que quelqu'un peut me dire comment ajouter une prime à ceci ? Je ne vois cette option nulle part sur cet écran, que je sois en mode édition ou en mode affichage.
0 votes
Plus pertinent que les scripts utilisés est l'action finale tentée - il semble assez sûr d'être une liste de pick and squash intermixés, non ? Et y-a-t-il des commits de fusion dans la branche rebasée ?
rebase -p
de toute façon)0 votes
Je ne suis pas sûr de ce que vous entendez par "dernière tentative d'action", mais c'est es c'est juste une liste de choix et de squash mélangés, avec les 400 derniers environ qui sont tous des choix. Il n'y a pas de fusions dans cette liste, bien que le rebasement lui-même effectue ses propres fusions. D'après ce que j'ai lu, "rebase -p" n'est pas recommandé en mode interactif (qui dans mon cas n'est pas si interactif que ça, bien sûr). À partir de kernel.org/pub/software/scm/git/docs/git-rebase.html : "Ceci utilise la machinerie --interactive en interne, mais la combiner avec l'option --interactive explicitement n'est généralement pas une bonne idée."
0 votes
Par "dernière tentative d'action", j'entends la liste de choix/quash transmise à l'équipe d'intervention.
rebase --interactive
- Il s'agit en quelque sorte d'une liste d'actions que git doit tenter. J'espérais que vous pourriez être en mesure de réduire cela à un seul squash qui causait des conflits, et éviter toute la complexité supplémentaire de vos scripts d'aide. L'autre information manquante est le moment où les conflits se produisent - lorsque git applique les correctifs pour former la courge, ou lorsqu'il essaie de dépasser la courge et d'appliquer le correctif suivant ? (Et êtes-vous sûr que rien de mauvais ne se produit avec votre kludge GIT_EDITOR ? Un autre vote pour un cas de test simple).0 votes
Merci pour ces commentaires utiles. J'ai mis à jour la question pour refléter mes réponses.
0 votes
Je ne pense vraiment pas que vous vouliez utiliser un
--strategy
l'option. Tout ça s'est passé sans ça, non ? Et encore, pouvez-vous réduire cela à une seule opération simple, sans utiliser votre script, qui produit le problème ? (A moins que l'erreur ne se produise après toutes les opérations, et qu'elle ne se produise pas si vous ne faites que certaines d'entre elles).0 votes
Je l'ai essayé sans un
--strategy
sans utiliser l'option script, et sans même faire de squash. (En d'autres termes, ungit rebase -i
qui était essentiellement un non-op.) Il n'y a pas eu les premiers problèmes (c.-à-d. "rien à commettre"), mais il s'est à nouveau arrêté au premier conflit espace/tabulation. Si je fais legit rebase -i
et faire juste cette première courge, elle souffre aussi de l'étrangeté du "rien à engager".0 votes
Vous êtes super-extra-sûr qu'il n'y a rien dans votre configuration ? En particulier, est-ce que
apply.whitespace
être fixé ? (Soit utilisergit config --get apply.whitespace
ou vérifier toutes les configurations de repo, d'utilisateur et de système). Et juste au cas où, est-ce que c'est avec une version actuelle de git ?0 votes
J'ai vérifié tous mes paramètres avec les deux
git config --global -l
ygit config -l
(dans le répertoire de dépôt approprié), et aucun espace blanc n'est apparu. Juste pour être super-extra-sûr, j'ai vérifié explicitement la présence deapply.whitespace
à l'échelle locale et mondiale. Nada. La version actuelle que j'utilise est la 1.7.1, bien que l'historique du dépôt ait été généré avec une ou plusieurs versions antérieures, bien sûr. Le bon côté des choses, c'est qu'au moins je ne rate pas quelque chose de flagrant :) (Bien que l'ego mis à part, j'aimerais vraiment que ce soit le cas).0 votes
Au fait, je ne me sentirai pas offensé, même si vous posez des questions qui vous semblent évidentes. Parfois, l'ordinateur est vraiment es débranché.
0 votes
Huh. C'est vraiment bizarre, et je suis à court d'idées. J'ai nunca vu un échec de rebasement sans intervention. C'est vraiment no-op, non ? C'est-à-dire que le SHA1 est un ancêtre du commit actuel. En fait, je pensais qu'il détecterait qu'il pouvait simplement avancer rapidement dans ce cas, mais même si ce n'est pas le cas, tout ce qu'il fera pour une commande
pick
est un appelgit cherry-pick
. Je suppose que pour continuer à réduire la liste... vous pourriez vérifier le commit juste avant celui qui a échoué, puis faire ce cherry-pick vous-même, et voir s'il échoue ?0 votes
Eh bien, je ne suis pas sûr que ce soit vraiment un no-op. Par no-op, je veux dire que j'ai accepté la liste telle quelle, sans rien changer. Il y a beaucoup de branches qui pourraient être écrasées, cependant. C'est une bonne idée de faire du cherry-picking après avoir vérifié le commit unique. Je reviendrai vers vous pour les résultats. Je prévois également de créer un test simple qui contient une branche qui est fusionnée avec un conflit qui nécessite une résolution manuelle.
0 votes
Le cherry-pick unique a bien fonctionné (c'est-à-dire qu'il n'a pas reproduit l'erreur). Juste pour m'assurer que je l'ai fait correctement (c'est mon premier cherry-pick - insérer une blague ici), j'ai regardé le fichier enregistré à partir de ma commande rebase -i (essentiellement un fichier journal), j'ai vérifié les SHA-1 avant celui qui rapporte le problème, puis j'ai cherry-pické celui qui a le problème. Après que cela n'ait pas recréé le problème, j'ai alors vérifié un commit plus en arrière, et j'ai cherry-pické deux commits supplémentaires (pour un total de 3 cherry-picks, finissant par un commit plus en avant). Je n'ai toujours pas eu le problème.
0 votes
OK, j'ai pu recréer ce problème avec un dépôt de jouets. Code à suivre.
2 votes
Le cas du référentiel jouet est beaucoup, beaucoup plus facile à comprendre que votre scripts - et montre que les scripts n'ont rien à voir avec cela, juste le fait que vous essayez de rebaser une branche contenant une fusion avec résolution de conflit (une fusion légèrement maléfique).