Пока у вас есть несколько хороших ответов, но я думаю, что стоит отметить, что, учитывая формулировку вашего вопроса, команда должна будет напечатать несколько имен веток для коммитов B
и C
, например.
Если вы готовы жить в рамках ограничений Git reflogs (по сути, 90 дней по умолчанию), существует альтернативный форум, который, я думаю, будет делать то, что вы хотите.
Позвольте мне перерисовать ваш график так, как я предпочитаю, который слева (более ранние коммиты) направо (более поздние коммиты) с именами по правым краям:
D--F--G--I <-- branch1
/
A--B--C--E--H--M--N <-- master
\
J--K--L--O <-- branch2
Когда вы создали branch1
, вы , вероятно, сделали так:
$ git checkout master # which resolves to commit `C`, because ...
... потому что в этот момент график выглядит так:
A--B--C <-- master (HEAD)
Теперь вы запускаете:
$ git checkout -b branch1
что делает график таким:
A--B--C <-- master, branch1 (HEAD)
То есть, теперь существуют два имени ветви, , оба из которых идентифицируют commit C
. Имя HEAD
в настоящее время прикреплено к branch1
из-за использования git checkout -b
.
На данный момент вы:
... do some work ...
$ git commit -m message # which creates commit `D`
дает:
D <-- branch1 (HEAD)
/
A--B--C <-- master
Когда вы делаете больше коммитов, они добавляются везде, где прикреплено HEAD
, поэтому branch1
накапливает коммиты.
Ваш вопрос включает в себя следующее:
Существует ли какой-либо метод для перечисления только тех веток, которые имеют данный коммит, во время или после создания ветки?
, который сталкивается с двумя проблемами: на самом деле ветви не имеют «создания», а коммиты имеют - или содержат , на самом деле - каждый коммит, который доступен из их кончика в обратном направлении.
Что мы могли бы вместо этого спросить: Для каждого коммита, у каких веток reflogs есть записи, которые напрямую связаны с этим коммитом? Предполагая, что значение reflog не истекло и не было удалено, мы обнаружим, что на этом этапе - где branch1
существует и указывает на коммит D
- коммит D
находится только в рефлоге branch1
, коммит C
находится как в branch1
, так и в master
рефлоге, коммит B
находится только в reflog master
, а commit A
находится только в reflog master
.
Позже, когда у нас есть коммиты с A
по O
включительно, мы обнаружим, что H
похож на C
: он находится в двух разных reflogs для master
и branch2
. Оставшиеся коммиты находятся только в одном рефлоге.
Следовательно, мы начнем с вопроса на основе reflog. Затем для коммитов, которые появляются в нескольких рефлогах, мы объявляем, что такой коммит «более созвучен» с тем, какой рефлог был создан позже . Это связано с тем, что запись reflog с C
в качестве коммита на master
была создана в то время, когда сам коммит C
был создан, поэтому запись reflog имеет более раннюю дату; но когда был создан branch1
, указывающий на фиксацию C
, то, по сути, "принимает" коммит C
в branch1
.
Обратите внимание, что эта формулировка также имеет недостаток - помимо, конечно, того, который возникает, когда истекает срок действия записи reflog. Предположим, мы сейчас запустили:
git checkout -b branch3 <hash-of-C>
и сделать новый коммит на branch3
? Наш график сейчас:
,--P <-- branch3 (HEAD)
/
| D--F--G--I <-- branch1
|/
A--B--C--E--H--M--N <-- master
\
J--K--L--O <-- branch2
Commit C
теперь в трех reflogs: один для master
, один для branch1
и один для branch3
. (Полный reflog для branch3
состоит из «commit P
, затем commit C
».) последний равен branch3
: присоединяя C
в качестве основы branch3
, мы эффективно "украли" коммит C
для branch3
.
Если вам это не нравится, очевидной альтернативой является присвоение коммита C
для самого раннего рефлога, который его содержит, т.е. master
. Но сейчас:
git who-owns <hash-of-C>
говорит master
, а не branch1
, это не то, что вы просили. Это достаточно легко сделать любой из них, хотя; вот непроверенная структура псевдокода, с которой можно начать:
#! /bin/sh
# git-who-owns: pick a branch to "own" a commit
. git-sh-setup
case $# in
0) die "usage: git who-owns <commit> ...";;
esac
who-owns() {
local b hash
hash=$(git rev-parse $1^{commit}) || return 1
for b in $(git for-each-ref --format='%(refname:short) refs/heads); do
git reflog --no-abbrev $b | awk '$1 == "$hash"'
done
}
status=0
for arg do who-owns "$arg" || status=1; done
exit $status
Если этот скрипт находится в вашем $PATH
как git-who-owns
, запуск git who-owns <commit-specifier>
выведет все соответствующие reflogs.
TherЭто еще один большой недостаток: если коммит мог принадлежать одной или нескольким веткам, но был введен с помощью массовых операций, таких как git fetch
, за которыми следует git merge
, его хэш-идентификатор не будет ни в одном журнале ссылок. Вы можете объединить вышеприведенное с операциями --contains
, чтобы назначить такие «промежуточные» коммиты ветвям.
(Хотя идея в целом не очень работоспособна. Я бы не стал тратить на это много времени. Вместо этого, добавьте маркер, такой как тег, в граничный коммит, если вы хотите создать ограниченные списки коммитов Затем git rev-list branch-name ^marker
получит необходимые вам идентификаторы коммитов. Это условно пишется git rev-list marker..branch-name
, например, git log orgin/master..master
.)