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

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

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


repo_squash.sh(это фактически запущенный script):


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(этот script используется только 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

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

Обновлено с дополнительной информацией из обсуждения с 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], а другой с четырьмя пробелами. Это звучит так, как будто это может быть проблема с тем, как я настроил свой файл конфигурации, но в этом нет ничего подобного. (Я заметил, что для core.ignorecase установлено значение true, но, очевидно, git -clone сделал это автоматически. Я не совсем удивлен тем, что исходный источник был на машине Windows.)

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

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

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

На жаворонке (под влиянием комментариев Jefromi) я решил сделать change my repo_squash.sh следующим:

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

И тогда я просто принял оригинальные записи, как есть. I.e, "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 [email protected]{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 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 the commit message file
else
# Use our pre-canned message
  echo "Created two line file" > $1
fi

P.S.: Да, я знаю, что некоторые из вас съеживаются, когда вы видите, что я использую emacs в качестве откатного редактора.

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

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

Ответ 1

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

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

К сожалению, как мы отметили в комментариях, -i и -p (сохранить слияния) не очень хорошо ладят. Я знаю, что редактирование/переписывание работы, и что переупорядочение не делает. Тем не менее, я считаю, что он отлично работает с кабачками. Это не документировано, но оно работало для тестовых случаев, которые я описываю ниже. Если ваш случай - путь, более сложный, у вас может быть много проблем с тем, что вы хотите, хотя это будет возможно. (Мораль истории: очистите 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 сможет повторно использовать записанное разрешение с момента возникновения конфликтов, и все, что вам нужно сделать, это проверить его, чтобы убедиться, что оно работает правильно, добавить файлы в индекс и продолжить. (Вы даже можете сделать шаг дальше, включив rerere.autoupdate, и он добавит их для вас, поэтому слияние не будет даже сбой). Я предполагаю, однако, что вы никогда не разрешали ререре, так что вам придется самому разрешать конфликт. *

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

Ответ 2

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

Быть на хозяине:

# 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"

Ответ 3

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

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"

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

Ответ 4

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

См. commit db2b3b820e2b28da268cc88adff076b396392dfe (июль 2013 г., git 1.8.4+),

Не игнорировать параметры слияния в интерактивной переадресации

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

Подписано: Арно Фонтен

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

Ответ 5

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

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

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