У нас есть репозиторий Git с более чем 400 коммитами, первая пара десятков из которых была совершена методом проб и ошибок. Мы хотим очистить эти коммиты, объединив многие из них в один коммит. Естественно, git-rebase кажется правильным решением. Моя проблема в том, что это заканчивается конфликтами слияния, и эти конфликты нелегко разрешить. Я не понимаю, почему вообще должны быть какие-то конфликты, ведь я просто раздавливаю коммиты (а не удаляю и не меняю порядок). Скорее всего, это демонстрирует, что я не совсем понимаю, как git-rebase выполняет сквош.
Вот модифицированная версия скриптов, которые я использую:
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 " $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: (этот файл используется только 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
Я оставлю содержание «нового сообщения» вашему воображению. Изначально я делал это без опции «--strategy theirs» (т. Е. Используя стратегию по умолчанию, которая, если я правильно понимаю документацию, является рекурсивной, но я не уверен, какая рекурсивная стратегия используется), а также не т работать. Кроме того, я должен указать, что, используя закомментированный код в repo_squash_helper.sh, я сохранил исходный файл, с которым работает сценарий sed, и запустил сценарий sed, чтобы убедиться, что он делает то, что я хотел ( это было). Опять же, я даже не знаю, почему может возникнуть конфликт, поэтому, похоже, не имеет большого значения, какая стратегия используется. Любые советы или идеи были бы полезны, но в основном я просто хочу, чтобы это сжатие работало.
Обновлено с дополнительной информацией из обсуждения с Джефроми:
Перед тем как приступить к работе над нашим массивным «настоящим» репозиторием, я использовал аналогичные сценарии в тестовом репозитории. Это был очень простой репозиторий, и тест работал чисто.
Сообщение, которое я получаю, когда он терпит неудачу:
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
Бит "оба модифицированных" звучит для меня странно, так как это был всего лишь результат выбора. Также стоит отметить, что если я смотрю на «конфликт», он сводится к одной строке, в которой одна версия начинается с символа [табуляции], а другая - с четырех пробелов. Похоже, это может быть проблема с тем, как я настроил свой файл конфигурации, но в нем нет ничего подобного. (Я заметил, что для core.ignorecase установлено значение true, но очевидно, что git-clone сделал это автоматически. Я не совсем удивлен этим, учитывая, что исходный источник находился на машине Windows.)
Если я вручную исправлю file_X.cpp, он вскоре выйдет из строя с другим конфликтом, на этот раз между файлом (CMakeLists.txt), который, по мнению одной версии, должен существовать, а другой - нет. Если я исправлю этот конфликт, сказав, что мне действительно нужен этот файл (что я и сделаю), через несколько коммитов я получу еще один конфликт (в этом же файле), где теперь есть некоторые довольно нетривиальные изменения. Осталось только 25% урегулирования конфликтов.
Я также должен указать, поскольку это может быть очень важно, этот проект начался в репозитории svn. Эта первоначальная история, скорее всего, была импортирована из репозитория svn.
Обновление №2:
На шутку (под влиянием комментариев Джефроми) я решил изменить свой repo_squash.sh на:
rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
А потом я просто принял исходные записи, как есть. Т.е. "rebase" ничего не должен был изменить. В результате были получены те же результаты, что и ранее.
Обновление № 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 " $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
PS: Да, я знаю, что некоторые из вас съеживаются, когда видят, что я использую emacs в качестве резервного редактора.
PPS: Мы знаем, что нам придется удалить все наши клоны существующего репозитория после перебазирования. (По строкам «не переустанавливайте репозиторий после его публикации».)
PPPS: Кто-нибудь может сказать мне, как добавить к этому награду? Я не вижу эту опцию нигде на этом экране, независимо от того, нахожусь ли я в режиме редактирования или в режиме просмотра.
rebase -p
равно не используете )