Как составить список веток, содержащих данный коммит, в точке создания ветки или после нее - PullRequest
0 голосов
/ 30 августа 2018

Я понимаю, что этот вопрос очень похож на 1419623 , но он другой.

Представьте себе следующее дерево:

master
|
N
M
H-J-K-L-O  -- branch2
E  
C-D-F-G-I  -- branch1
B
A

Мне нужен метод, чтобы сказать, что коммиты C, D, F, G и я все принадлежим к branch1. Для коммитов D, F, G I git branch --contains прекрасно работает. Однако для коммита C в нем будут перечислены master, branch1 и branch2, потому что все они действительно включают его. Есть ли какой-либо метод, чтобы только перечислить ветви, которые имеют данный коммит во время или после создания ветви?

То есть мне нужна какая-то команда, которая будет вести себя так:

$ git some-command B
master
$ git some-command C
branch1
master
$ git some-command F
branch1
$ git some-command H
branch2
master
$ git some-command L
branch2
$ git some-command M
master

Есть ли?

Ответы [ 3 ]

0 голосов
/ 30 августа 2018

В Git ветви не хранят метаданные о том, когда и как они были созданы, или о том, кто их создал. Они просто указатели на коммит.

И на самом деле не имеет смысла хранить эту информацию. Например, вот типичный рабочий процесс:

git checkout master
// Make changes and commit A (on master)
git checkout -b my-branch
// Make changes and commit B (on my-branch)
git checkout master
// Make changes and commit C (on master)

... который генерирует граф коммитов как:

* C  master
|
| * B  my-branch
|/
* A

... но я не могу сказать вам, сколько раз я случайно фиксировал master вместо ветки. Хороший способ исправить это - извлечь новую ветку там, где вы находитесь, а затем сбросить master туда, где она была до этого коммита. Что-то вроде:

git checkout master
// Make changes and commit A (on master)
// Make changes and commit B (on master)
// Oops... I meant to commit B on a branch
git checkout -b my-branch
git checkout master
git reset master^
// Make changes and commit C (on master)

Оба они генерируют один и тот же граф коммитов, но будут иметь разные интерпретации того, какие коммиты «принадлежат» каждой ветви (в частности, my-branch не будет содержать ни A, ни B, поскольку ветвь была создана после каждый из этих коммитов).

0 голосов
/ 31 августа 2018

Пока у вас есть несколько хороших ответов, но я думаю, что стоит отметить, что, учитывая формулировку вашего вопроса, команда должна будет напечатать несколько имен веток для коммитов 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.)

0 голосов
/ 30 августа 2018

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

...