Найти материнская ветка в Git



допустим, у меня есть следующий локальный репозиторий с деревом фиксации:



master --> a


develop c --> d


feature f --> g --> h


master мой это последний стабильный релиз код,develop - мой это 'следующий' код релиза и feature и новая функция готовится к develop.



что я хочу быть в состоянии сделать на моем удаленном РЕПО с помощью крючков, это для толчков к feature будет отказано, если commit f прямая потомок develop головы. т. е. дерево фиксации выглядит так, потому что функция была git rebase on d.



master --> a


develop c --> d


feature f --> g --> h


так можно:




  • определите родительскую ветвь feature?

  • определите фиксацию в родительской ветви, которая f потомок?


оттуда я бы проверил, что такое глава родительской ветви, и посмотреть, если f предшественник соответствует головке родительской ветви, чтобы определить, если функция должна быть перебазирована.

674   16  
git

16 ответов:

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

история Git основана на A Даг коммитов. Ветви (и "ссылки" в целом) - это просто временные метки, которые указывают на определенные совершает в постоянно растущей группе DAG commit. Таким образом, отношения между ветвями могут меняться с течением времени, но отношения между коммитами-нет.

    ---o---1                foo
            \
             2---3---o      bar
                  \
                   4
                    \
                     5---6  baz

похоже baz основан на (старая редакция) bar? Но что, если мы удалим bar?

    ---o---1                foo
            \
             2---3
                  \
                   4
                    \
                     5---6  baz

теперь это выглядит как baz на основе foo. Но родословная baz не изменилось, мы просто удалили метку (и полученный оборванный коммит). А что если мы добавим новую метку в 4?

    ---o---1                foo
            \
             2---3
                  \
                   4        quux
                    \
                     5---6  baz

теперь это выглядит как baz на основе quux. Тем не менее, родословная не изменилась, изменились только ярлыки.

Если, однако, мы спрашивали " является ли commit 6 потомок commit 3?(предполагая 3 и 6 полные имена фиксации SHA-1), то ответ будет "да", будь то bar и quux метки присутствуют или нет.

таким образом, вы можете задать такие вопросы, как " является ли толкнул совершить a потомок текущего кончика разработки ветку?", но вы не можете достоверно спросить "Что такое родительская ветвь нажатой фиксации?".

наиболее надежный вопрос, который, кажется, приближается к тому, что вы хотите:

для всех предков подтолкнули совершить (за исключением нынешняя верхушка разработки и его предки), которые имеют текущий наконечник разработки как родитель:

  • в хотя бы один такой коммит существует?
  • являются ли все такие коммиты одиночными родительскими коммитами?

который может быть реализован как:

pushedrev=...
basename=develop
if ! baserev="$(git rev-parse --verify refs/heads/"$basename" 2>/dev/null)"; then
    echo "'$basename' is missing, call for help!"
    exit 1
fi
parents_of_children_of_base="$(
  git rev-list --pretty=tformat:%P "$pushedrev" --not "$baserev" |
  grep -F "$baserev"
)"
case ",$parents_of_children_of_base" in
    ,)     echo "must descend from tip of '$basename'"
           exit 1 ;;
    ,*\ *) echo "must not merge tip of '$basename' (rebase instead)"
           exit 1 ;;
    ,*)    exit 0 ;;
esac

это будет охватывать некоторые из того, что вы хотите ограничить, но, возможно, не все.

для справки, вот расширенный пример истории:

    A                                   master
     \
      \                    o-----J
       \                  /       \
        \                | o---K---L
         \               |/
          C--------------D              develop
           \             |\
            F---G---H    | F'--G'--H'
                    |    |\
                    |    | o---o---o---N
                     \   \      \       \
                      \   \      o---o---P
                       \   \   
                        R---S

приведенный выше код может быть использован для отклонения Hи S при получении H',J,K или N, но он также примет L и P (они включают слияния, но они не сливают кончик разработки).

также отклонить L и P, вы можете изменить вопрос и спросить

для всех предков подтолкнули совершить (за исключением нынешняя верхушка разработки и его предки):

  • есть ли какие-либо коммиты с двумя родителями?
  • если нет, то хотя бы один такой коммит есть текущий наконечник разработки его (только) родителей?
pushedrev=...
basename=develop
if ! baserev="$(git rev-parse --verify refs/heads/"$basename" 2>/dev/null)"; then
    echo "'$basename' is missing, call for help!"
    exit 1
fi
parents_of_commits_beyond_base="$(
  git rev-list --pretty=tformat:%P "$pushedrev" --not "$baserev" |
  grep -v '^commit '
)"
case "$parents_of_commits_beyond_base" in
    *\ *)          echo "must not push merge commits (rebase instead)"
                   exit 1 ;;
    *"$baserev"*)  exit 0 ;;
    *)             echo "must descend from tip of '$basename'"
                   exit 1 ;;
esac

перефразирование

другой способ сформулировать вопрос: "Какова ближайшая фиксация, которая находится на ветке, отличной от текущей ветви, и какая ветвь это?"

решение

вы можете найти его с немного магии командной строки

git show-branch -a \
| grep '\*' \
| grep -v `git rev-parse --abbrev-ref HEAD` \
| head -n1 \
| sed 's/.*\[\(.*\)\].*//' \
| sed 's/[\^~].*//'

вот как это работает:

  1. отображение текстовой истории всех коммитов, включая удаленные ветви.
  2. указаны предки текущего коммита по звезде. Отфильтровать все остальное.
  3. игнорировать все коммиты в текущей ветке.
  4. первым результатом будет ближайшая ветвь предка. Игнорируйте другие результаты.
  5. названия ветвей отображаются [в скобках]. Игнорируйте все, что находится за скобками, и скобки.
  6. иногда имя ветви будет включать ~# или^#, чтобы указать, сколько коммитов находится между указанным коммитом и наконечником ветви. Нам все равно. Игнорировать их.

и в результате

запуск вышеуказанного кода на

 A---B---D <-master
      \
       \
        C---E---I <-develop
             \
              \
               F---G---H <-topic

даст вам develop Если вы запустите его из H и master Если вы запустите его и.

код доступен в виде gist

вы также можете попробовать:

git log --graph --decorate

у меня есть решение вашей общей проблемы (определить, если feature происходит от кончика develop), но он не работает с использованием метода, который вы изложили.

можно использовать git branch --contains чтобы перечислить все ветви, сошедшие с кончика develop, а затем использовать grep чтобы убедиться feature среди них.

git branch --contains develop | grep "^ *feature$"

если он среди них, он будет печатать " feature" к стандартному выходу и имеют код возврата 0. В противном случае он ничего не напечатает и будет иметь код возврата часть 1.

это работает нормально для меня.

git show-branch | grep '*' | grep -v "$(git rev-parse --abbrev-ref HEAD)" | head -n1 | sed 's/.*\[\(.*\)\].*//' | sed 's/[\^~].*//'

вежливые ответы от: @droidbot и @Jistanidiot

поскольку ни один из ответов выше не работал в нашем репозитории, я хочу поделиться своим собственным способом, используя последние слияния в git log:

#!/bin/bash
git log --oneline --merges "$@" | grep into | sed 's/.* into //g' | uniq --count | head -n 10

положите его в скрипт с именем git-last-merges, который также принимает имя ветви в качестве аргумента (вместо текущей ветви), а также другие git log аргументы

на выходе мы можем вручную определить родительскую ветвь(ы) на основе собственных соглашений о ветвлении и количества слияний из каждой ветви.

EDIT: Если вы используете git rebase на дочерних ветвях часто (и слияния часто переадресовываются, поэтому не слишком много коммитов слияния), этот ответ не будет работать хорошо, поэтому я написал сценарий для подсчета коммитов вперед (normal и merge), а за коммитами (не должно быть никакого за слиянием в родительской ветви) на всех ветвях по сравнению с текущей ветвью. Просто запустите этот скрипт и дайте мне знать, если у вас работает или нет

#!/bin/bash
HEAD="`git rev-parse --abbrev-ref HEAD`"
echo "Comparing to $HEAD"
printf "%12s  %12s   %10s     %s\n" "Behind" "BehindMerge" "Ahead" "Branch"
git branch | grep -v '^*' | sed 's/^\* //g' | while read branch ; do
    ahead_merge_count=`git log --oneline --merges $branch ^$HEAD | wc -l`
    if [[ $ahead_merge_count != 0 ]] ; then
        continue
    fi
    ahead_count=`git log --oneline --no-merges $branch ^$HEAD | wc -l`
    behind_count=`git log --oneline --no-merges ^$branch $HEAD | wc -l`
    behind_merge_count=`git log --oneline --merges ^$branch $HEAD | wc -l`
    behind="-$behind_count"
    behind_merge="-M$behind_merge_count"
    ahead="+$ahead_count"
    printf "%12s  %12s   %10s     %s\n" "$behind" "$behind_merge" "$ahead" "$branch"
done | sort -n

помните это, как описано в "Git: поиск того, из какой ветви пришел коммит", вы не можете легко определить ветвь, где была сделана эта фиксация (ветви могут быть переименованы, перемещены, удалены...), даже если git branch --contains <commit> - это старт.

  • вы можете вернуться от фиксации к фиксации до git branch --contains <commit> Не список feature филиала и филиала,
  • сравнить, что совершить SHA1, чтобы /refs/heads/develop

если два коммита id матч, вы хорошо идти (это будет означать feature ветвь имеет свое происхождение во главе develop).

git parent

вы можете просто выполнить команду

git parent

чтобы найти родителя ветви, если вы добавите @Joe Chrysler ответ как git alias. Это упростит использование.

откройте файл gitconfig, расположенный по адресу"~/.gitconfig " с помощью любого текстового редактора.

vim  ~/.gitconfig

добавьте в файл следующую команду alias:

[alias]
            parent = "!git show-branch | grep '*' | grep -v \"$(git rev-parse --abbrev-ref HEAD)\" | head -n1 | sed 's/.*\[\(.*\)\].*/\1/' | sed 's/[\^~].*//' #"

сохранить и закрыть редактор.

выполнить команду git parent

вот именно!

магия командной строки JoeChrysler может быть упрощена. Вот логика, как написано:

git show-branch -a           |
  ack '\*'                   | # we want only lines that contain an asterisk
  ack -v "$current_branch"   | # but also don't contain the current branch
  head -n1                   | # and only the first such line
  sed 's/.*\[\(.*\)\].*//' | # really, just the part of the line between []
  sed 's/[\^~].*//'            # and with any relative refs (^, ~n) removed

мы можем сделать то же самое, что все пять из этих команд фильтры в относительно простой :

git show-branch -a | awk -F'[]^~[]' '/\*/ && !/'"$current_branch"'/ {print ;exit}'  

вот так ломается:

-F'[]^~[]' 

разделить строку на поля в ],^,~ и [ символы.

/\*/                      

найти строки, содержащие звездочка

&& !/'"$current_branch"'/

...но не текущее название ветки

{ print ;               

когда вы найдете такую строку, выведите ее второе поле (то есть часть между первым и вторым вхождениями наших символов разделителя полей). Для простых имен ветвей это будет только то, что находится между скобками; для ссылок с относительными скачками это будет просто имя без модификатора. Таким образом, наш набор разделителей полей обрабатывает намерение обоих sed команды.

  exit }

затем немедленно выйти. Это означает, что он только когда-либо обрабатывает первую совпадающую строку, поэтому нам не нужно передавать вывод через head -n 1.

@Mark Reed: вы должны добавить, что строка фиксации должна содержать не только звездочку, но и начинаться со звездочки! В противном случае сообщения фиксации, содержащие звездочку, также включаются в соответствующие строки. Так и должно быть:

git show-branch -a | awk -F'[]^~[]' '/^\*/ && !/'"$current_branch"'/ {print ;exit}'

или длинная версия:

git show-branch -a           |
  awk '^\*'                  | # we want only lines that contain an asterisk
  awk -v "$current_branch"   | # but also don't contain the current branch
  head -n1                   | # and only the first such line
  sed 's/.*\[\(.*\)\].*//' | # really, just the part of the line between []
  sed 's/[\^~].*//'            # and with any relative refs (^, ~n) removed`

кросс-платформенная реализация с Ant

    <exec executable="git" outputproperty="currentBranch">
        <arg value="rev-parse" />  
        <arg value="--abbrev-ref" />  
        <arg value="HEAD" />  
    </exec>

    <exec executable="git" outputproperty="showBranchOutput">
        <arg value="show-branch" />  
        <arg value="-a" />  
    </exec>

    <loadresource property="baseBranch">
      <propertyresource name="showBranchOutput"/>
          <filterchain>
            <linecontains>
              <contains value="*"/>
            </linecontains>
            <linecontains negate="true">
              <contains value="${currentBranch}"/>
            </linecontains>
            <headfilter lines="1"/>
            <tokenfilter>
                <replaceregex pattern=".*\[(.*)\].*" replace=""/>
                <replaceregex pattern="[\^~].*" replace=""/>
            </tokenfilter>
          </filterchain>
    </loadresource>

    <echo message="${currentBranch} ${baseBranch}" />

вот реализация PowerShell решения Марка Рида:

git show-branch -a | where-object { $_.Contains('*') -eq $true} | Where-object {$_.Contains($branchName) -ne $true } | select -first 1 | % {$_ -replace('.*\[(.*)\].*','')} | % { $_ -replace('[\^~].*','') }
vbc=$(git rev-parse --abbrev-ref HEAD)
vbc_col=$(( $(git show-branch | grep '^[^\[]*\*' | head -1 | cut -d* -f1 | wc -c) - 1 )) 
swimming_lane_start_row=$(( $(git show-branch | grep -n "^[\-]*$" | cut -d: -f1) + 1 )) 
git show-branch | tail -n +$swimming_lane_start_row | grep -v "^[^\[]*\[$vbc" | grep "^.\{$vbc_col\}[^ ]" | head -n1 | sed 's/.*\[\(.*\)\].*//' | sed 's/[\^~].*//'

достигает тех же целей, что и ответ Марка Рида, но использует гораздо более безопасный подход, который не ведет себя плохо в ряде сценариев:

  1. последняя фиксация родительской ветви-это слияние, что делает столбец show - не *
  2. сообщение содержит название
  3. сообщение содержит *

любой, кто хочет сделать это в эти дни - приложение SourceTree Atlassian показывает вам отличное визуальное представление о том, как ваши ветви связаны друг с другом, т. е. где они начинались и где они в настоящее время сидят в порядке фиксации (например, голова или 4 фиксации позади и т. д.).

Если вы используете исходное дерево, посмотрите на свои данные фиксации > родители >, тогда вы увидите подчеркнутые номера фиксации (ссылки)

Я не говорю, что это хороший способ решить эту проблему, однако это, кажется, работает для меня.

git branch --contains $(cat .git/ORIG_HEAD) Проблема в том, что cat'ING файл заглядывает во внутреннюю работу git, поэтому это не обязательно совместимо вперед (или обратно совместимо).

Comments

    Ничего не найдено.