Как я могу видеть, от какой ветки от нее разветвлялась ветка?

У моего репозитория git есть три ветки, devel, stable и customers/acme_patches. Давно, stable был разветвлен от devel, и все исправления ошибок имеют место в stable. Время от времени stable снова объединяется в devel. customers/acme_patches - это ветвь с несколькими клиентскими патчами. Филиал не был объединен ни с одним из devel и stable.

Немного искусства ASCII, чтобы проиллюстрировать сценарий:

            o---o---o          customers/acme_patches?
           /
  o---o---1---o---o---o        stable
 /     \           \
o---o---o---2---o---o---o---o  devel
             \
              o---o---o        customers/acme_patches?

Теперь я задаюсь вопросом:

Какая ветвь была customers/acme_patches разветвлена ​​от - devel или stable? Я знаю только, что в прошлом он был раздвоен одним из них, но я не знаю, что. Например. он мог бы зафиксировать 1 или 2 на приведенной выше диаграмме.

Я играл с git log --oneline --graph и gitk, но поскольку customers/acme_patches был разветвлен на несколько сотен комм. назад, трудно следовать за строками.

Может быть, быстрая команда (немного script тоже прекрасна), которая может каким-то образом выполнить коммиты в customers/acme_patches назад, чтобы найти первый коммит с двумя дочерними элементами (точка fork), а затем определяет, было сделано в stable или в devel?

В лучшем случае я мог бы просто выполнить что-то вроде (извините приглашение, я на Windows):

C:\src> git fork-origin customers/acme_patches
stable

Ответ 1

Ну, вероятно, нет идеального решения этого ответа. Я имею в виду, что эквивалент fork-origin в git (насколько мне известно). Поскольку ветвь stable объединяется в devel, ваш acme_patches (от 1) находится на ветке devel и stable.

Что вы можете сделать:

git branch --contains $(git merge-base customers/acme_patches devel stable)

Если у вас стабильный, а не девелопмент, или девелопмент, а не стабильный, то вы знаете, откуда оно взялось.

Например, в случае 2 у вас будет

$ git branch --contains $(git merge-base customers/acme_patches devel stable)
customers/acme_patches
devel

в то время как в случае 1 у вас будет

$ git branch --contains $(git merge-base customers/acme_patches devel stable)
customers/acme_patches
devel
stable

Как сейчас, на обеих ветвях (из-за слияния от stable к dev)

Ответ 2

С git 1.9/2.0 (Q1 2014) вы можете использовать git merge-base --fork-point для запроса лучшего общего предка в соответствии с Git.

Вы можете увидеть эту новую опцию:


А так как совершить ad8261d из John Keeping (johnkeeping), git rebase может использовать тот же самый новый параметр --fork-point, который может пригодиться, если вам нужно переустановить ветвь типа customers/acme_patches на devel.
(Я не говорю, что это имело бы смысл в вашем конкретном сценарии)


Примечание: git 2.16 (Q1 2018) уточняет и улучшает документацию для "merge-base --fork-point", так как было ясно, что она вычисляла, но не почему/зачем.

См. commit 6d1700b (09 ноября 2017 г.) Junio ​​C Хамано (gitster).
(слияние Junio ​​C Hamano - gitster - в совершить 022dd4a, 27 ноября 2017 г.)

merge-base --fork-point doc: уточнить пример и режимы отказа

Иллюстрированная история, используемая для объяснения режима --fork-pointназванный тремя ключевыми точками, фиксирует B3, B2 и B1 от самого старого до новейший, который трудно было прочитать.
Отнесите их к B0, B1, B2.
Также проиллюстрируйте историю после того, как была выполнена перезагрузка с использованием средства --fork-point.

В тексте уже упоминается использование reflog, но описание не clear какую выгоду мы пытаемся получить с помощью reflog.
Уточните, что нужно найти коммиты, которые, как известно, находятся на кончике ветвь удаленного отслеживания.
Это, в свою очередь, требует от пользователей знать последствия базовых допущений, а именно, истечение записей reflog сделает невозможным определить, какие коммиты находились на кончике ветвей удаленного отслеживания, и мы терпим неудачу, когда сомневаемся (вместо того, чтобы давать случайные и неправильный результат без предупреждение).
Другим ограничением является то, что это не будет полезно, если вы не разветвлялись с кончика ветки удаленного слежения, а из середины.
Опишите их.

Итак документация теперь читает:

После работы с ветвью topic, созданной с помощью git checkout -b topic origin/master, история ветки удаленного отслеживания origin/master возможно, был переделан и перестроен, что привело к история этой формы:

                 o---B2
                /
---o---o---B1--o---o---o---B (origin/master)
        \
         B0
          \
           D0---D1---D (topic)

где origin/master используется для указания на значения B0, B1, B2 и теперь это точки в B, и ваша ветвь topic была запущена поверх нее назад когда origin/master был в B0, и вы построили три коммитов: D0, D1, и D, поверх нее.
Представьте, что теперь вы хотите переустановить работу, которую вы сделали в topic, поверх обновленного origin/master.

В таком случае git merge-base origin/master topic вернет родительский элемент B0 на приведенном выше рисунке, но B0^..D не является диапазоном вы обязуетесь переигрывать поверх B (он включает B0, который это не то, что вы написали; это битва, другая сторона отбрасывается, когда он переместил наконечник от B0 до B1).

git merge-base --fork-point origin/master topic призван помочь в этом случае.
Требуется не только B, но также B0, B1 и B2 (т.е. старые подсказки ветвей удаленного отслеживания, о которых знает ваш репозиторий reflog), чтобы увидеть, на каком основании была создана ваша ветка темы, и находит B0, что позволяет вам воспроизводить только коммиты по вашей теме, за исключением коммитов другой стороны позже отбрасываются.

Следовательно

$ fork_point=$(git merge-base --fork-point origin/master topic)

найдет B0 и

$ git rebase --onto origin/master $fork_point topic

воспроизведет D0, D1 и D поверх B, чтобы создать новую историю этого форма:

         o---B2
        /
---o---o---B1--o---o---o---B (origin/master)
    \                   \
     B0                  D0'--D1'--D' (topic - updated)
      \
       D0---D1---D (topic - old)

Предостережение заключается в том, что старые записи reflog в вашем репозитории могут быть expired by git gc.
Если B0 больше не отображается в рефлоге ветки удаленного слежения origin/master, режим --fork-point, очевидно, не может найти его и не удастся, избегая случайного и бесполезного результата (например, родительский элемент B0, как и ту же команду без опции --fork-point).

Кроме того, ветвь удаленного отслеживания использует режим --fork-pointс тем должна быть ваша тема, раздвоенная с ее наконечника.
Если вы разветвлялись от более старой фиксации, чем кончик, этот режим не нашел бы точку fork (представьте себе, что в приведенной выше истории ошибок B0 не существует, origin/master началось с B1, переместилось на B2, а затем B, и вы раздвоены ваша тема в origin/master^, когда origin/master был B1; форма история будет такой же, как и выше, без B0, а родительская B1 - это то, что git merge-base origin/master topic правильно находит, но режим --fork-point не будет, поскольку он не является одним из который раньше был на вершине origin/master).

Ответ 3

Ну, git merge-base customers/acme_patches stable должен показать общий предок этих двух ветвей.

Вы можете попробовать, например, gitk --left-right customers/acme_patches...stable (отметьте три точки!). Это покажет все коммиты, которые находятся в этих ветвях, а не в базе слияния. Использование --left-right будет отмечать каждую фиксацию стрелкой влево или вправо в соответствии с той ветвью, в которой они находятся в левой стрелке, если они находятся в клиенте /acme _patches и стрелке вправо, если они находятся в стабильном состоянии.

Возможно, добавление --date-order, которое я нашел, иногда помогает понять результат.

(Этот синтаксис можно использовать с git log --graph, а не gitk, но imho - это случай, когда визуальный графический дисплей является большим улучшением).

Ответ 4

Не уверен, что он охватывает все случаи, но здесь функции, которые я придумал:

git_branch_contains() {
    local b=$1
    local c=$2
    IFS_=$IFS
    IFS=$'\n'
    local branches=($(git branch --contains "$c" | sed -E 's/^(\*| ) //'))
    IFS=$IFS_
    for b2 in "${branches[@]:+${branches[@]}}"; do
        if [ "$b2" = "$b" ]; then
            return 0
        fi
    done
    return 1
}

git_upstream_branch() {
    local b=$1
    local c1=$(git merge-base --fork-point master "$b") || local c1=
    local c2=$(git merge-base --fork-point dev "$b") || local c2=
    if ! [ "$c1" ]; then
        echo dev
        return
    fi
    if ! [ "$c2" ]; then
        echo master
        return
    fi
    local fp
    if git merge-base --is-ancestor "$c1" "$c2"; then
        fp=$c2
    else
        fp=$c1
    fi
    if git_branch_contains master "$fp" && ! git_branch_contains dev "$fp"; then
        echo master
    else
        echo dev
    fi
}

И здесь script для их проверки (git-upstream-branch-test.sh):

#!/usr/bin/env bash
set -eu

. git-upstream-branch.sh

git_commit() {
    if ! [ "${commit_i:-}" ]; then
        commit_i=0
    fi
    (( commit_i++ )) || true
    echo "$commit_i" > "$commit_i"
    git add "$commit_i"
    git commit -qm "c$commit_i"
}

git_merge() {
    if ! [ "${merge_i:-}" ]; then
        merge_i=0
    fi
    (( merge_i++ )) || true
    git merge -m "$merge_i" $1
}

A_TOPOLOGY=${1:-}

mkdir git-upstream-branch-test-repo
cd git-upstream-branch-test-repo
git init -q
if [ "$A_TOPOLOGY" = 10 ]; then
    git_commit
    git_commit
    git checkout -qb dev
    git_commit
    git_commit
    git checkout -q master
    git_commit
    git_commit
    c=$(git rev-parse HEAD)
    git_commit
    git_commit
    git checkout -q dev
    git checkout -qb t1
    git_commit
    git_commit
    git checkout -q dev
    git_commit
    git_commit
    git rebase --onto "$c" dev t1
elif [ "$A_TOPOLOGY" = 11 ]; then
    git_commit
    git_commit
    git checkout -qb dev
    git_commit
    git_commit
    git checkout -q master
    git_commit
    git_commit
    git checkout -q dev
    c=$(git rev-parse HEAD)
    git_commit
    git_commit
    git checkout -q master
    git checkout -qb t1
    git_commit
    git_commit
    git checkout -q master
    git_commit
    git_commit
    git rebase --onto "$c" master t1
else
    git_commit
    git_commit
    git checkout -qb dev
    git_commit
    git_commit
    git checkout -q master
    git_commit
    git_commit
    if [ "$A_TOPOLOGY" = 4 ] || [ "$A_TOPOLOGY" = 5 ] || [ "$A_TOPOLOGY" = 6 ]; then
        git_merge dev
        git_commit
        git_commit
        git checkout -q dev
        git_commit
        git_commit
        git checkout -q master
    elif [ "$A_TOPOLOGY" = 7 ] || [ "$A_TOPOLOGY" = 8 ] || [ "$A_TOPOLOGY" = 9 ]; then
        git checkout -q dev
        git_merge master
        git_commit
        git_commit
        git checkout -q master
        git_commit
        git_commit
    fi
    git checkout -qb t1
    git_commit
    git_commit
    git checkout -q master
    git_commit
    git_commit
    if [ "$A_TOPOLOGY" = 2 ] || [ "$A_TOPOLOGY" = 5 ] || [ "$A_TOPOLOGY" = 8 ]; then
        git_merge dev
    elif [ "$A_TOPOLOGY" = 3 ] || [ "$A_TOPOLOGY" = 6 ] || [ "$A_TOPOLOGY" = 9 ]; then
        git checkout -q dev
        git_merge master
    fi
fi
git --no-pager log --oneline --graph --decorate --all
git_upstream_branch t1

Используйте его так,

$ rm -rf git-upstream-branch-test-repo && ./git-upstream-branch-test.sh NUMBER

Где NUMBER - это число от 1 до 11, чтобы указать, какой случай (топология) проверить.