Почему git-rebase дает мне конфликты слияния, когда все, что я делаю, это сжимает коммиты?



у нас есть Git-репозиторий с более чем 400 коммитов, первые пару десятков из которых было много проб и ошибок. Мы хотим очистить эти коммиты, раздавив многие из них в один коммит. Естественно, git-rebase, кажется, путь. Моя проблема заключается в том, что она заканчивается конфликтами слияния, и эти конфликты нелегко решить. Я не понимаю, почему вообще должны быть какие-либо конфликты, так как я просто сжимаю коммиты (не удаляя или переставляя). Очень вероятно, это демонстрирует что я не совсем понимаю, как git-rebase делает свои squashes.



вот модифицированная версия скриптов, которые я использую:





repo_squash.sh (это скрипт, который на самом деле выполняется):





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 (этот скрипт используется только repo_squash.sh):





if grep -q "pick " 
then
# cp ../repo_squash_history.txt
# emacs -nw
sed -f ../repo_squash_list.txt < > .tmp
mv .tmp
else
if grep -q "initial import"
then
cp ../repo_squash_new_message1.txt
elif grep -q "fixing bad import"
then
cp ../repo_squash_new_message2.txt
else
emacs -nw
fi
fi




repo_squash_list.txt: (этот файл используется только repo_squash_helper.sh)





# Initial import
s/pick (251a190)/squash /g
# Leaving "Needed subdir" for now
# Fixing bad import
s/pick (46c41d1)/squash /g
s/pick (5d7agf2)/squash /g
s/pick (3da63ed)/squash /g




я оставлю содержание "нового сообщения" вашему воображению. Первоначально я сделал это без опции "--strategy theirs" (т. е. используя стратегию по умолчанию, которая, если я правильно понимаю документацию, рекурсивна, но я не уверен, какая рекурсивная стратегия используется), и это также не сработало. Кроме того, я должен отметить, что, используя закомментированный код в repo_squash_helper.sh, я сохранил исходный файл, который sed скрипт работает и запускает сценарий sed против него, чтобы убедиться, что он делает то, что я хотел, чтобы он делал (это было). Опять же, я даже не знаю, почему там б это конфликт, так что, похоже, не так важно, какая стратегия используется. Любые советы или понимание были бы полезны, но в основном я просто хочу, чтобы это раздавило работу.



обновлено с дополнительной информацией из обсуждения с Jefromi:



прежде чем работать над нашим массивным" реальным " репозиторием, я использовал аналогичные скрипты в тестовом репозитории. Это был очень простой репозиторий, и тест работал чисто.



сообщение, которое я получаю, когда он терпит неудачу:



Finished one cherry-pick.
# Not currently on any branch.
nothing to commit (working directory clean)
Could not apply 66c45e2... Needed subdir


это первый выбор после первого сквоша фиксации. Работает git status дает чистый рабочий каталог. Если я тогда сделаю git rebase --continue, Я получаю очень похожее сообщение после еще нескольких коммитов. Если я сделаю это снова, я получу еще одно очень похожее сообщение после пары десятков коммитов. Если я сделаю это еще раз, на этот раз он идет через примерно сотню коммитов, и выдает это сообщение:



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


если я запускаю git status, я:



# 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


бит "оба модифицированных" звучит странно для меня, так как это был просто результат выбора. Также стоит отметить, что если я посмотрю на "конфликт", он сводится к одной строке с одной версией, начинающейся с символа [tab], а другой-с четырех пробелов. Это звучало так, как будто это может быть проблема с тем, как я настроил свой конфигурационный файл, но в нем нет ничего подобного. (Я заметил это ядро.ignorecase имеет значение true, но, очевидно, git-clone сделал это автоматически. Я не совсем удивлен этим, учитывая, что исходный источник был на машине Windows.)



если я вручную исправить file_X.cpp, затем он терпит неудачу вскоре после этого с другим конфликтом, на этот раз между файлом (CMakeLists.txt), в котором одна версия считает, что должна существовать и одна версия считает, что не стоит. Если я могу исправить этот конфликт, сказав, что я хочу этого файл (который я делаю), несколько коммитов позже я получаю еще один конфликт (в этом же файле), где теперь есть некоторые довольно нетривиальные изменения. Это еще только около 25% пути через конфликты.



Я также должен отметить, поскольку это может быть очень важно, что этот проект начался в репозитории svn. Эта начальная история, скорее всего, была импортирована из этого репозитория svn.



обновление #2:



на жаворонков (под влиянием комментариев Jefromi), я решил сделать изменение мое repo_squash.sh быть:



rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a


и затем, я просто принял оригинальные записи, как есть. То есть," ребаз " не должен был ничего менять. Это закончилось с теми же результатами, описанными ранее.



обновление #3:



альтернативно, если я опущу стратегию и заменю последнюю команду:



git rebase -i bd6a09a484b8230d0810e6689cf08a24f26f287a


Я больше не получаю проблем с ребазом "ничего не делать", но я все еще остаюсь с другим рознь.



обновление с репозиторием игрушек, который воссоздает проблему:



test_squash.sh (это файл, который вы на самом деле запустить):



#========================================================
# 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 (используется test_sqash.sh):



# If the file has the phrase "pick " in it, assume it's the log file
if grep -q "pick "
then
sed -e "s/pick (.*) (Meant for it to be two lines)/squash /g" < > .tmp
mv .tmp
# Else, assume it's the commit message file
else
# Use our pre-canned message
echo "Created two line file" >
fi


P. S.: И да, я знаю, некоторые из вас передергивает, когда вы видите меня с помощью Emacs в качестве запасного редактор.



P. P. S.: Мы знаем, что нам придется сдуть все наши клоны существующего репозитория после перебазирования. (По принципу "ты не следует перебазировать репозиторий после его публикации".)



P. P. P. S: Может кто-нибудь сказать мне, как добавить щедрость к этому? Я не вижу эту опцию в любом месте на этом экране, независимо от того, нахожусь ли я в режиме редактирования или в режиме просмотра.

740   5  

5 ответов:

хорошо, я достаточно уверен, чтобы выбросить ответ. Может быть, придется отредактировать его, но я думаю, что знаю, в чем ваша проблема.

ваш тестовый случай игрушечного РЕПО имеет слияние в нем-хуже того, он имеет слияние с конфликтами. И вы перебазируетесь через слияние. Без -p (который не полностью работает с -i), слияния игнорируются. Это означает, что все, что вы сделали в вашем разрешении конфликта нет когда ребаз пытается вишни-выбрать следующий зафиксируйте, поэтому его патч может не применяться. (Я считаю, что это показано как конфликт слияния, потому что git cherry-pick можно применить патч, выполнив трехстороннее слияние между исходной фиксацией, текущей фиксацией и общим предком.)

к сожалению, как мы уже отметили в комментариях, -i и -p (сохранить слияния) не очень хорошо ладят. Я знаю, что правки/формулировки работу, и что дозаказа не будет. Однако, Я верить что он отлично работает с squashes. Это не документально, но это сработало для тестовых случаев, которые я описываю ниже. Если ваш случай намного сложнее, у вас может быть много проблем с тем, что вы хотите, хотя это все еще будет возможно. (Мораль истории: убирайтесь с rebase -iдо слияние.)

Итак, предположим, что у нас есть очень простой случай, когда мы хотим раздавить вместе A, B и C:

- o - A - B - C - X - D - E - F (master)
   \             /
    Z -----------

теперь, как я уже сказал, Если бы не было конфликтов в X,git rebase -i -p работает, как и следовало ожидать.

если есть конфликты, то все становится немного сложнее. Он будет делать прекрасное сжатие, но затем, когда он попытается воссоздать слияние, конфликты повторятся. Вам придется разрешить их снова, добавить их в индекс, а затем использовать git rebase --continue чтобы двигаться дальше. (Конечно, вы можете разрешить их снова, проверив версию из исходной фиксации слияния.)

если у вас есть rerere включено в вашем РЕПО (rerere.enabled значение true), это будет проще-git сможет reиспользовать reШнуровой reрешение с момента, когда у вас изначально были конфликты, и все, что вам нужно сделать, это проверить его, чтобы убедиться, что он работает правильно, добавить файлы в индекс и продолжить. (Вы даже можете пойти на один шаг дальше, включив rerere.autoupdate, и мы добавим их для вас, так что слияние даже не плохо). Я предполагаю, однако, что вы никогда не включали rerere, поэтому вам придется заняться разрешением конфликта себе.*

* Или, вы можете попробовать rerere-train.sh скрипт из git-contrib, который пытается "запустить базу данных [the] rerere из существующих коммитов слияния" - в основном, он проверяет все коммиты слияния, пытается объединить их, и если слияние не удается, он захватывает результаты и показывает их git-rerere. Это может занять много времени, и я никогда не использовал его, но это может быть очень полезно.

Если вы не возражаете против создания новой ветви, вот как я справился с этой проблемой:

находясь на master:

# create a new branch
git checkout -b new_clean_branch

# apply all changes
git merge original_messy_branch

# forget the commits but have the changes staged for commit
git reset --soft master        

git commit -m "Squashed changes from original_messy_branch"

Я искал аналогичное требование, т. е. отбрасывая промежуточные коммиты моей ветви разработки , я обнаружил, что эта процедура работает для меня.
на моей рабочей ветке

git reset –hard mybranch-start-commit
git checkout mybranch-end-commit . // files only of the latest commit
git add -a
git commit -m”New Message intermediate commits discarded”

viola мы подключили последнюю фиксацию к стартовой фиксации ветви! Нет проблем слияния конфликтов! В своей учебной практике я пришел к этому выводу на данном этапе , есть ли лучший подход для этой цели .

отметим, что -X и параметры стратегии были проигнорированы при использовании в интерактивной ребазе.

посмотреть commit db2b3b820e2b28da268cc88adff076b396392dfe (июль 2013, git 1.8.4+),

не игнорируйте параметры слияния в интерактивной rebase

стратегия слияния и ее параметры могут быть указаны в git rebase, но с -- interactive, они были полностью проигнорированы.

подпись: Арно Фонтейн

что означает -X и стратегии работы с интерактивной перебазирования, а также простые перебазироваться, и ваш первоначальный сценарий может сейчас лучше работать.

я столкнулся с более простой, но похожей проблемой, где я имел 1) разрешен конфликт слияния в локальной ветке, 2) продолжал работать больше немного нарушает, 3) хотел перебазировать и ударить конфликты слияния.

для меня git rebase -p -i master работали. Он сохранил первоначальное решение конфликта и позволил мне раздавить других сверху.

надеюсь, что это поможет кому-то!

Comments

    Ничего не найдено.