How to find the nearest parent of a Git branch?

后端 未结 21 1608
野性不改
野性不改 2020-11-22 00:54

Let\'s say I have the following local repository with a commit tree like this:

master --> a
            \\
             \\
      develop c --> d
               


        
相关标签:
21条回答
  • 2020-11-22 00:59

    Here is a PowerShell implementation of Mark Reed's solution:

    git show-branch -a | where-object { $_.Contains('*') -eq $true} | Where-object {$_.Contains($branchName) -ne $true } | select -first 1 | % {$_ -replace('.*\[(.*)\].*','$1')} | % { $_ -replace('[\^~].*','') }
    
    0 讨论(0)
  • 2020-11-22 00:59

    This did not work for me when I had done something like develop > release-v1.0.0 > feature-foo, it would go all the way back to develop, note there was a rebase involved, not sure if that is compounding my issue...

    The following did give the correct commit hash for me

    git log --decorate \
      | grep 'commit' \
      | grep 'origin/' \
      | head -n 2 \
      | tail -n 1 \
      | awk '{ print $2 }' \
      | tr -d "\n"
    
    0 讨论(0)
  • 2020-11-22 01:01

    Remember that, as described in "Git: Finding what branch a commit came from", you cannot easily pinpoint the branch where that commit has been made (branches can be renamed, moved, deleted...), even though git branch --contains <commit> is a start.

    • You can go back from commit to commit until git branch --contains <commit> doesn't list the feature branch and list develop branch,
    • compare that commit SHA1 to /refs/heads/develop

    If the two commits id match, you are good to go (that would mean the feature branch has its origin at the HEAD of develop).

    0 讨论(0)
  • 2020-11-22 01:05

    A solution

    The solution based on git show-branch did not quite work for me (see below), so I've combined it with the one based on git log and ended up with this:

    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
    

    Limitations and Caveats

    • HEAD can be detached (many CI tools do so to ensure they build correct commit in a given branch), but origin branch and local branch have to be both at par or "above" the current HEAD.
    • There must be no tags in the way (I presume; I have not tested the script on commits with a tag between child and parent branch)
    • the script relies on the fact "HEAD" is always listed as the first decoration by the log command
    • running the script on master and develop results (mostly) in <SHA> Initial commit

    The results

     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
    

    Despite local branch existence (e.g. only origin/topic is present since the commit O was checked-out by directly by its SHA), the script should print as follows:

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

    * - or master if develop's commits were on top of master's HEAD (~ the master would be fast-forwardable to develop)


    Why did not show-branch work for me

    The solution based on git show-branch proved unreliable for me in the following situations:

    • detached HEAD – including detached head case means replacing grep '\*' \ for `grep '!' \ – and that is just the beginning of all the troubles
    • running the script on master and develop results in develop and `` respectively
    • branches on master branch (hotfix/ branches) end up with the develop as a parent since their closest master branch parent was marked with ! instead of * for a reason.
    0 讨论(0)
  • 2020-11-22 01:09

    I have a solution to your overall problem (determine if feature is descended from the tip of develop), but it doesn't work using the method you outlined.

    You can use git branch --contains to list all the branches descended from the tip of develop, then use grep to make sure feature is among them.

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

    If it is among them, it will print " feature" to standard output and have a return code of 0. Otherwise, it will print nothing and have a return code of 1.

    0 讨论(0)
  • 2020-11-22 01:11

    @Mark Reed: You should add that the commit line should not only contain an asterisk, but begin with an asterisk! Otherwise commit messages that contain an asterisk are also included in the matched lines. So it should be:

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

    or the long version:

    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 讨论(0)
提交回复
热议问题