Мне нужно сгенерировать отчет со всеми именами ветвей, которые были созданы из develop
ветви (которая была разветвлена master
когда-то в прошлом). Мой подход состоял в том, чтобы получить первый и последний коммит в develop
с помощью git log --pretty=format:"%h" master..develop
, а затем использовать диапазон коммитов с git branch --contains <hash>
.
Однако у вышеуказанного подхода есть несколько проблем:
- Мне нужно запускать команды, чтобы узнать диапазон коммитов, которые нужно учитывать в моем запросе.
- Мне придется повторять команду
git branch --contains
каждый хэш в диапазоне. - Вывод команд
git branch --contains
будет содержать избыточные результаты в большинстве случаев из-за того, что большинство коммитов являются общими для ветвей.
Я надеюсь, что есть лучший и более прямой способ сделать это. Ниже я попытаюсь проиллюстрировать интересующий вас случай:
master
^
| develop
a---b ^
\ |
--c--d---e---f---g--h--i
\ \ \ \
k-l m n o--p
^ ^ ^ ^
| | | |
branch_1 | branch_3 |
branch_2 branch_4
// Desired output:
branch_1
branch_2
branch_3
branch_4
2 ответа
Вы можете быть очень близко с одной командой:
git log --ancestry-path --branches --not $(git merge-base --all master develop) \
--pretty=%D --simplify-by-decoration --decorate-refs=refs/heads
Это «покажи мне только украшения веток во всех историях вершин ветвей, которые восходят к текущей базе слияния мастера и развития».
Вы можете сделать его красивее, передавая результат через что-нибудь вроде awk '{print $NF}' RS=', |\n'
.
Обратите внимание, что это (как и ответ @ torek) предполагает, что вы делаете это в репозитории, администрируемом для стабильности на уровне архива, таком как ваш производственный мастер, нет никакой необходимой связи между именами веток и фиксациями вообще, не говоря уже о постоянном, глобальном или уникальном один. Если вы пытаетесь навсегда связать коммиты с какой-либо внешней административной записью, сделайте это в сообщении фиксации, а не в имени ветви.
Редактировать: если вы хотите получить краткий обзор текущей структуры ветки с момента разделения на мастер-разработку,
git log --graph --decorate-refs=refs/heads --oneline \
--branches --simplify-by-decoration \
--ancestry-path --boundary --not `git merge-base master develop`
Имя ветви просто указывает на один коммит. Все более ранние коммиты, которые являются частью этой ветви, находятся путем обхода графика с этой точки. Так работает, например, часть 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
с {{X5} } . )
Вы можете беспокоиться о сбое в таком случае:
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
тест. Здесь логика заключается в том, чтобы определить, является ли какой-либо из хеш-идентификаторов коммитов в выходных данных списка рев предков рассматриваемой ветви.
(Обратите внимание, что при этом будет напечатано develop
- git merge-base --is-ancestor
считает коммит своим собственным предком - так что вы можете явно пропустить его.)
(Вы можете значительно ускорить это, найдя just соответствующие базовые коммиты. Например, в приведенном выше примере их всего два. Затем вы можете использовать --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
.
Новые вопросы
git
Git - это система управления распределенными версиями с открытым исходным кодом (DVCS). Используйте этот тег для вопросов, связанных с использованием Git и рабочими процессами. НЕ ИСПОЛЬЗУЙТЕ тег [github] для решения проблем, связанных с Git, просто потому, что репозиторий размещен на GitHub. Также не используйте этот тег для общих вопросов по программированию, которые связаны с репозиторием Git.