Git: Как вывести список всех веток, которые были созданы из определенной ветки? - PullRequest
2 голосов
/ 30 мая 2019

Мне нужно сгенерировать отчет со всеми именами ветвей, которые были созданы из develop ветви (которая когда-то была разветвленной master).Мой подход состоял в том, чтобы получить первый и последний коммит в develop с использованием git log --pretty=format:"%h" master..develop, а затем использовать диапазон коммитов с git branch --contains <hash>.

Однако у вышеупомянутого подхода есть несколько проблем:

  1. Мне нужно запустить команды, чтобы узнать диапазон коммитов, которые необходимо учесть в моем запросе.
  2. Мне придется повторять команду git branch --contains для каждого хеша в диапазоне.
  3. Вывод команд 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 ]

4 голосов
/ 30 мая 2019

Вы можете быть очень близко с одной командой:

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

Редактировать: если вы хотите получить краткий обзор текущей структуры веток с момента расщепления master-development,

git log --graph --decorate-refs=refs/heads --oneline \
         --branches --simplify-by-decoration \
         --ancestry-path --boundary --not `git merge-base master develop`
3 голосов
/ 30 мая 2019

Имя ветки просто указывает на один коммит.Все более ранние коммиты, являющиеся частью этой ветви, обнаруживаются путем перехода назад по графику с этой точки.Так работает, например, часть 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...