611 Stimmen

So finden Sie den nächstgelegenen Elternteil eines Git-Zweigs

Nehmen wir an, ich habe das folgende lokale Repository mit einem Commit-Baum wie diesem:

master --> a
            \
             \
      develop c --> d
               \
                \
         feature f --> g --> h

master ist mein dies ist der neueste stabile Release-Code , develop ist mein dies ist der "nächste" Freigabecode und feature ist eine neue Funktion, die in Vorbereitung ist für develop .

Mit Hilfe von Hooks möchte ich Pushes verweigern können an feature in mein entferntes Repository zu übertragen, es sei denn f ist ein direkter Abkömmling von develop HEAD. D.h. der Commit-Baum sieht so aus, weil das Feature git rebase auf d .

master --> a
            \
             \
      develop c --> d
                     \
                      \
               feature f --> g --> h

Ist es also möglich,:

  • Identifizieren Sie den übergeordneten Zweig von feature ?
  • Identifizieren Sie die Übertragung im übergeordneten Zweig, die f ist ein Nachkomme von?

Von dort aus würde ich prüfen, was HEAD des übergeordneten Zweigs ist, und sehen, ob f Vorgänger mit dem übergeordneten Zweig HEAD übereinstimmt, um festzustellen, ob das Merkmal neu geordnet werden muss.

62voto

Daniel Stutzbach Punkte 69710

Ich habe eine Lösung für Ihr Gesamtproblem (feststellen, ob feature stammt von der Spitze des develop ), aber es funktioniert nicht mit der von Ihnen beschriebenen Methode.

Sie können verwenden git branch --contains um alle Zweige aufzulisten, die von der Spitze von develop , dann verwenden Sie grep um sicherzustellen, dass feature ist unter ihnen.

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

Wenn sie darunter ist, wird sie gedruckt " feature" auf die Standardausgabe und hat den Rückgabewert 0. Andernfalls wird nichts ausgegeben und der Rückgabewert ist 1.

47voto

Richard Sitze Punkte 7886

Was Sie im Folgenden erwartet

gisten:

Warum sollte jemand diesen langen Beitrag lesen wollen? Denn während die vorherigen Antworten das Problem das Problem mit der ursprünglichen Frage verstehen, sie nicht zu korrekten/aussagekräftigen Ergebnissen führen; oder eine genaue Lösung verschiedene Problem.

Sie können auch nur den ersten Abschnitt lesen; er löst das Problem des "etwas finden" und sollte den Umfang des Problems hervorheben. Für manche mag das ausreichend sein.

Dies zeigt Ihnen einen Weg, wie Sie korrekte und aussagekräftige Ergebnisse aus Git zu extrahieren zu extrahieren (die Sie vielleicht nicht mögen), und demonstriert einen Weg, um Ihr Wissen über Ihre Konventionen auf diese Ergebnisse anzuwenden um das zu erhalten, wonach Sie wirklich suchen.

Die folgenden Abschnitte behandeln:

  • Eine unvoreingenommene Frage & Lösung :
    • die nächsten Git-Zweige mit git show-branch .
    • wie die erwarteten Ergebnisse aussehen sollten
  • Beispielgrafik & Ergebnisse
  • Dosierung von Filialen : Arbeit um die Grenzen von git show-branch
  • Eine voreingenommene Frage & Lösung : Einführung von (Benennungs-)Konventionen zur Verbesserung der Ergebnisse

Das Problem mit der Frage

Wie bereits erwähnt, verfolgt Git keine Beziehungen zwischen Zweigen; Zweige sind einfach Namen, die auf einen Commit verweisen. In der offiziellen Git-Dokumentation und anderen Quellen finden wir oft etwas irreführende Diagramme wie z. B.:

A---B---C---D    <- master branch
     \
      E---F      <- work branch

Ändern wir die Form des Diagramms und die hierarchisch anmutenden Namen, um ein entsprechendes Schaubild zu erstellen:

      E---F   <- jack
     /
A---B
     \
      C---D   <- jill

Der Graph (und damit auch Git) sagt uns absolut nichts darüber, welcher Zweig zuerst erstellt wurde (und damit, welcher Zweig vom anderen abgezweigt wurde).

Das master ist ein Elternteil von work in der ersten Grafik ist eine Frage der Konvention.

Deshalb

  • einfache Werkzeuge werden Antworten produzieren, die die Verzerrung ignorieren
  • Bei komplexeren Werkzeugen werden Konventionen (Vorurteile) berücksichtigt.

Eine unvoreingenommene Frage

Zunächst muss ich vor allem die Antwort von Joe Chrysler, andere Antworten hier und die vielen Kommentare/Vorschläge rundherum würdigen; Sie haben mich inspiriert und mir den Weg gewiesen!

Erlauben Sie mir, Joes Formulierung neu zu formulieren und dabei mehrere Zweige zu berücksichtigen, die mit dem nächsten Commit verbunden sind (das kommt vor!):

"Welches ist der nächste Commit, der sich auf einem anderen Zweig als dem aktuellen Zweig befindet, und welcher Zweig ist das?"

Oder mit anderen Worten:

Q1

Angesichts einer Verzweigung B : die Übergabe berücksichtigen C am nächsten bei B'HEAD ( C sein könnte B'HEAD ) die auch von anderen Branchen genutzt wird: Welche Zweige, außer B haben C in ihrer Commit-Historie?

Eine unvoreingenommene Lösung

Ich entschuldige mich im Voraus; es scheint, dass die Leute Einzeiler bevorzugen. Sie können gerne (lesbare/wartbare) Verbesserungen vorschlagen!

#!/usr/local/bin/bash

# git show-branch supports 29 branches; reserve 1 for current branch
GIT_SHOW_BRANCH_MAX=28

CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
if (( $? != 0 )); then
    echo "Failed to determine git branch; is this a git repo?" >&2
    exit 1
fi

##
# Given Params:
#   EXCEPT : $1
#   VALUES : $2..N
#
# Return all values except EXCEPT, in order.
#
function valuesExcept() {
    local except=$1 ; shift
    for value in "$@"; do
        if [[ "$value" != "$except" ]]; then
            echo $value
        fi
    done
}

##
# Given Params:
#   BASE_BRANCH : $1           : base branch; default is current branch
#   BRANCHES    : [ $2 .. $N ] : list of unique branch names (no duplicates);
#                                perhaps possible parents.
#                                Default is all branches except base branch.
#
# For the most recent commit in the commit history for BASE_BRANCH that is
# also in the commit history of at least one branch in BRANCHES: output all
# BRANCHES that share that commit in their commit history.
#
function nearestCommonBranches() {
    local BASE_BRANCH
    if [[ -z "${1+x}" || "$1" == '.' ]]; then
        BASE_BRANCH="$CURRENT_BRANCH"
    else
        BASE_BRANCH="$1"
    fi

    shift
    local -a CANDIDATES
    if [[ -z "${1+x}" ]]; then
        CANDIDATES=( $(git rev-parse --symbolic --branches) )
    else
        CANDIDATES=("$@")
    fi
    local BRANCHES=( $(valuesExcept "$BASE_BRANCH" "${CANDIDATES[@]}") )

    local BRANCH_COUNT=${#BRANCHES[@]}
    if (( $BRANCH_COUNT > $GIT_SHOW_BRANCH_MAX )); then
        echo "Too many branches: limit $GIT_SHOW_BRANCH_MAX" >&2
        exit 1
    fi

    local MAP=( $(git show-branch --topo-order "${BRANCHES[@]}" "$BASE_BRANCH" \
                    | tail -n +$(($BRANCH_COUNT+3)) \
                    | sed "s/ \[.*$//" \
                    | sed "s/ /_/g" \
                    | sed "s/*/+/g" \
                    | egrep '^_*[^_].*[^_]$' \
                    | head -n1 \
                    | sed 's/\(.\)/\1\n/g'
          ) )

    for idx in "${!BRANCHES[@]}"; do
        ## to include "merge", symbolized by '-', use
        ## ALT: if [[ "${MAP[$idx]}" != "_" ]]
        if [[ "${MAP[$idx]}" == "+" ]]; then
            echo "${BRANCHES[$idx]}"
        fi
    done
}

# Usage: gitr [ baseBranch [branchToConsider]* ]
#   baseBranch: '.' (no quotes needed) corresponds to default current branch
#   branchToConsider* : list of unique branch names (no duplicates);
#                        perhaps possible (bias?) parents.
#                        Default is all branches except base branch.
nearestCommonBranches "${@}"

Wie es funktioniert

Unter Berücksichtigung der Ausgabe von: git show-branch

Für git show-branch --topo-order feature/g hotfix master release/2 release/3 feature/d würde die Ausgabe etwa so aussehen:

! [feature/g] TEAM-12345: create X
 * [hotfix] TEAM-12345: create G
  ! [master] TEAM-12345: create E
   ! [release/2] TEAM-12345: create C
    ! [release/3] TEAM-12345: create C
     ! [feature/d] TEAM-12345: create S
------
+      [feature/g] TEAM-12345: create X
+      [feature/g^] TEAM-12345: create W
     + [feature/d] TEAM-12345: create S
     + [feature/d^] TEAM-12345: create R
     + [feature/d~2] TEAM-12345: create Q
        ...
  +    [master] TEAM-12345: create E
 *     [hotfix] TEAM-12345: create G
 *     [hotfix^] TEAM-12345: create F
 *+    [master^] TEAM-12345: create D
+*+++  [release/2] TEAM-12345: create C
+*++++ [feature/d~8] TEAM-12345: create B

Ein paar Punkte:

  • der ursprüngliche Befehl führte N (6) Verzweigungsnamen in der Befehlszeile auf
  • diese Zweignamen erscheinen in der Reihenfolge der ersten N Zeilen der Ausgabe
  • die Zeilen nach dem Header stellen Commits dar
  • die ersten N Spalten der Commit-Zeilen stellen (als Ganzes) ein " Verzweigungs-/Befehlsmatrix ", wobei ein einzelnes Zeichen in der Spalte X zeigt die Beziehung (oder das Fehlen einer Beziehung) zwischen einem Zweig (Kopfzeile X ) und die aktuelle Übertragung.

Primäre Schritte

  1. Angesichts einer BASE_BRANCH
  2. Gegeben eine geordnete Menge (eindeutig) BRANCHES die nicht enthalten sind BASE_BRANCH
  3. Der Einfachheit halber sei N sein BRANCH_COUNT , das ist die Größe von BRANCHES ; Sie umfasst nicht BASE_BRANCH
  4. git show-branch --topo-order $BRANCHES $BASE_BRANCH :
    • Seit BRANCHES enthält nur eindeutige Namen (die als gültig angesehen werden) die Namen werden 1-1 mit den Kopfzeilen der Ausgabe übereinstimmen, und entsprechen den ersten N Spalten der Branch/Commit-Matrix.
    • Seit BASE_BRANCH ist nicht in BRANCHES wird die letzte der Kopfzeilen sein, und entspricht der letzten Spalte der Branch/Commit-Matrix.
  5. tail : Beginn mit Zeile N+3 die erste wegwerfen N+2 Zeilen: N Zweige + Basiszweig + Trennungszeile ---.. .
  6. sed Diese könnten in einem zusammengefasst werden, sind aber aus Gründen der Übersichtlichkeit getrennt.
    • alles nach der Branch/Commit-Matrix entfernen
    • Leerzeichen durch Unterstriche '_' ersetzen; mein Hauptgrund war, potenzielle Probleme bei der IFS-Analyse zu vermeiden und für die Fehlersuche/Lesbarkeit.
    • ersetzen. * avec + Die Basisverzweigung steht immer in der letzten Spalte, und das ist ausreichend. Außerdem, wenn man es allein lässt, geht es durch bash Erweiterung des Pfadnamens, und das macht immer Spaß mit *
  7. egrep : grep für Commits, die mindestens einem Branch zugeordnet sind ( [^_] ) UND zur BASE_BRANCH ( [^_]$ ). Vielleicht sollte das Grundmuster der Verzweigung lauten \+$ ?
  8. head -n1 : Nehmen Sie die erste verbleibende Übertragung
  9. sed : Trennen Sie jedes Zeichen der Verzweigungs-/Bindungsmatrix in separaten Zeilen.
  10. Erfassen der Zeilen in einem Array MAP dann haben wir zwei Arrays:
    • BRANCHES : Länge N
    • MAP : Länge N+1 : zuerst N Elemente 1-1 mit BRANCHES , und das letzte Element, das dem BASE_BRANCH .
  11. Iterieren über BRANCHES (das ist alles, was wir wollen, und es ist kürzer) und prüfen Sie das entsprechende Element in MAP : Ausgang BRANCH[$idx] si MAP[$idx] ist + .

Beispielgrafik & Ergebnisse

Betrachten Sie das folgende, etwas konstruierte Beispieldiagramm:

  • Es werden voreingenommene Namen verwendet, da sie (mir) helfen, die Ergebnisse abzuwägen und zu berücksichtigen.
  • Gehen Sie davon aus, dass Zusammenführungen existieren und ignoriert werden.
  • Die Grafik versucht im Allgemeinen, Verzweigungen als solche hervorzuheben (Gabelung), ohne visuell eine Präferenz/Hierarchie zu suggerieren; ironischerweise master fällt auf, nachdem ich mit dieser Sache fertig war.

                         J                   <- feature/b
                        /
                       H
                      / \ 
                     /   I                   <- feature/a
                    /
                   D---E                     <- master
                  / \ 
                 /   F---G                   <- hotfix
                /
       A---B---C                             <- feature/f, release/2, release/3
            \   \ 
             \   W--X                        <- feature/g
              \ 
               \       M                     <- support/1
                \     /
                 K---L                       <- release/4
                      \ 
                       \       T---U---V     <- feature/e
                        \     /
                         N---O
                              \ 
                               P             <- feature/c
                                \ 
                                 Q---R---S   <- feature/d

Unverzerrte Ergebnisse für die Beispielgrafik

Angenommen, das Skript ist eine ausführbare Datei gitr und dann ausführen:

gitr <baseBranch>

Für verschiedene Branchen B erhalten wir die folgenden Ergebnisse:

GESCHENKT B

Gemeinsames Commit C

Zweige !B mit C in ihrer Geschichte?

Merkmal/a

H

Merkmal/b

Merkmal/b

H

Merkmal/a

Merkmal/c

P

Merkmal/d

Merkmal/d

P

Merkmal/c

feature/e

O

Merkmal/c, Merkmal/d

Merkmal/f

C

Merkmal/a, Merkmal/b, Merkmal/g, Hotfix, Master, Release/2, Release/3

Merkmal/g

C

Merkmal/a, Merkmal/b, Merkmal/f, Hotfix, Master, Release/2, Release/3

Hotfix

D

Merkmal/a, Merkmal/b, Master

Meister

D

Merkmal/a, Merkmal/b, Hotfix

Freigabe/2

C

Merkmal/a, Merkmal/b, Merkmal/f, Merkmal/g, Hotfix, Master, Release/3

Freigabe/3

C

Merkmal/a, Merkmal/b, Merkmal/f, Merkmal/g, Hotfix, Master, Release/2

Freigabe/4

L

Merkmal/c, Merkmal/d, Merkmal/e, Unterstützung/1

Unterstützung/1

L

Merkmal/c, Merkmal/d, Merkmal/e, Release/4

Dosierung von Filialen

[Zu diesem Zeitpunkt vorgelegt weil es zu diesem Zeitpunkt am besten in das endgültige Drehbuch passt. Dieser Abschnitt ist nicht obligatorisch, Sie können ihn einfach überspringen. ]

git show-branch beschränkt sich auf 29 Zweige. Das mag für manche ein Hindernis sein (kein Urteil, ich sage es nur!).

In manchen Situationen können wir die Ergebnisse verbessern, indem wir Zweige in Stapeln gruppieren.

  • BASE_BRANCH muss bei jeder Verzweigung angegeben werden.
  • Wenn es eine große Anzahl von Zweigen in einem Repo gibt kann dies für sich genommen von begrenztem Wert sein.
  • Es kann mehr Nutzen bringen, wenn Sie andere Wege finden um die Verzweigungen zu begrenzen (die gestapelt werden würden).
  • Der vorherige Punkt trifft auf meinen Anwendungsfall zu, also laden Sie vor!

Dieser Mechanismus ist NICHT perfekt, wenn sich die Ergebnisgröße dem Maximum (29) nähert, ist mit einem Fehlschlag zu rechnen. Details unten

Batch-Lösung

#
# Remove/comment-out the function call at the end of script,
# and append this to the end.
##

##
# Given:
#   BASE_BRANCH : $1           : first param on every batch
#   BRANCHES    : [ $2 .. $N ] : list of unique branch names (no duplicates);
#                                perhaps possible parents
#                                Default is all branches except base branch.
#
# Output all BRANCHES that share that commit in their commit history.
#
function repeatBatchingUntilStableResults() {
    local BASE_BRANCH="$1"

    shift
    local -a CANDIDATES
    if [[ -z "${1+x}" ]]; then
        CANDIDATES=( $(git rev-parse --symbolic --branches) )
    else
        CANDIDATES=("$@")
    fi
    local BRANCHES=( $(valuesExcept "$BASE_BRANCH" "${CANDIDATES[@]}") )

    local SIZE=$GIT_SHOW_BRANCH_MAX
    local COUNT=${#BRANCHES[@]}
    local LAST_COUNT=$(( $COUNT + 1 ))

    local NOT_DONE=1
    while (( $NOT_DONE && $COUNT < $LAST_COUNT )); do
        NOT_DONE=$(( $SIZE < $COUNT ))
        LAST_COUNT=$COUNT

        local -a BRANCHES_TO_BATCH=( "${BRANCHES[@]}" )
        local -a AGGREGATE=()
        while (( ${#BRANCHES_TO_BATCH[@]} > 0 )); do
            local -a BATCH=( "${BRANCHES_TO_BATCH[@]:0:$SIZE}" )
            AGGREGATE+=( $(nearestCommonBranches "$BASE_BRANCH" "${BATCH[@]}") )
            BRANCHES_TO_BATCH=( "${BRANCHES_TO_BATCH[@]:$SIZE}" )
        done
        BRANCHES=( "${AGGREGATE[@]}" )
        COUNT=${#BRANCHES[@]}
    done
    if (( ${#BRANCHES[@]} > $SIZE )); then
        echo "Unable to reduce candidate branches below MAX for git-show-branch" >&2
        echo "  Base Branch : $BASE_BRANCH" >&2
        echo "  MAX Branches: $SIZE" >&2
        echo "  Candidates  : ${BRANCHES[@]}" >&2
        exit 1
    fi
    echo "${BRANCHES[@]}"
}

repeatBatchingUntilStableResults "$@"
exit 0

Wie es funktioniert

Wiederholen Sie den Vorgang, bis sich die Ergebnisse stabilisieren.

  1. Pause BRANCHES in Partien von GIT_SHOW_BRANCH_MAX (alias SIZE ) Elemente
  2. aufrufen nearestCommonBranches BASE_BRANCH BATCH
  3. Aggregieren der Ergebnisse zu einer neuen (kleineren?) Gruppe von Zweigen

Wie sie scheitern kann

Übersteigt die Anzahl der aggregierten Zweige die max. SIZE und eine weitere Dosierung/Verarbeitung kann diese Zahl nicht verringern dann entweder:

  • die aggregierten Zweige sind die Lösung, aber das kann nicht überprüft werden durch git show-branch , oder
  • jede Charge wird nicht kleiner; möglicherweise würde eine Verzweigung von einem Stapel helfen, einen anderen zu reduzieren (diff merge base); der derzeitige Algo gibt sich geschlagen und versagt.

Alternative in Betracht ziehen

Einzelnes Paaren eines Basiszweigs mit jedem anderen Zweig von Interesse, Bestimmung eines Commit-Knotens (Merge-Basis) für jedes Paar; Sortieren der Menge der Merge-Basen in der Reihenfolge der Commit-Historie, Auswahl des nächstgelegenen Knotens, Bestimmung aller mit diesem Knoten verbundenen Zweige.

Ich sage das aus einer Position der Rückschau. Es ist wahrscheinlich wirklich der richtige Weg. Ich gehe vorwärts; vielleicht gibt es einen Wert außerhalb des aktuellen Themas.

Eine voreingenommene Frage

Sie haben vielleicht bemerkt, dass die Kernfunktion nearestCommonBranches in dem früheren Skript beantwortet mehr als die Frage Q1 stellt. Vielmehr beantwortet die Funktion eine allgemeinere Frage:

Q2

Angesichts einer Verzweigung B und eine geordnete Menge (keine Duplikate) P von Zweigen ( B nicht in P ): die Übergabe berücksichtigen C am nächsten bei B'HEAD ( C sein könnte B'HEAD ) die von Zweigen in P : Welche Zweige in P haben C in ihrer Commit-Historie, in der Reihenfolge von P?

Auswahl von P eine Voreingenommenheit vermittelt oder eine (begrenzte) Konvention beschreibt. Um alle Merkmale Ihrer Voreingenommenheit/Konvention zu erfassen, sind möglicherweise zusätzliche Hilfsmittel erforderlich, was den Rahmen dieser Diskussion sprengen würde.

Modellierung einfacher Verzerrungen/Konventionen

Voreingenommenheit ist je nach Organisation und Praxis unterschiedlich, und das Folgende ist möglicherweise nicht für Ihre Organisation geeignet. Vielleicht helfen Ihnen aber einige der hier vorgestellten Ideen Ihnen helfen, eine Lösung für Ihre Bedürfnisse zu finden.

Eine voreingenommene Lösung; Voreingenommenheit durch die Namenskonvention der Branche

Vielleicht kann die Voreingenommenheit in die Namenskonvention eingeordnet und aus ihr extrahiert werden, der verwendeten Benennungskonvention.

Voreingenommenheit durch P (Diese anderen Branchenbezeichnungen)

Das brauchen wir für den nächsten Schritt, also sehen wir uns an, was wir tun können, indem wir die Namen der Verzweigungen mit regex filtern.

Die Kombination aus dem früheren und dem neuen Code ist verfügbar als gist: gitr

#
# Remove/comment-out the function call at the end of script,
# and append this to the end.
##

##
# Given Params:
#   BASE_BRANCH : $1           : base branch
#   REGEXs      : $2 [ .. $N ] : regex(s)
#
# Output:
#   - git branches matching at least one of the regex params
#   - base branch is excluded from result
#   - order: branches matching the Nth regex will appear before
#            branches matching the (N+1)th regex.
#   - no duplicates in output
#
function expandUniqGitBranches() {
    local -A BSET[$1]=1
    shift

    local ALL_BRANCHES=$(git rev-parse --symbolic --branches)
    for regex in "$@"; do
        for branch in $ALL_BRANCHES; do
            ## RE: -z ${BSET[$branch]+x ...  ; presumes ENV 'x' is not defined
            if [[ $branch =~ $regex && -z "${BSET[$branch]+x}" ]]; then
                echo "$branch"
                BSET[$branch]=1
            fi
        done
    done
}

##
# Params:
#   BASE_BRANCH: $1    : "." equates to the current branch;
#   REGEXS     : $2..N : regex(es) corresponding to other to include
#
function findBranchesSharingFirstCommonCommit() {
    if [[ -z "$1" ]]; then
        echo "Usage: findBranchesSharingFirstCommonCommit ( . | baseBranch ) [ regex [ ... ] ]" >&2
        exit 1
    fi

    local BASE_BRANCH
    if [[ -z "${1+x}" || "$1" == '.' ]]; then
        BASE_BRANCH="$CURRENT_BRANCH"
    else
        BASE_BRANCH="$1"
    fi

    shift
    local REGEXS
    if [[ -z "$1" ]]; then
        REGEXS=(".*")
    else
        REGEXS=("$@")
    fi

    local BRANCHES=( $(expandUniqGitBranches "$BASE_BRANCH" "${REGEXS[@]}") )

## nearestCommonBranches  can also be used here, if batching not used.
    repeatBatchingUntilStableResults "$BASE_BRANCH" "${BRANCHES[@]}"
}

findBranchesSharingFirstCommonCommit "$@"

Verzerrte Ergebnisse für das Beispieldiagramm

Betrachten wir die geordnete Menge

P = { ^release/.*$ ^support/.*$ ^master$ }

Angenommen, das Skript (alle Teile) befindet sich in einer ausführbaren Datei gitr und dann ausführen:

gitr <baseBranch> '^release/.*$' '^support/.*$' '^master$'

Für verschiedene Branchen B erhalten wir die folgenden Ergebnisse:

GESCHENKT B

Gemeinsames Commit C

Zweige P mit C in ihrer Geschichte (in Reihenfolge)

Merkmal/a

D

Meister

Merkmal/b

D

Meister

Merkmal/c

L

Freigabe/4, Unterstützung/1

Merkmal/d

L

Freigabe/4, Unterstützung/1

feature/e

L

Freigabe/4, Unterstützung/1

Merkmal/f

C

Freigabe/2, Freigabe/3, Master

Merkmal/g

C

Freigabe/2, Freigabe/3, Master

Hotfix

D

Meister

Meister

C

Freigabe/2, Freigabe/3

Freigabe/2

C

Freigabe/3, Master

Freigabe/3

C

Freigabe/2, Master

Freigabe/4

L

Unterstützung/1

Unterstützung/1

L

Freigabe/4

Das kommt einer endgültigen Antwort schon näher; die Antworten für die Versionszweige sind nicht ideal. Lassen Sie uns noch einen Schritt weiter gehen.

Voreingenommenheit durch BASE_NAME et P

Eine Richtung, in die dies gehen könnte, wäre die Verwendung verschiedener P für verschiedene Basisnamen. Lassen Sie uns einen Entwurf dafür ausarbeiten.

Konventionen

DISCLAIMER: Ein Git-Flow-Purist bin ich nicht, macht bitte Nachsicht mit mir

  • Ein Unterstützungszweig soll vom Master abzweigen.
    • Es wird KEINE zwei Support-Zweige geben, die eine gemeinsame Commit haben.
  • Ein Hotfix-Zweig muss von einem Support- oder Master-Zweig abzweigen.
  • Ein Versionszweig muss von einem Support- oder Masterzweig abzweigen.
    • Es kann mehrere Versionszweige geben, die sich einen gemeinsamen Commit teilen; d.h. zur gleichen Zeit von Master abgezweigt.
  • Ein Bugfix-Zweig soll von einem Release-Zweig abzweigen.
  • Ein Feature-Zweig kann von einem Feature, Release, Support oder Master abzweigen:
    • im Sinne von "übergeordnet", kann ein Merkmalszweig nicht festgelegt werden als als übergeordnetes Merkmal über einen anderen festgelegt werden (siehe die anfängliche Diskussion).
    • daher: Feature-Zweige überspringen und suchen Sie nach "Eltern" in den Release-, Support- und/oder Master-Zweigen.
  • jeder andere Zweigname, der als Arbeitszweig betrachtet wird, mit den gleichen Konventionen wie ein Funktionszweig.

Mal sehen, wie weit wir git damit:

Base Branch Pattern

Übergeordnete Zweige, geordnet

Kommentar(e)

^master$

k.A.

kein Elternteil

^support/.*$

^master$

^hotfix/.*$

^support/.*$ ^master$

einen Support-Zweig gegenüber dem Master-Zweig bevorzugen (Reihenfolge)

^Freigabe/.*$

^support/.*$ ^master$

einen Support-Zweig gegenüber dem Master-Zweig bevorzugen (Reihenfolge)

^bugfix/.*$

^Freigabe/.*$

^feature/.*$

^release/.*$ ^support/.*$ ^master$

^.*$

^release/.*$ ^support/.*$ ^master$

Redundant, aber Designbelange getrennt halten

Drehbuch

Die Kombination aus dem früheren und dem neuen Code ist verfügbar als gist: gitp

#
# Remove/comment-out the function call at the end of script,
# and append this to the end.
##

# bash associative arrays maintain key/entry order.
# So, use two maps, values correlated by index:
declare -a MAP_BASE_BRANCH_REGEX=( "^master$" \
                                       "^support/.*$" \
                                       "^hotfix/.*$" \
                                       "^release/.*$" \
                                       "^bugfix/.*$" \
                                       "^feature/.*$" \
                                       "^.*$" )

declare -a MAP_BRANCHES_REGEXS=("" \
                                    "^master$" \
                                    "^support/.*$ ^master$" \
                                    "^support/.*$ ^master$" \
                                    "^release/.*$" \
                                    "^release/.*$ ^support/.*$ ^master$" \
                                    "^release/.*$ ^support/.*$ ^master$" )

function findBranchesByBaseBranch() {
    local BASE_BRANCH
    if [[ -z "${1+x}" || "$1" == '.' ]]; then
        BASE_BRANCH="$CURRENT_BRANCH"
    else
        BASE_BRANCH="$1"
    fi

    for idx in "${!MAP_BASE_BRANCH_REGEX[@]}"; do
        local BASE_BRANCH_REGEX=${MAP_BASE_BRANCH_REGEX[$idx]}
        if [[ "$BASE_BRANCH" =~ $BASE_BRANCH_REGEX ]]; then
            local BRANCHES_REGEXS=( ${MAP_BRANCHES_REGEXS[$idx]} )
            if (( ${#BRANCHES_REGEXS[@]} > 0 )); then
                findBranchesSharingFirstCommonCommit $BASE_BRANCH "${BRANCHES_REGEXS[@]}"
            fi
            break
        fi
    done
}

findBranchesByBaseBranch "$1"
Verzerrte Ergebnisse für das Beispieldiagramm

Angenommen, das Skript (alle Teile) befindet sich in einer ausführbaren Datei gitr und dann ausführen:

gitr <baseBranch>

Für verschiedene Branchen B erhalten wir die folgenden Ergebnisse:

GESCHENKT B

Gemeinsames Commit C

Zweige P mit C in ihrer Geschichte (in Reihenfolge)

Merkmal/a

D

Meister

Merkmal/b

D

Meister

Merkmal/c

L

Freigabe/4, Unterstützung/1

Merkmal/d

L

Freigabe/4, Unterstützung/1

feature/e

L

Freigabe/4, Unterstützung/1

Merkmal/f

C

Freigabe/2, Freigabe/3, Master

Merkmal/g

C

Freigabe/2, Freigabe/3, Master

Hotfix

D

Meister

Meister

(leer, kein Wert)

Freigabe/2

C

Meister

Freigabe/3

C

Meister

Freigabe/4

L

Unterstützung/1

Unterstützung/1

L

Meister

Refactor for the Win!

Möglichkeiten!

In diesem letzten Beispiel hat der Release-Zweig eine gemeinsame Übergabe mit mehreren anderen Zweigen: Release-, Support- oder Master-Zweig.

Wir sollten die verwendeten Konventionen "überarbeiten" oder neu bewerten und sie ein wenig straffen.

Bedenken Sie dies git Verwendungskonvention:

Wenn Sie einen neuen Versionszweig erstellen: Erstellen Sie sofort einen neuen Commit; aktualisieren Sie vielleicht eine Version oder die README-Datei. Dies stellt sicher, dass Feature/Arbeitszweige für die Version (die von der Version abgezweigt sind) den Commit mit dem Versionszweig gemeinsam haben vor dem Commit für den zugrundeliegenden Support- oder Master-Branc liegt (und nicht mit diesem geteilt wird). Support- oder Master-Zweig.

Zum Beispiel:

        G---H   <- feature/z
       /
      E         <- release/1
     /
A---B---C---D   <- master
     \
      F         <- release/2

Ein Feature, das von Release/1 abzweigt, kann keinen gemeinsamen Commit haben haben, das release/1 (seine Eltern) und master oder release/2 umfasst.

Damit steht für jede Verzweigung ein Ergebnis, das übergeordnete Ergebnis, zur Verfügung, mit diesen Konventionen.

FERTIG! Mit Tools und Konventionen kann ich in einer OCD-freundlichen, strukturierten Git-Welt leben.

Ihre Erfahrungen können variieren!

Abschiedsgedanken

  1. gists
  1. Vor allem: Ich bin zu dem Schluss gekommen, dass, über das hinaus, was hier dargelegt wurde, irgendwann akzeptieren muss, dass es möglicherweise mehrere Zweigen zu tun haben.

    • Vielleicht könnten alle potenziellen Zweige validiert werden; Regeln wie "mindestens einer" oder "alle" oder ?? könnten angewendet werden.
  2. Es sind Wochen wie diese, in denen ich denke, dass es wirklich an der Zeit ist, Python zu lernen.

28voto

Matt Stuvysant Punkte 389

Eine Lösung

Die Lösung auf Grund git show-branch hat bei mir nicht ganz funktioniert (siehe unten), deshalb habe ich sie mit derjenigen kombiniert, die auf Grund git log und endete mit diesem Ergebnis:

git log --decorate --simplify-by-decoration --oneline \ # selects only commits with a branch or tag
      | grep -v "(HEAD" \                               # removes current head (and branch)
      | head -n1 \                                      # selects only the closest decoration
      | sed 's/.* (\(.*\)) .*/\1/' \                    # filters out everything but decorations
      | sed 's/\(.*\), .*/\1/' \                        # picks only the first decoration
      | sed 's/origin\///'                              # strips "origin/" from the decoration

Beschränkungen und Vorbehalte

  • HEAD kann abgetrennt werden (viele CI-Tools tun dies, um sicherzustellen, dass sie den richtigen Commit in einem bestimmten Zweig bauen), aber Ursprungsniederlassung und örtliche Niederlassung müssen beides sein zum Nennwert oder "darüber" den aktuellen HEAD.
  • Es muss eine keine Tags im Weg (ich nehme an; ich habe das Skript nicht auf Übertragungen mit einem Tag zwischen Kind- und Elternzweig getestet)
  • Das Skript stützt sich auf die Tatsache, dass "KOPF" ist immer als erste Dekoration aufgeführt durch die log Befehl
  • läuft das Drehbuch auf master et develop führt (meist) zu <SHA> Initial commit

Die Ergebnisse

 A---B---D---E---F <-origin/master, master
      \      \
       \      \
        \      G---H---I <- origin/hotfix, hotfix
         \
          \
           J---K---L <-origin/develop, develop
                \
                 \
                  M---N---O <-origin/feature/a, feature/a
                       \   \
                        \   \
                         \   P---Q---R <-origin/feature/b, feature/b
                          \
                           \
                            S---T---U <-origin/feature/c, feature/c

Trotz der Existenz lokaler Niederlassungen (z. B. nur origin/topic ist seit der Übertragung vorhanden O direkt durch seinen SHA ausgecheckt wurde), sollte das Skript wie folgt drucken:

  • Für Übertragungen G , H , I (Zweigstelle hotfix ) master
  • Für Übertragungen M , N , O (Zweigstelle feature/a ) develop
  • Für Übertragungen S , T , U (Zweigstelle feature/c ) develop
  • Für Übertragungen P , Q , R (Zweigstelle feature/b ) feature/a
  • Für Übertragungen J , K , L (Zweigstelle develop ) <sha> Initial commit *
  • Für Übertragungen B , D , E , F (Zweigstelle master ) <sha> Initial commit

* - oder master si develop die Commits des Masters über dem HEAD des Masters liegen (~ der Master wäre schnell zu entwickeln)


Warum funktioniert show-branch bei mir nicht?

Die Lösung auf Grund git show-branch hat sich für mich in den folgenden Situationen als unzuverlässig erwiesen:

  • abgetrennter KOPF - einschließlich des abgetrennten Kopfgehäuses bedeutet das Ersetzen grep '\*' \ für `grep '!' \ - und das ist erst der Anfang aller Probleme
  • läuft das Drehbuch auf master et develop führt zu develop und `` jeweils
  • Niederlassungen in master Zweig ( hotfix/ Zweige) enden mit dem develop als Elternteil, da ihre engsten master übergeordneten Zweig wurde markiert mit ! anstelle von * aus einem bestimmten Grund.

16voto

saeedgnu Punkte 3783

Da keine der vorangegangenen Antworten bei unserem Repository funktioniert hat, möchte ich meine eigene Methode vorstellen, bei der die letzten Merges in git log :

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

Fügen Sie es in ein Skript namens git-last-merges die auch den Namen einer Verzweigung als Argument akzeptiert (anstelle der aktuellen Verzweigung), sowie andere git log Argumente.

Anhand der Ausgabe können wir die übergeordnete(n) Verzweigung(en) anhand der eigenen Verzweigungskonventionen und der Anzahl der Zusammenführungen von jeder Verzweigung manuell ermitteln.

Wenn Sie git rebase auf untergeordneten Zweigen oft (und Zusammenführungen werden oft vorgespult, damit es nicht zu viele Zusammenführungs-Commits gibt), wird diese Antwort nicht gut funktionieren, also habe ich ein Skript geschrieben, um Vorwärts-Commits (normal und Zusammenführung) und Rückwärts-Commits (es sollte keine Rückwärts-Zusammenführung im Elternzweig geben) auf allen Zweigen im Vergleich zum aktuellen Zweig zu zählen.

#!/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

1 Stimmen

Antwort, die für mich in dem Fall, dass der aktuelle Zweig der Master-Zweig ist, vernünftig funktioniert hat. Die meisten anderen Lösungen gaben ein zufälliges (und eindeutig falsches) Ergebnis in diesem zugegebenermaßen Randfall, in dem es keine tatsächlichen übergeordneten Zweige gibt.

2 Stimmen

Das ist die einzige Antwort, die bei mir funktioniert hat. Um das erste Elternteil anstelle einer Liste der ersten 10 zu erhalten, können Sie dies verwenden: git log --oneline --merges "$@" | grep into | sed 's/.* into //g' | uniq --count | head -n 1 | cut -d ' ' -f 8

11voto

Ivan Kuznetsov Punkte 81
git log -2 --pretty=format:'%d' --abbrev-commit | tail -n 1 | sed 's/\\s(//g; s/,/\\n/g';

(Herkunft/Eltern-Name, Eltern-Name)

git log -2 --pretty=format:'%d' --abbrev-commit | tail -n 1 | sed 's/\\s(//g; s/,/\\n/g';

herkunft/eltern-name

git log -2 --pretty=format:'%d' --abbrev-commit | tail -n 1 | sed 's/(.\*,//g; s/)//';

übergeordneter Name

1 Stimmen

Das gibt mir nur den Namen des entfernten Zweigs zurück.

0 Stimmen

Gibt den letzten Tag zurück, nicht den Namen des Elternteils

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X