Поиск ветки Git коммита

651

Есть ли способ узнать, из какой ветви происходит коммит, учитывая его хеш-значение SHA-1 ?

Бонусные баллы, если вы можете сказать мне, как сделать это, используя Ruby Grit.

Этан Гандерсон
источник
4
Различные методы ниже прагматичны, полезно, работая пути , выводить на вероятный ответ, но давайте обратим внимание , что в мерзавец сам вопрос недоразумение, совершающие не из ветвей. Филиалы приходят и уходят, они движутся, только коммиты представляют реальную историю репо. Опять же, это не способ сказать, что решения ниже плохие. Просто знайте, что ни один из них не дает полностью достоверного ответа, который недоступен в git. (Простой случай - удаленные ветки: я разветвляюсь, дважды фиксирую, объединяюсь с другой веткой, удаляю первую ветку. Откуда берется коммит?
RomainValeri
1
У меня есть случай, когда я явно извлекаю мелкий клон глубиной 1 из тега. Это дешево, легко и эффективно ... и до сих пор делал все, что хотел красиво. Теперь, когда я хотел бы знать, на какой ветви был тег, я думаю, по крайней мере, для этой детали. Вы не можете всегда идти домой, лол
Пол Ходжес

Ответы:

865

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

Найти ветки, на которых зафиксирован коммит

git branch -a --contains <commit>

Это расскажет вам все ветви, которые имеют данный коммит в своей истории. Очевидно, что это менее полезно, если коммит уже был объединен.

Поиск по журналам

Если вы работаете в репозитории, в котором была сделана фиксация, вы можете найти в журналах регистрации строку для этой фиксации. Рефлоги старше 90 дней удаляются git-gc, поэтому если коммит слишком старый, вы его не найдете. Тем не менее, вы можете сделать это:

git reflog show --all | grep a871742

найти коммит a871742. Обратите внимание, что вы ДОЛЖНЫ использовать abbreviatd 7 первых цифр коммита. Вывод должен быть примерно таким:

a871742 refs/heads/completion@{0}: commit (amend): mpc-completion: total rewrite

с указанием того, что коммит был сделан по ветке "Завершение". Вывод по умолчанию показывает сокращенные хеши коммитов, поэтому не ищите полный хеш, иначе вы ничего не найдете.

git reflog show на самом деле просто псевдоним для git log -g --abbrev-commit --pretty=oneline , так что если вы хотите поиграть с форматом вывода, чтобы сделать разные вещи доступными для grep, это ваша отправная точка!

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

Найти последующий коммит слияния

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

git log --merges <commit>..

видеть коммиты слияния, которые имеют данный коммит в качестве предка. (Если коммит был объединен только один раз, первым должно быть слияние, за которым вы следите; в противном случае, я полагаю, вам придется изучить несколько из них.) Сообщение коммита слияния должно содержать имя ветви, которая была объединена.

Если вы хотите иметь возможность рассчитывать на это, вы можете использовать --no-ffопцию git mergeдля принудительного создания коммитов слияния даже в случае ускоренной перемотки вперед. (Однако не слишком рвитесь. Это может стать запутывающим, если его чрезмерно использовать.) Ответ VonC на связанный вопрос услужливо развивает эту тему.

Cascabel
источник
5
+1. Само отсутствие информации по этой конкретной проблеме заставляет задуматься, действительно ли это проблема? Филиалы могут быть изменены, переименованы или удалены в любое время. Может быть git describeдостаточно, поскольку (аннотированные) теги можно рассматривать как более значимые, чем ветви.
VonC
9
Я определенно согласен - ветки должны быть легкими и гибкими. Если вы принимаете рабочий процесс, в котором эта информация становится важной, вы можете использовать --no-ffопцию, чтобы убедиться, что всегда есть коммит слияния, так что вы всегда можете проследить путь данного коммита, когда он слился с master.
Каскабель
32
К вашему сведению, если вы хотите найти коммиты, которые существуют только на удаленном компьютере, добавьте -aфлаг к первой команде, например,git branch -a --contains <commit>
Jonathan Day
8
@JonathanDay: нет, это найдет коммиты в любой ветке. Если вы хотите только один на пульте, используйте -r.
Каскабель
7
@SimonTewsi Как я уже сказал, если это действительно проблема, используйте merge --no-ffдля надежной записи названия ветви при слиянии. Но в остальном воспринимайте имена ветвей как временные короткие метки, а коммиты - как постоянные. "Какое короткое имя мы упоминали во время разработки?" не должно быть таким важным вопросом, как "что делает этот коммит?"
Каскабель
156

Эта простая команда работает как шарм:

git name-rev <SHA>

Например (где test-branch это имя ветки):

git name-rev 651ad3a
251ad3a remotes/origin/test-branch

Даже это работает для сложных сценариев, таких как:

origin/branchA/
              /branchB
                      /commit<SHA1>
                                   /commit<SHA2>

Здесь git name-rev commit<SHA2>возвращает branchB .

khichar.anil
источник
3
Ты спас мой день. Это идеальное решение.
Тако
8
И это идеальное решение заняло всего 8 лет. Спасибо!
S1J0
6
Я нашел git name-rev --name-only <SHA>более полезным для получения только название ветви. Мой вопрос ... Может ли он вернуть более одной ветки при любых обстоятельствах?
Дин Кейтон,
1
Это должен быть лучший ответ.
JCollier
1
Спасибо @JCollier за добрые слова.
khichar.anil
47

Обновление декабря 2013 года:

sschuberth комментарии

git-what-branch(Скрипт Perl, см. Ниже), похоже, больше не поддерживается. git-when-mergedэто альтернатива, написанная на Python, которая работает очень хорошо для меня.

Он основан на « Найти коммит слияния, который включает определенный коммит ».

git when-merged [OPTIONS] COMMIT [BRANCH...]

Найти, когда коммит был объединен в одну или несколько веток.
Найти коммит слияния, который принесCOMMIT в указанные ФИЛИАЛЫ.

В частности, ищите самый старый коммит в истории первого родителя, BRANCHкоторый содержит в COMMITкачестве предка.


Оригинальный ответ сентябрь 2010:

Себастьян Душ только что дернулся (за 16 минут до этого ТАКОГО ответа):

git-what-branch : узнайте, на какой ветке находится коммит или как он попал в именованную ветку

Это сценарий Perl от Сета Робертсона, который кажется очень интересным:

СИНТАКСИС

git-what-branch [--allref] [--all] [--topo-order | --date-order ]
[--quiet] [--reference-branch=branchname] [--reference=reference]
<commit-hash/tag>...

ОБЗОР

Сообщите нам (по умолчанию) самый ранний причинный путь коммитов и слияний, чтобы запрошенный коммит попал в именованную ветвь. Если фиксация была сделана непосредственно в именованной ветви, это, очевидно, самый ранний путь.

Под самым ранним причинным путем мы подразумеваем путь, который объединяется в именованную ветвь как можно раньше, за время фиксации (если --topo-orderне указано иное).

ПРЕДСТАВЛЕНИЕ

Если во многих ветвях (например, сотнях) содержится фиксация, системе может потребоваться много времени (для определенного коммита в дереве Linux потребовалось 8 секунд, чтобы исследовать ветку, но было более 200 ветвей-кандидатов), чтобы отследить путь каждому коммиту.
Выбор конкретного--reference-branch --reference tag для изучения будет в сотни раз быстрее (если у вас есть сотни веток-кандидатов).

ПРИМЕРЫ

 # git-what-branch --all 1f9c381fa3e0b9b9042e310c69df87eaf9b46ea4
 1f9c381fa3e0b9b9042e310c69df87eaf9b46ea4 first merged onto master using the following minimal temporal path:
   v2.6.12-rc3-450-g1f9c381 merged up at v2.6.12-rc3-590-gbfd4bda (Thu May  5 08:59:37 2005)
   v2.6.12-rc3-590-gbfd4bda merged up at v2.6.12-rc3-461-g84e48b6 (Tue May  3 18:27:24 2005)
   v2.6.12-rc3-461-g84e48b6 is on master
   v2.6.12-rc3-461-g84e48b6 is on v2.6.12-n
   [...]

Эта программа не учитывает эффекты выбора вишни коммитов, только операции слияния.

VonC
источник
4
git-what-branchкажется, больше не поддерживается. git-when-merged - это альтернатива, написанная на Python, которая очень хорошо работает для меня.
Щуберт
@sschuberth спасибо за это обновление. Я включил ваш комментарий в ответ для большей наглядности.
VonC
37

Например, чтобы найти, что c0118faкоммит произошел от redesign_interactions:

* ccfd449 (HEAD -> develop) Require to return undef if no digits found
*   93dd5ff Merge pull request #4 from KES777/clean_api
|\
| * 39d82d1 Fix tc0118faests for debugging debugger internals
| * ed67179 Move &push_frame out of core
| * 2fd84b5 Do not lose info about call point
| * 3ab09a2 Improve debugger output: Show info about emitted events
| *   a435005 Merge branch 'redesign_interactions' into clean_api
| |\
| | * a06cc29 Code comments
| | * d5d6266 Remove copy/paste code
| | * c0118fa Allow command to choose how continue interaction
| | * 19cb534 Emit &interact event

Вы должны запустить:

git log c0118fa..HEAD --ancestry-path --merges

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

commit a435005445a6752dfe788b8d994e155b3cd9778f
Merge: 0953cac a06cc29
Author: Eugen Konkov
Date:   Sat Oct 1 00:54:18 2016 +0300

    Merge branch 'redesign_interactions' into clean_api

Обновить

Или только одна команда:

git log c0118fa..HEAD --ancestry-path --merges --oneline --color | tail -n 1
Евгений Коньков
источник
4
Это было единственное решение, которое дало мне желаемый результат. Я попробовал остальное.
создатель
Обратите внимание, что это работает, только если сводки фиксации слияния содержат имена ветвей, используемые в слияниях, что по умолчанию они обычно делают. Однако git merge -m"Any String Here"будет скрывать информацию об источнике и целевой ветви.
Qneill
@qneill: Нет, слияние всегда есть информация о присоединяемых филиалах: Merge: f6b70fa d58bdcb. Вы можете назвать свой коммит слияния как вы. У меня не будет дела
Евгений Коньков
1
@EugenKonkov после того, как ветви прошли слияние, история о том, какой путь в графе, с которого они пришли, остается в reflog, но не в базе данных объектов git. Я опубликовал ответ, пытаясь объяснить, что я говорю
qneill
У меня работает версия UPD, простая команда, которая находит исходный коммит
vpalmu
9

git branch --contains <ref>это самая очевидная «фарфоровая» команда для этого. Если вы хотите сделать что-то похожее только с помощью «сантехнических» команд:

COMMIT=$(git rev-parse <ref>) # expands hash if needed
for BRANCH in $(git for-each-ref --format "%(refname)" refs/heads); do
  if $(git rev-list $BRANCH | fgrep -q $COMMIT); then
    echo $BRANCH
  fi
done

(кросспост из этого SO ответа )

Джефф Боуман
источник
6

хичар.анил покрытый большую часть этого в своем ответе.

Я просто добавляю флаг, который удалит теги из списка имен ревизий. Это дает нам:

git name-rev --name-only --exclude=tags/* $SHA
phyatt
источник
Кажется, чего-то не хватает во втором предложении.
Питер Мортенсен
Я обновил описание. Спасибо за отзыв.
Phyatt
4

Опция бедного человека состоит в том, чтобы использовать инструмент tig1 on HEAD, искать коммит, а затем визуально следовать линии от этого коммита до тех пор, пока коммит слияния не будет виден. В сообщении о слиянии по умолчанию должно быть указано, в какую ветку и где происходит слияние :)

1 Tig - это основанный на ncurses интерфейс текстового режима для Git. Он функционирует в основном как браузер Git-репозитория, но он также может помочь в организации изменений для фиксации на уровне чанка и выступать в качестве пейджера для вывода различных команд Git.

user1338062
источник
3

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

Вы можете проверить это здесь: https://github.com/pajp/branch-info-commits

pajp
источник
1
Но если вы хотите добавить метаданные, почему бы не использовать git notes вместо того, чтобы связываться с заголовками коммитов? См. Stackoverflow.com/questions/5212957/… , stackoverflow.com/questions/7298749/… или stackoverflow.com/questions/7101158/…
VonC
Честно говоря, я не знал, что git заметки существовали, когда я писал это. Да, использование git notes для достижения того же самого - вероятно, лучшая идея.
Pajp
3

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

Это то, с чем я работаю:

git rev-parse HEAD | xargs git name-rev

При желании вы можете обрезать вывод:

git rev-parse HEAD | xargs git name-rev | cut -d' ' -f2 | sed 's/remotes\/origin\///g'
миро
источник
0

Я думаю, что кто-то должен столкнуться с той же проблемой, что и не может найти ветку, хотя в действительности она существует в одной ветке.

Тебе лучше сначала все потянуть

git pull --all

Затем выполните поиск по ветке:

git name-rev <SHA>

или:

git branch --contains <SHA>
Йейтс Чжоу
источник
0

Если OP пытается определить историю , пройденную ветвью при создании определенного коммита («выяснить, из какой ветки происходит коммит, учитывая его значение хеш-функции SHA-1»), то без рефлога нет какого - либо записи в базе данных объектов Git, которые показывают, какая именованная ветвь была связана с какой историей коммитов.

(Я отправил это как ответ в ответ на комментарий.)

Надеюсь, этот скрипт иллюстрирует мою точку зрения:

rm -rf /tmp/r1 /tmp/r2; mkdir /tmp/r1; cd /tmp/r1
git init; git config user.name n; git config user.email e@x.io
git commit -m"empty" --allow-empty; git branch -m b1; git branch b2
git checkout b1; touch f1; git add f1; git commit -m"Add f1"
git checkout b2; touch f2; git add f2; git commit -m"Add f2"
git merge -m"merge branches" b1; git checkout b1; git merge b2
git clone /tmp/r1 /tmp/r2; cd /tmp/r2; git fetch origin b2:b2
set -x;
cd /tmp/r1; git log --oneline --graph --decorate; git reflog b1; git reflog b2;
cd /tmp/r2; git log --oneline --graph --decorate; git reflog b1; git reflog b2;

Вывод показывает отсутствие какого-либо способа узнать, поступил ли коммит с 'Add f1' из ветви b1 или b2 из удаленного клона / tmp / r2.

(Последние строки вывода здесь)

+ cd /tmp/r1
+ git log --oneline --graph --decorate
*   f0c707d (HEAD, b2, b1) merge branches
|\
| * 086c9ce Add f1
* | 80c10e5 Add f2
|/
* 18feb84 empty
+ git reflog b1
f0c707d b1@{0}: merge b2: Fast-forward
086c9ce b1@{1}: commit: Add f1
18feb84 b1@{2}: Branch: renamed refs/heads/master to refs/heads/b1
18feb84 b1@{3}: commit (initial): empty
+ git reflog b2
f0c707d b2@{0}: merge b1: Merge made by the 'recursive' strategy.
80c10e5 b2@{1}: commit: Add f2
18feb84 b2@{2}: branch: Created from b1
+ cd /tmp/r2
+ git log --oneline --graph --decorate
*   f0c707d (HEAD, origin/b2, origin/b1, origin/HEAD, b2, b1) merge branches
|\
| * 086c9ce Add f1
* | 80c10e5 Add f2
|/
* 18feb84 empty
+ git reflog b1
f0c707d b1@{0}: clone: from /tmp/r1
+ git reflog b2
f0c707d b2@{0}: fetch origin b2:b2: storing head
qneill
источник
И каков выход для git log 80c10e5..HEAD --ancestry-path --merges --oneline --color | tail -n 1 и git log 086c9ce..HEAD --ancestry-path --merges --oneline --color | tail -n 1команды в обоих случаях?
Евгений Коньков
SHA, конечно, меняются каждый раз, когда запускаются команды, чтобы изменить их, я использовал HEAD ^ 1 и HEAD ^ 2 при новом запуске. Команды и вывод: $ git log HEAD^1..HEAD --ancestry-path --merges --oneline --color | tail -n 1что дает376142d merge branches и $ git log HEAD^2..HEAD --ancestry-path --merges --oneline --color | tail -n 1какие выходы 376142d merge branches - которые показывают сводку коммитов слияния, которую (как я утверждал) можно перезаписать при создании слияния, возможно, запутав историю ветвлений слияния.
qneill
0

TL; DR:

Используйте ниже, если вы заботитесь о статусах выхода из оболочки:

  • branch-current - название текущей ветви
  • branch-names - чистые имена веток (по одному в строке)
  • branch-name - Убедитесь, что только одна ветвь возвращается из branch-names

Обе branch-nameи branch-namesпринимают коммит в качестве аргумента, и по умолчанию, HEADесли ничего не дано.


Псевдонимы, полезные в сценариях

branch-current = "symbolic-ref --short HEAD"  # https://stackoverflow.com/a/19585361/5353461
branch-names = !"[ -z \"$1\" ] && git branch-current 2>/dev/null || git branch --format='%(refname:short)' --contains \"${1:-HEAD}\" #"  # https://stackoverflow.com/a/19585361/5353461
branch-name = !"br=$(git branch-names \"$1\") && case \"$br\" in *$'\\n'*) printf \"Multiple branches:\\n%s\" \"$br\">&2; exit 1;; esac; echo \"$br\" #"

Коммит доступен только из одной ветки

% git branch-name eae13ea
master
% echo $?
0
  • Выход на STDOUT
  • Выходное значение есть 0.

Фиксация достижима из нескольких веток

% git branch-name 4bc6188
Multiple branches:
attempt-extract
master%
% echo $?
1
  • Выход в STDERR
  • Выходное значение 1 .

Из-за статуса выхода их можно безопасно строить. Например, чтобы получить удаленный доступ для извлечения:

remote-fetch = !"branch=$(git branch-name \"$1\") && git config branch.\"$branch\".remote || echo origin #"
Том Хейл
источник
-1

Чтобы найти местное отделение:

grep -lR YOUR_COMMIT .git/refs/heads | sed 's/.git\/refs\/heads\///g'

Чтобы найти удаленную ветку:

grep -lR $commit .git/refs/remotes | sed 's/.git\/refs\/remotes\///g'
Гэри Лин
источник
1
некоторые объяснения ваших примеров будут великолепны
Евгений Коньков
-4

Помимо поиска по всему дереву, пока вы не найдете соответствующий хеш, нет.

янтарный
источник
7
На самом деле я не хочу отрицать это, потому что, строго говоря, это правда, что git не хранит эту информацию постоянно, но, тем не менее, очень часто ее можно найти. Смотри мой ответ.
Каскабель