Как перечислить ветки, содержащие эквивалентную фиксацию

В предыдущем вопросе кто-то предоставил ответ на поиск ветвей, в которых содержалась ТОЧНАЯ фиксация:

Как просмотреть ветки, содержащие заданный коммит

В принятом ответе подчеркивается, что это работает только для EXACT commit id, а не для идентичного фиксации. Далее было указано, что Git Черри можно использовать для решения этой проблемы.

Git вишневое ПОМОЩЬ, предназначенное для обратного хода; обнаружение коммитов НЕ продвигается вверх по течению. Это бесполезно, если я не знаю, какая ветвь его создала и что находится выше по течению. Поэтому я не понимаю, как это поможет решить эту проблему.

Может ли кто-нибудь объяснить/предоставить пример использования Git cherry, чтобы найти все ветки, которые содержат "эквивалент" определенного коммита?

Ответ 1

Прежде чем вы сможете ответить на вопрос о том, какие ветки содержат эквивалентную фиксацию, вы должны определить, "какие коммиты эквивалентны". После этого вы просто используете git branch --contains для каждого из коммитов.

К сожалению, нет 100% надежного способа определения эквивалентных коммитов.

Самый надежный метод - проверить идентификатор патча набора изменений, введенного коммитом. Это то, на что полагаются git cherry, git log --cherry и git log --cherry-mark. Внутри они все называют git patch-id. Идентификатор патча - это только SHA1 нормализованного разброса изменений. Любая фиксация, которая вводит идентичные изменения, будет иметь один и тот же идентификатор патча. Кроме того, любая фиксация, которая вводит в основном идентичные изменения, которые отличаются только пробелом или номером строки, в котором они применяются в файле, будет иметь одинаковый идентификатор патча. Если две коммиты имеют одинаковый идентификатор патча, почти гарантировано, что они эквивалентны - вы практически никогда не получите ложный результат через идентификатор патча. Ложные негативы часто возникают. Каждый раз, когда вы выполняете git cherry-pick и вам приходится вручную разрешать конфликты слияния, вы, вероятно, вносили различия в набор изменений. Даже изменение 1 символа приведет к созданию другого идентификатора патча.

Проверка идентификатора патча требует написания сценариев, как советует Chronial. Сначала вычислите патч-идентификатор оригинала с чем-то вроде

(примечание - скрипты, которые не тестировались, должны быть достаточно близки к работе)

origCommitPatchId=$(git diff ORIG_COMMIT^! | git patch-id | awk '{print $1}')

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

for rev in $(git rev-list --all)
do
   testPatchId=$(git diff ${rev}^1..${rev} | git patch-id | awk '{print $1}')
   if [ "${origCommitPatchId}" = "${testPatchId}" ]; then
      echo "${rev}"
   fi
done

Теперь у вас есть список SHA, и вы можете передать их на git branch -a --contains

Что делать, если вышеупомянутое не работает для вас, хотя из-за конфликтов слияния?

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

Итак, вы можете получить эту информацию с вашего оригинального коммита с помощью

git log -1 --pretty="%an %ae %ad" ORIG_COMMIT

Затем, как и прежде, вам придется пройти через каждую фиксацию в своей истории, распечатать эту же информацию и сравнить ее. Это может дать вам дополнительные совпадения.

Вы также можете использовать git log --grep=ORIG_COMMIT, который найдет любые коммиты, которые ссылаются на ORIG_COMMIT в сообщении фиксации.

Если ни одна из этих работ не может попытаться найти конкретную строку, которая была введена с помощью кирки, или может git log --grep для чего-то еще, что могло быть уникальным в сообщении фиксации.

Если это все звучит сложно, ну, это так. Вот почему я говорю людям избегать использования вишневого салата, когда это возможно. git branch --contains невероятно ценна и проста в использовании и на 100% надежна. Ни одно из других решений даже не приближается.

Ответ 2

Кажется, что работает (но не проверено много). Он запускает git cherry для каждой локальной ветки git и печатает имя ветки, если git cherry не перечисляет фиксацию как отсутствующую в ветке.

# USAGE: git-cherry-contains <commit> [refs]
# Prints each local branch containing an equivalent commit.
git-cherry-contains() {
    local sha; sha=$(git rev-parse --verify "$1") || return 1
    local refs; refs=${2:-refs/heads/}
    local branch
    while IFS= read -r branch; do
        if ! git cherry "$branch" "$sha" "$sha^" | grep -qE "^\+ $sha"; then
            echo "$branch"
        fi
    done < <(git for-each-ref --format='%(refname:short)' $refs)
}

См. Andrew C post для отличного объяснения того, как работает git cherry (используя git patch-id).

Ответ 3

Команда

Используйте следующую команду Bash (замените <COMMIT HASH> хешем фиксации, который вы ищете):

PATCH_ID=$(git show <COMMIT HASH> | git patch-id | cut -d' ' -f1) \
&& ALL_MATCHING_COMMIT_HASHES=$(git log --all -p | git patch-id | grep $PATCH_ID | cut -d' ' -f2) \
&& for HASH in $ALL_MATCHING_COMMIT_HASHES; do echo "$(git branch -a --contains $HASH) (commit $HASH)"; done 

Пример вывода

[email protected] test_cherry_picking $ PATCH_ID=$(git show 59faabb91cfc8e449737f93be8c7df3825491674 | git patch-id | cut -d' ' -f1) \
&& ALL_MATCHING_COMMIT_HASHES=$(git log --all -p | git patch-id | grep $PATCH_ID | cut -d' ' -f2) \
&& for HASH in $ALL_MATCHING_COMMIT_HASHES; do echo "$(git branch -a --contains $HASH) (commit $HASH)"; done

* hotfix (commit 59faabb91cfc8e449737f93be8c7df3825491674)
master (commit bb5fa0d16931fa1d5fa9f5e9ee5c27634fad7da8)

[email protected] test_cherry_picking $

Описание

Вычисляет идентификатор PATCH для заданного GIT ПАРАМЕТР ПЕРЕСМОТРА (например, хеш фиксации). Затем находит все фиксации с вычисленным идентификатором PATCH. Наконец, все имена ветвей, содержащие эти коммиты, печатаются на консоли.

Это, конечно, работает только до тех пор, пока идентификатор PATCH одинаковый для всех (вишневых) коммитов. Каждый раз, когда вы выбираете вишню и должны вручную разрешать конфликты слияния, вы, вероятно, вносите различия в набор изменений. Это приведет к разным идентификаторам PATCH.

Ответ 4

$ for i in `git rev-list --all --grep="something unique in the commit message"`; do git branch --all --contains $i; done | sort | uniq