Мне нужно сгенерировать отчет со всеми именами ветвей, которые были созданы из 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
git
2
mbadawi23 30 Май 2019 в 03:09

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`
4
jthill 30 Май 2019 в 02:46

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

3
torek 30 Май 2019 в 20:41