488 votes

Comment trouver le parent le plus proche d'une branche Git ?

Disons que j'ai le dépôt local suivant avec un arbre de commit comme ceci :

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

master est mon il s'agit du dernier code de la version stable , develop est mon il s'agit du code de la "prochaine" version y feature es une nouvelle fonctionnalité en préparation pour develop .

En utilisant des crochets, je veux être capable de refuser les poussées pour feature à mon référentiel distant, à moins que le commit f est un descendant direct de develop HEAD. C'est à dire que l'arbre de commit ressemble à ceci, parce que la fonctionnalité a été git rebase en d .

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

Alors, est-il possible de :

  • Identifiez la branche mère de feature ?
  • Identifiez le commit de la branche mère qui f est un descendant de ?

A partir de là, je vérifierais quel est le HEAD de la branche mère, et voir si f Le prédécesseur correspond à la branche parent HEAD, pour déterminer si la fonctionnalité doit être rebasée.

0 votes

Cette question devrait être reformulée pour trouver le parent d'un parent.

2voto

ladiko Points 67

La solution de Mark Reed est essentiellement correct. Cependant, notez que la ligne de livraison ne doit pas seulement contenir un astérisque, mais commencer par un astérisque ! Sinon, les messages de commit qui contiennent un astérisque sont également inclus dans les lignes correspondantes. Cela devrait donc être le cas :

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

ou la version longue :

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/.*\[\(.*\)\].*/\1/' | # really, just the part of the line between []
  sed 's/[\^~].*//'            # and with any relative refs (^, ~n) removed`

0 votes

Dans la version longue, vouliez-vous dire s/awk/grep/ ?

2voto

Haoyang Feng Points 140

Utilisez :

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/.*\[\(.*\)\].*/\1/' | sed 's/[\^~].*//'

Il permet d'atteindre les mêmes objectifs que La réponse de Mark Reed mais il utilise une approche beaucoup plus sûre qui ne se comporte pas mal dans un certain nombre de scénarios :

  1. Le dernier commit de la branche parent est une fusion, ce qui fait que la colonne affiche - pas *
  2. Le message d'engagement contient un nom de branche
  3. Le message d'engagement contient *

2voto

Richard Sitze Points 5167

Ce à quoi il faut s'attendre ci-dessous

gistes :

Pourquoi voudrait-on lire ce long message ? Parce que si les réponses précédentes ont clairement comprendre le problème de la question originale, elles sont loin de fournir des résultats corrects et significatifs ; ou de résoudre avec précision une différents problème.

N'hésitez pas à passer en revue la première section ; elle résout le problème de "trouver quelque chose" et devrait mettre en évidence l'étendue du problème. Pour certains, cela peut être suffisant.

Cela vous montrera comment extraire des résultats corrects et significatifs de git (vous ne les aimerez peut-être pas), et démontrera une façon d'appliquer votre connaissance de vos conventions à ces résultats pour extraire ce que vous recherchez vraiment.

Sections sous la couverture :

  • Une question et une solution impartiales :
    • les branches git les plus proches en utilisant git show-branch .
    • à quoi doivent ressembler les résultats attendus
  • Exemple de graphique et de résultats
  • Branches de mise en lots : travailler autour des limites de git show-branch
  • Une question et une solution biaisées : introduire des conventions (de dénomination) pour améliorer les résultats

Le problème de la question

Comme cela a été mentionné, git ne suit pas les relations entre les branches ; Les branches sont simplement des noms faisant référence à un commit. Dans la documentation officielle de git et d'autres sources, nous rencontrerons souvent des diagrammes quelque peu trompeurs tels que :

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

Changeons la forme du diagramme et les noms hiérarchiquement suggestifs pour montrer un graphique équivalent :

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

Le graphe (et donc git) ne nous dit absolument rien sur la branche qui a été créée en premier (donc, qui a été branchée sur l'autre).

Ce master est un parent de work dans le premier graphique est une question de convention.

Par conséquent,

  • un simple outillage produira des réponses qui ignorent le biais
  • un outillage plus complexe intègre des conventions (biais).

Une question impartiale

Tout d'abord, je dois reconnaître avant tout la réponse de Joe Chrysler, les autres réponses ici, et les nombreux commentaires/suggestions tout autour ; Ils m'ont inspiré et indiqué la voie à suivre !

Permettez-moi de reformuler la reformulation de Joe, en tenant compte des multiples branches liées au commit le plus proche (cela arrive !) :

"Quel est le commit le plus proche qui réside sur une branche autre que la branche branche actuelle, et de quelle branche s'agit-il ?"

Ou, en d'autres termes :

Q1

Étant donné une branche B : considérer l'engagement C le plus proche de B'HEAD ( C pourrait être B'HEAD ) qui est partagée par d'autres branches : quelles branches, autres que B ont C dans leur historique de livraison ?

Une solution impartiale

Je m'excuse d'avance ; il semble que les gens préfèrent les phrases toutes faites. N'hésitez pas à suggérer des améliorations (lisibles/maintenables) !

#!/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 "${@}"

Comment cela fonctionne

Considérant la sortie de : git show-branch

Pour git show-branch --topo-order feature/g hotfix master release/2 release/3 feature/d le résultat ressemblerait à ceci :

! [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

Quelques points :

  • la commande originale listait N (6) noms de branches sur la ligne de commande
  • Ces noms de branches apparaissent, dans l'ordre, comme les N premières lignes de la sortie.
  • les lignes qui suivent l'en-tête représentent les commits
  • les N premières colonnes des lignes d'engagement représentent (dans leur ensemble) une " matrice branche/commit ", où un seul caractère de la colonne X indique la relation (ou l'absence de relation) entre une branche (ligne d'en-tête) X ) et le commit actuel.

Étapes primaires

  1. Étant donné un BASE_BRANCH
  2. Étant donné un ensemble ordonné (unique) BRANCHES qui ne comprend pas BASE_BRANCH
  3. Pour des raisons de brièveté, laissons N être BRANCH_COUNT , qui est la taille de BRANCHES ; il ne comprend pas BASE_BRANCH
  4. git show-branch --topo-order $BRANCHES $BASE_BRANCH :
    • Desde BRANCHES ne contient que des noms uniques (présumés valides) les noms correspondront à 1-1 avec les lignes d'en-tête de la sortie, et correspondent aux N premières colonnes de la matrice branche/engagement.
    • Desde BASE_BRANCH n'est pas dans BRANCHES elle sera la dernière des lignes d'en-tête, et correspond à la dernière colonne de la matrice branche/engagement.
  5. tail : commencer par la ligne N+3 ; jeter le premier N+2 lignes : N branches + branche de base + ligne de séparation ---.. .
  6. sed : ils pourraient être combinés en un seul... mais sont séparés pour plus de clarté.
    • supprimer tout ce qui se trouve après la matrice de branche/commit
    • remplacer les espaces par des caractères de soulignement '_' ; ma raison principale était d'éviter les problèmes potentiels d'analyse de l'IFS et pour des raisons de débogage et de lisibilité.
    • remplacer * con + ; la branche de base est toujours dans la dernière colonne, et c'est suffisant. De plus, si on le laisse seul, il passe par bash l'expansion du nom de chemin, et c'est toujours amusant avec les *
  7. egrep grep pour les commits qui correspondent à au moins une branche ( [^_] ) ET à la BASE_BRANCH ( [^_]$ ). Peut-être que ce modèle de branche de base devrait être \+$ ?
  8. head -n1 : prendre le premier engagement restant
  9. sed : séparer chaque caractère de la matrice branche/commit sur des lignes distinctes.
  10. Capturez les lignes dans un tableau MAP à ce moment-là, nous avons deux tableaux :
    • BRANCHES : longueur N
    • MAP : longueur N+1 : première N éléments 1-1 avec BRANCHES et le dernier élément correspondant au BASE_BRANCH .
  11. Faire un itération sur BRANCHES (c'est tout ce que nous voulons, et c'est plus court) et vérifier l'élément correspondant dans MAP : sortie BRANCH[$idx] si MAP[$idx] es + .

Exemple de graphique et de résultats

Considérons l'exemple de graphe suivant, quelque peu artificiel :

  • Des noms biaisés seront utilisés, car ils aident à (me) pondérer et à considérer les résultats.
  • Je suppose que les fusions existent et sont ignorées.
  • Le graphique tente généralement de mettre en évidence les branches en tant que telles (bifurcation), sans suggérer visuellement une préférence/hiérarchie ; ironiquement master ressort après que j'en ai fini avec cette chose.

                         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

Résultats non biaisés pour l'exemple de graphique

En supposant que le script est dans un fichier exécutable gitr puis exécuter :

gitr <baseBranch>

Pour les différentes branches B nous obtenons les résultats suivants :

DONNÉ B

Engagement partagé C

Branches !B avec C dans leur histoire ?

caractéristique/a

H

caractéristique/b

caractéristique/b

H

caractéristique/a

caractéristique/c

P

caractéristique/d

caractéristique/d

P

caractéristique/c

caractéristique/e

O

caractéristique/c, caractéristique/d

caractéristique/f

C

feature/a, feature/b, feature/g, hotfix, master, release/2, release/3

caractéristique/g

C

feature/a, feature/b, feature/f, hotfix, master, release/2, release/3

hotfix

D

caractéristique/a, caractéristique/b, maître

maître

D

fonctionnalité/a, fonctionnalité/b, correctif

release/2

C

feature/a, feature/b, feature/f, feature/g, hotfix, master, release/3

release/3

C

feature/a, feature/b, feature/f, feature/g, hotfix, master, release/2

release/4

L

caractéristique/c, caractéristique/d, caractéristique/e, support/1

support/1

L

feature/c, feature/d, feature/e, release/4

Branches de mise en lots

[Présenté à ce stade parce qu'il s'intègre mieux dans le script final à ce stade. Cette section n'est pas obligatoire, n'hésitez pas à la sauter. ]

git show-branch se limite à 29 branches. Cela peut être un blocage pour certains (sans jugement, juste pour dire !).

Nous pouvons améliorer les résultats, dans certaines situations, en regroupant les branches en lots.

  • BASE_BRANCH doit être soumis avec chaque branche.
  • S'il y a un grand nombre de branches dans un repo cela peut avoir une valeur limitée, par lui-même.
  • Peut fournir plus de valeur si vous trouvez d'autres moyens pour limiter les branches (qui seraient mises en lots).
  • Le point précédent correspond à mon cas d'utilisation, alors en avant !

Ce mécanisme n'est PAS parfait, lorsque la taille du résultat approche le maximum (29), attendez-vous à ce qu'il échoue. Détails ci-dessous

Solution par lots

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

Comment cela fonctionne

Répétez jusqu'à ce que les résultats se stabilisent

  1. Pause BRANCHES en lots de GIT_SHOW_BRANCH_MAX (alias SIZE ) éléments
  2. appelez nearestCommonBranches BASE_BRANCH BATCH
  3. Agrégation des résultats en un nouvel ensemble de branches (plus petites ?)

Comment cela peut échouer

Si le nombre de succursales agrégées dépasse le maximum SIZE et que la poursuite du traitement par lots ne peut pas réduire ce nombre. alors soit :

  • l'agrégation des branches EST la solution, mais cela ne peut pas être vérifié par git show-branch ou
  • chaque lot ne réduit pas ; éventuellement une branche d'un lot aiderait à réduire un autre lot (diff merge base) ; l'algo actuel s'avoue vaincu et échoue.

Envisager une alternative

Apparier individuellement une branche de base avec chaque autre branche d'intérêt, déterminer un nœud de validation (base de fusion) pour chaque paire ; trier l'ensemble des bases de fusion dans l'ordre de l'historique des validations, prendre le nœud le plus proche, déterminer toutes les branches associées à ce nœud.

Je présente cela avec un certain recul. C'est probablement la bonne voie à suivre. Je vais de l'avant ; peut-être y a-t-il de la valeur en dehors du sujet actuel.

Une question biaisée

Vous avez peut-être remarqué que la fonction principale nearestCommonBranches dans le script précédent répond à plus que la question Q1. En fait, la fonction répond à une question plus générale :

Q2

Étant donné une branche B et un ensemble ordonné (pas de doublons) P de branches ( B pas dans P ) : considérer le commit C le plus proche de B'HEAD ( C pourrait être B'HEAD ) qui est partagé par les branches dans P : dans l'ordre par ordre de P, quelles branches dans P ont C dans leur historique de commit ?

Choisir P fournit un parti pris, ou décrit une convention (limitée). Faire correspondre toutes les caractéristiques de vos biais/conventions peut nécessiter des outils supplémentaires, ce qui n'entre pas dans le cadre de cette discussion.

Modélisation d'un simple biais/convention

Le parti pris varie selon l'organisation et les pratiques, et ce qui suit peut ne pas convenir à votre organisation. Si rien d'autre n'est fait, peut-être que certaines des idées présentées ici pourront vous aider à vous aider à trouver une solution à vos besoins.

Une solution partiale ; partialité de la convention de dénomination des branches

Peut-être que le parti pris peut être mis en correspondance avec, et extrait de, la convention de dénomination utilisée.

Biais par P (Ces autres noms de branches)

Nous allons en avoir besoin pour l'étape suivante, alors voyons ce que nous pouvons faire en filtrant les noms de branches par regex.

La combinaison du code précédent et du nouveau code ci-dessous est disponible en tant que 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 "$@"

Résultats biaisés pour le graphique d'exemple

Considérons l'ensemble ordonné

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

En supposant que le script (toutes les parties) est dans un fichier exécutable gitr puis exécuter :

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

Pour les différentes branches B nous obtenons les résultats suivants :

DONNÉ B

Engagement partagé C

Branches P avec C dans leur historique (dans l'ordre)

caractéristique/a

D

maître

caractéristique/b

D

maître

caractéristique/c

L

communiqué/4, support/1

caractéristique/d

L

communiqué/4, support/1

caractéristique/e

L

communiqué/4, support/1

caractéristique/f

C

release/2, release/3, master

caractéristique/g

C

release/2, release/3, master

hotfix

D

maître

maître

C

release/2, release/3

release/2

C

release/3, master

release/3

C

release/2, master

release/4

L

support/1

support/1

L

release/4

Nous nous rapprochons d'une réponse définitive ; les réponses pour les branches de la version finale ne sont pas idéales. Allons un peu plus loin.

Biais par BASE_NAME y P

Une façon d'y parvenir pourrait être d'utiliser différents systèmes de gestion de l'information. P pour différents noms de base. Travaillons sur un design pour ça.

Conventions

CLAUSE DE NON-RESPONSABILITÉ : Je ne suis pas un puriste du flux git, soyez indulgent avec moi s'il vous plaît.

  • Une branche de support doit se brancher sur master.
    • Il n'y aura PAS deux branches de support partageant un commit commun.
  • Une branche de correctif doit être dérivée d'une branche de support ou d'un master.
  • Une branche de publication doit être dérivée d'une branche de support ou d'un master.
    • Il peut y avoir plusieurs branches de version partageant un même commit ; c'est-à-dire des branches de master en même temps.
  • Une branche de correction de bogues doit être dérivée d'une branche de publication.
  • une branche de fonctionnalité peut être dérivée d'une fonctionnalité, d'une version, d'un support ou d'un master :
    • aux fins du "parent", une branche caractéristique ne peut pas être établie comme comme parent d'une autre (voir la discussion initiale).
    • donc : sauter les branches caractéristiques et chercher le "parent" parmi les branches release, support, et/ou master.
  • tout autre nom de branche pour être considéré comme une branche de travail, avec les mêmes conventions qu'une branche de fonctionnalité.

Voyons voir jusqu'où nous git avec ça :

Motif de la branche de base

Branches mères, ordonnées

Commentaire(s)

^master$

s/o

pas de parent

^support/.*$

^master$

^hotfix/.*$

^support/.*$ ^master$

donner la préférence à une branche de support par rapport à master (commande)

^release/.*$

^support/.*$ ^master$

donner la préférence à une branche de support par rapport à master (commande)

^bugfix/.*$

^release/.*$

^feature/.*$

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

^.*$

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

Redondance, mais séparation des préoccupations de conception

script

La combinaison du code précédent et du nouveau code ci-dessous est disponible en tant que 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"
Résultats biaisés pour le graphique d'exemple

En supposant que le script (toutes les parties) est dans un fichier exécutable gitr puis exécuter :

gitr <baseBranch>

Pour les différentes branches B nous obtenons les résultats suivants :

DONNÉ B

Engagement partagé C

Branches P avec C dans leur historique (dans l'ordre)

caractéristique/a

D

maître

caractéristique/b

D

maître

caractéristique/c

L

communiqué/4, support/1

caractéristique/d

L

communiqué/4, support/1

caractéristique/e

L

communiqué/4, support/1

caractéristique/f

C

release/2, release/3, master

caractéristique/g

C

release/2, release/3, master

hotfix

D

maître

maître

(vide, aucune valeur)

release/2

C

maître

release/3

C

maître

release/4

L

support/1

support/1

L

maître

Refactor for the Win !

Opportunités !

Dans ce dernier exemple, la branche release partage un commit commun avec plusieurs autres branches : release, support, ou master.

Nous allons "remanier" ou réévaluer les conventions utilisées et les resserrer un peu.

Considérez ceci git convention d'utilisation :

Lors de la création d'une nouvelle branche de version : créer immédiatement un nouveau commit ; peut-être mettre à jour une version, ou le fichier README. Cela garantit que les branches de fonctionnalité/travail pour la version (branchée à partir de la version) auront le commit partagé avec la branche release avant (et non pas partagé par) le commit de la branche support ou master support ou master sous-jacente.

Par exemple :

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

Une branche de fonctionnalité de la version 1 ne peut pas avoir un commit commun qui inclut release/1 (son parent) et master ou release/2.

Cela fournit un résultat, le parent, pour chaque branche, avec ces conventions.

FAIT ! Avec des outils et des conventions, je peux vivre dans un monde git structuré et respectueux des TOC.

Votre kilométrage peut varier !

Réflexions sur l'avenir

  1. gistes
  1. Avant tout : j'en suis venu à la conclusion que, au-delà de ce qui a été présenté ici, à un certain point on doit accepter qu'il peut y avoir de multiples branches à traiter.

    • Peut-être que des validations pourraient être faites sur toutes les branches potentielles ; des règles "au moins une" ou "toutes" ou ? ? pourraient être appliquées.
  2. C'est dans des semaines comme celle-ci que je pense vraiment qu'il est temps que j'apprenne Python.

1voto

Cacahuete Frito Points 1200

Les solutions basées sur git show-branch -a et certains filtres ont un inconvénient : Git peut considérer un nom de branche d'une branche éphémère.

Si vous avez quelques parents possibles auxquels vous tenez, vous pouvez vous poser cette question similaire (et probablement celle que le PO voulait connaître) :

D'un sous-ensemble spécifique de toutes les branches, quel est le parent le plus proche d'une branche git ?

Pour simplifier, je considérerai qu'une "branche git" fait référence à HEAD (c'est-à-dire la branche actuelle).

Imaginons que nous ayons les branches suivantes :

HEAD
important/a
important/b
spam/a
spam/b

Les solutions basées sur git show-branch -a + filtres, peut donner que le parent le plus proche de HEAD es spam/a mais cela ne nous intéresse pas.

Si nous voulons savoir lequel des important/a y important/b est le parent le plus proche de HEAD nous pourrions exécuter ce qui suit :

for b in $(git branch -a -l "important/*"); do
    d1=$(git rev-list --first-parent ^${b} HEAD | wc -l);
    d2=$(git rev-list --first-parent ^HEAD ${b} | wc -l);
    echo "${b} ${d1} ${d2}";
done \
| sort -n -k2 -k3 \
| head -n1 \
| awk '{print $1}';

Ce qu'il fait :

1.) $(git branch -a -l "important/*") : Imprimer une liste de toutes les branches avec un certain motif ( "important/*" ).

2.) d=$(git rev-list --first-parent ^${b} HEAD | wc -l); : Pour chacune de ces branches ( $b ), calculer la distance ( $d1 ) en nombre de commits, de HEAD à l'engagement le plus proche dans $b (comme lorsqu'on calcule la distance entre un point et une ligne). Vous pouvez considérer la distance différemment ici : vous ne voudrez peut-être pas utiliser la fonction --first-parent ou la distance entre l'extrémité et l'extrémité des branches ( "${b}"...HEAD ), ...

2.2) d2=$(git rev-list --first-parent ^HEAD ${b} | wc -l); : Pour chacune de ces branches ( $b ), calculer la distance ( $d2 ) en nombre de commits depuis l'extrémité de la branche jusqu'au commit le plus proche en HEAD . Nous utiliserons cette distance pour choisir entre deux branches dont la distance $d1 était égale.

3.) echo "${b} ${d1} ${d2}"; : Imprime le nom de chacune des branches, suivi des distances pour pouvoir les trier ultérieurement (premier $d1 et ensuite $d2 ).

4.) | sort -n -k2 -k3 : Trier le résultat précédent, de sorte que nous obtenons une liste triée (par distance) de toutes les branches, suivie de leurs distances (les deux).

5.) | head -n1 : Le premier résultat de l'étape précédente sera la branche qui a une plus petite distance, c'est-à-dire la branche parentale la plus proche. Il suffit donc d'écarter toutes les autres branches.

6.) | awk '{print $1}'; : Nous ne nous intéressons qu'au nom de la branche, et pas à la distance, donc extraire le premier champ, qui était le nom du parent. Et voilà ! :)

1voto

Rod Points 3357

Git est livré avec un couple de clients GUI qui vous aide à visualiser cela. Ouvrez GitGUI et allez dans le menu Référentiel Visualiser l'historique de toutes les branches .

0 votes

Comment s'ouvre exactement "GitGUI" ? Sur quelle plate-forme (système d'exploitation, etc.) ? Pouvez-vous ajouter quelques références à votre réponse (mais sans "Editer :", "Mettre à jour :", ou similaire - la réponse doit apparaître comme si elle avait été écrite aujourd'hui) ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X