Конфликт при сжатии истории линейного коммита - PullRequest
5 голосов
/ 30 июля 2010

Как это возможно, что при попытке раздавить / исправить линейную ветвь мне все равно придется выполнять ручные слияния? Репо был конвертирован из Subversion. Каждый конфликт имеет вид «Ошибка автоматического выбора вишни» или «Отмена фиксации из-за пустого сообщения о фиксации». Последнее я мог понять, но --fixup-empty или что-то было бы полезно.

Типичный выход:

user@machine:/path (master|REBASE-i)$ git add * && git rebase --continue 
[detached HEAD c536940] fixup!
 Author: John Doe <John.doe@example.com>
 2 files changed, 57 insertions(+), 4 deletions(-)
Automatic cherry-pick failed.  After resolving the conflicts,
mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'
Could not apply 8854a54... >6d5f180 foo
user@machine:/path (master|REBASE-i)$ git st
# Not currently on any branch.
# Unmerged paths:
#   (use "git reset HEAD <file>..." to unstage)
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#   both modified:      filename.ics
#
no changes added to commit (use "git add" and/or "git commit -a")

Ответы [ 3 ]

2 голосов
/ 19 октября 2011

Эти работы:

git mergetool
git rebase --continue
2 голосов
/ 04 мая 2011

Вот мое предложение по реализации какой-то --fixup-empty функциональности:

git filter-branch --msg-filter "sed 's/^$/Unknown/'"

Это заменяет пустые сообщения о фиксации на «Неизвестно» и особенно полезно, если вы хотите выполнить ребазинг после использования git-svn для преобразования репозитория Subversion, который содержал несколько пустых сообщений о фиксации, но не может, потому что он завершается с ошибкой «Aborting commit» из-за пустого сообщения о фиксации ".

0 голосов
/ 14 августа 2016

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

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

Настройка

Предположим, у вас есть идентификатор фиксации idA в качестве начального состояния и идентификатор фиксации idB в качестве конечного состояния, с кучей других фиксаций между ними. Тот факт, что вы хотите раздавить все промежуточные коммиты, говорит о том, что вам больше не важно, как вы попали из исходного состояния в конечное состояние - вам просто нужен коммит, который перемещает вас из точки idA в точку idB .

Предположим, что idB соответствует текущему HEAD, и скажем, что это также ваша master ветвь. Мы собираемся создать новую ветвь с именем squashed, которая имеет такое же содержимое рабочего дерева, что и master, но имеет только один коммит после идентификатора коммита idA.

Решение 1

  1. git checkout master (возможно, вы уже включены master)
  2. Используйте bash или файловый менеджер для копирования всего рабочего дерева в другое место. Просто убедитесь, что вы не указали каталог .git - это поведение по умолчанию в большинстве операционных систем. Итак, чтобы быть ясным - откройте папку репо, выберите все, скопируйте и вставьте ее в какую-то новую папку, на рабочем столе или в любом месте.
  3. git checkout -b squashed idA, чтобы создать новую ветвь с именем squashed с idA в качестве последнего коммита и сделать ее текущей ветвью.
  4. Вставьте содержимое master (которое вы поместили где-то еще в шаге 2) обратно в папку репо. Если ваш файловый менеджер спросит, попросите его заменить все файлы, которые были изменены.
  5. На верхнем уровне вашего репо git add ., а затем git commit. Ваш новый коммит будет иметь все изменения, которые приведут вас от idA до idB.

Решение 2

git branch squashed idA # make a new branch called `squash` with `idA` as its latest commit 
git checkout master # master is "idB" in this case.
git symbolic-ref HEAD refs/heads/squashed
git commit

И вуаля. Когда вы используете symbolic-ref для перемещения HEAD к вершине ветви squash, набор различий между состоянием рабочего дерева и новым местоположением HEAD вычисляется и разбивается на этапы.

Я хочу ответить на беспокойство, которое некоторые высказывали, что это «опасный», «низкоуровневый» хак git. Я постараюсь объяснить, как это работает, чтобы вы чувствовали себя непринужденно. Ваша папка .git - это место хранения всех версий файлов. Две папки в нем - objects и refs. Папка refs содержит файлы, которые сообщают git названия ваших веток и тому, чему они соответствуют. Так, например, если вы откроете .git/refs/heads/master, вы увидите идентификатор самого последнего коммита master. Папка objects содержит набор файлов в подпапках с шестнадцатеричными именами из двух символов. Объектами могут быть несколько разных вещей, включая патчи, коммиты и целые файлы. Также в верхнем уровне папки .git находится файл index. Это отличный пост о содержимом индексного файла. Что нужно знать для настоящего обсуждения, так это то, что индексный файл сообщает вам, какой объект (в папке objects) соответствует последней зафиксированной версии каждого файла в текущей ветви и фиксации.

На этом фоне вот что решение делает: команда symbolic-ref просто сообщает git "внезапно", что вы находитесь на ветви squash вместо ветви master не касаясь рабочего дерева . Это означает, что все ваши файлы соответствуют состоянию master, которое вы знаете и любите, но git видит это как idA с кучей незафиксированных изменений (то есть файл index указывает, что текущий проверенный Все файлы имеют версию idA, и это ссылка, по которой измеряются изменения рабочего дерева). Точные изменения, по сути, заставляют вас заявить idB. Это именно то, что делает Решение 1 также. В этом случае из-за способа реализации symbolic-ref все упомянутые изменения уже подготовлены (т. Е. git add -ed), поэтому все, что вам нужно сделать, это git commit. В этом нет ничего «опасного», потому что это не меняет master или любой из objects, за которым следит git. Вы только что создали новый файл в папке refs с именем squashed и один новый файл в папке objects, соответствующий этому новому комбинированному коммиту. Ваш master находится там, где вы его оставили, у вас просто есть новая ветка с именем squashed с тем же содержимым, но более короткой историей коммитов. Если вы не уверены, что происходит, будьте уверены, что вы можете git checkout master, и он будет там, где вы его оставили.

Если вы хотите принять squashed в качестве нового master, вы можете продолжить и

git branch -m master old-master
git branch -m squashed master

И тогда у вас будет старая ветка со всеми лишними коммитами, сохраненными как old-master, и master будет именно тем, что вы хотели.

Надеюсь, это поможет!

...