Имя ветки просто указывает на один коммит.Все более ранние коммиты, являющиеся частью этой ветви, обнаруживаются путем перехода назад по графику с этой точки.Так работает, например, часть master..develop
: она выбирает коммиты, которые являются предками develop
, исключая любые коммиты, являющиеся предками master
.
(обратите внимание, что, например, в этой ситуации:
o--o <-- master
/
...--o--o
\
*--*--* <-- develop
синтаксис с двумя точками master..develop
включает в себя три помеченных коммитом.)
Я думаю, что вы просите здесь:
for each branch name $branch:
if $branch identifies a commit that is a descendant of any
of the commits in the set `master..develop`:
print $branch
Нолюбой коммит, который является потомком, скажем, коммита i
, по определению является потомком коммита c
.Так что, как примечания tkruse в комментарии :
git branch --contains <hash-of-c>
должно хватить.Чтобы найти коммит для использования здесь, вы можете запустить git rev-list --topo-order master..develop | tail -1
.(Если вы используете --reverse
и head -1
, вы можете получить отказ сломанной трубы, так что метод tail -1
лучше. Досадно, что вы не можете объединить --reverse
с -n 1
.)
YouВы можете беспокоиться о сбое в таком случае:
o--o <-- master
/
...--o--o--1--o--o <-- branch-X
\ \
2--*--* <-- develop
\
o <-- branch-Y
Возможно, вы захотите включить branch-X
и branch-Y
, и они не будут найдены при использовании одной операции git branch --contains
в качествеони являются потомками двух разных коммитов в диапазоне master..develop
, обозначаемых здесь как 1
и 2
.Здесь вам действительно нужно несколько git branch --contains
-ов, или, альтернативно:
set -- $(git rev-list master..develop)
git for-each-ref --format='%(refname)' refs/heads | while read ref; do
branch=${ref#refs/heads/}
tip=$(git rev-parse $ref)
take=false
for i do
if git merge-base --is-ancestor $i $tip; then
take=true
break
fi
done
$take && echo $branch
done
(не проверено и исправлена хотя бы одна ошибка).Но это будет намного медленнее, чем простой тест git branch --contains
.Здесь логика заключается в том, чтобы определить, является ли какой-либо из хеш-идентификаторов фиксации в выходных данных rev-списка предком кончика рассматриваемой ветви.
(Обратите внимание, что при этом будет напечатано develop
- git merge-base --is-ancestor
считает коммит своим собственным предком, поэтому вы можете явно пропустить его.)
(Вы можете значительно ускорить это, найдя просто соответствующую базовую фиксацию.В приведенном выше примере, например, их всего два. Затем вы можете использовать --contains
для тех и объединить именованные ветви. Число используемых баз является функцией от числа и структуры любых коммитов слияния в master..develop
.может быть возможно использовать git rev-list --boundary
, чтобы найти просто границы фиксации; я не экспериментировал с этим.)
Приложение
Я понял во время ланча, что легкоспособ найти минимальный набор коммитов для --contains
или --is-ancestor
тестирования.Начните с полного списка коммитов в диапазоне, сначала отсортированного в топологическом порядке с родителями, например:
set -- $(git rev-list --reverse --topo-order master..develop)
Затем начните с пустого списка «коммитов, которые мы должны проверить»:
hashes=
Теперь для каждого идентификатора хеша в списке всех возможных хешей проверьте каждый, например:
if ! is_descendant $i $hashes; then
hashes="$hashes $i"
fi
, где is_descendant
равно:
# return true (i.e., 0) if $1 is a descendant of any of
# $2, $3, ..., $n
is_descendant() {
local i c=$1
shift
for i do
# $i \ancestor $c => $c \descendant $i
git merge-base --is-ancestor $i $commit_to_test && return 0
done
return 1
}
Послецикл завершен, $hashes
содержит минимальный набор коммитов, который можно использовать для тестирования --contains
или --is-ancestor
.