Изменения, сделанные слиянием, не найдены с помощью 'git bisect' или 'git log -S --all'

Я сделал исправление для файла и передал его в мой репозиторий git.

Некоторое время спустя я обнаружил, что он регрессировал. Я хотел знать, в какой фиксации исправления были вывезены, поэтому я попробовал git bisect и git log --all -S TERM, где "TERM" была строкой, добавленной моим исправлением и которая больше нигде не появилась в проекте.

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

После некоторого ручного поиска я обнаружил, что между двумя ветвями произошло слияние. У одной ветки было мое исправление, а у одного - нет. Автор слияния выбрал ветку без моего исправления (странно, поскольку в файле не было конфликта).

Мои вопросы:

  • Почему git bisect не может найти изменения слияния? Я не вижу ничего в manpage об этом ограничении. Есть ли другой способ использовать его, чтобы найти плохое слияние в приведенном выше сценарии?
  • Почему git log --all -S TERM не git log --all -S TERM слияния? В manpage говорится, что в нем перечисляются коммиты, которые "вводят или удаляют экземпляр". Это объединение слияния не удаляет мою строку? Есть ли другой способ использовать его, чтобы найти плохое слияние в приведенном выше сценарии?
  • Если git bisect и git log -S бесполезны в приведенном выше сценарии, что является эффективным способом найти плохое слияние? Мне очень долго нужно отслеживать изменение, о котором идет речь, без этих инструментов.

Ответ 1

  Почему git log --all -S TERM не перечисляет коммит слияния?

Вы должны добавить опцию -m:

git log -m --all -S TERM

Чтобы понять, почему в этом случае нужна специальная опция, давайте сначала посмотрим на операцию git log -p, которая должна включать изменения в перечисленной истории. Как вы можете легко проверить, по умолчанию для изменений слияния изменения не отображаются. Причина в том, что изменение - это разница между двумя ревизиями, тогда как для фиксации слияния у нас есть три (или более) ревизии. Параметр -m для git log решает эту проблему:

Различное форматирование

...

-m

Этот флаг заставляет коммиты слияния показывать полный diff как обычные коммиты; для каждого родителя слияния, отдельная запись в журнале и diff генерируется. Исключением является то, что только diff против первого родителя отображается, когда задана опция --first-parent; в этом случае выход представляет изменения, внесенные в текущую ветвь.

Затем, предполагая, что git log -S работает так, как если бы, обрабатывая diff, возвращенный git log -p, вы должны признать, что по умолчанию фиксации слияния будут исключены из результатов. Однако, к счастью, вы можете объединить опции -S и -m в git log, и это работает как положено.

Ответ 2

Решение с использованием git bisect

git bisect start $badCommit $goodCommit
git bisect run bash -c "! git merge-base --is-ancestor $goodCommit HEAD || git grep -q $TERM"
bit bisect reset

Примечание. В этом решении для простоты используется git bisect run. Вы также используете git bisect в "интерактивном" режиме. См. раздел объяснений ниже.

Основная идея

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

Идея была предложена CB Bailey в этом комментарии, хотя я не нашел способа использовать git rev-list.

Объяснение

Для части о git bisect объяснение немного сложнее. Он основан на том, как работает алгоритм bisect. Алгоритм предполагает, что все предки хорошего коммита хороши. Таким образом, при тестировании определенного коммита в git bisect вы должны пометить коммиты, которые также не являются потомками хорошего коммита, как хороший. Обычно это не тот случай, когда используется только простая функция grep. Вы можете определить, являются ли коммиты потомками другого коммита, используя git merge-base --is-ancestor $goodCommit HEAD (подробнее см. Как узнать, является ли один коммит предком другого коммита (или наоборот)? или , проверяя, коммит является предком другого)