简体   繁体   中英

GIT - How to know the branch a branch branched from?

Support I create multiple branches (aaa, bbb, ccc) from a commit. Then, I create a new branch (ddd) from one of the branch (bbb) and a make a commit on it.

Then I pushed everything to a remote. How would another person knows the new branch (ddd) comes from which branch?

The git commands I did was:

git branch aaa
git branch bbb
git branch ccc
git branch ddd bbb
git checkout ddd
echo 2 >> file
git add file
git commit -m "2"

And the git log would show


* commit d259a3b (HEAD, ddd)
|
| 2
|

* commit efb038c (develop, ccc, bbb, aaa)
|
| 1
|

* commit dd24bb6 (master)

It is even possible to know that ddd was branched from bbb?

Thanks

It is indeed impossible in general. Git records only 1 the single commit-ID (SHA-1) to which a reference name (such as a branch or tag) points. The history of that commit is determined solely by that commit's parent IDs, which do not record branch-names. When you push to a remote, your "push" operation sends the SHA-1 of the commit you're pushing (plus more SHA-1s for any other linked objects—trees and files, parent commits, etc—plus the contents of all those objects, as needed based on what's missing on the remote), and asks the remote to set its branch to point to the new SHA-1.

In other words, you tell the remote: "here, have commit 1234567, and set branch label ddd = 1234567". It may tell you "to do that I need five other commits", one of which you have labeled as bbb , but if you don't tell the remote "oh by the way set label bbb to this other commit too" then it won't have that information anywhere.


1 This is a bit of an overstatement: git also records, in the reflog, each SHA-1 that is stored in a label, including branch labels. It is therefore possible to walk back through the label's history to "figure out where it started". There are two limits on this though: reflogs are purely local, never transmitted by fetch or push; and reflogs expire, generally after 90 days in these cases (although this is configurable and there are additional complexities). So as long as we say there's a push step, or allow more than 3 months to pass, there's no way to tell.

add those two functions to your.bashrc:

function newbranch() {
    history_file=".git/branching_history"
    from=`git rev-parse --abbrev-ref HEAD`
    to=$1
    git checkout -b $1 > /dev/null 2>&1
    DATE=`date '+%Y-%m-%d %H:%M:%S'`
    echo "$DATE: from $from created branch $1" >> $history_file
}

function branchinghistory() {
    cat .git/branching_history
}

then when you create a new branch, don't run git checkout -b mybranch but do:

newbranch mybranch

this will store your branching log in .git/branching_history file. You can check the log with:

branchinghistory

the output should be:

2020-04-22 23:59:06: from master created branch mybranch

Branches are nothing more than pointers to a specific commit. You can only see from which commit ddd was branched from, not which branch.

Branches are simply pointers to commits. Because of this, it is not possible to see where a branch originated from, other than observing that two branches have an identical history up to some point.

For someone upstream, it is not relevant where a branch originated from, only that it contains a certain commit (or sequence of commits). This determines how/if git is able to fast-forward or merge branches together.

Append this snippet into your .bashrc or .zshrc files. Pay attention to the real path of your git binary by doing:

$ which git
/usr/local/bin/git

If git has a different path, adapt it into the following snippet.

This way you can register the history of a branch when it is created and rebased onto a new branch:

# keeps track of the branch from which a new branch is created.
# it updates the history of the branch in case of rebase onto
# when the branch is deleted it deletes its history also
# es. (branchA) $ git checkout -b branchB
#     (branchB) $ git branch --onto branchC branchA 
#     (branchB) $ git branch history
#     branchB created from branchA
#     branchB rebased onto branchC
git() {

   if [ "${1}" == "checkout" ] && [ "${2}" == "-b" ]; then
      mkdir -p .git/branches_history
      if [ "${4}" != "" ]; then
        # git checkout -b <new-branch> <from-branch>
        echo "${3} created from ${4}" > .git/branches_history/${3}
      else
        # git checkout -b <new-branch>
        echo "${3} created from $(/usr/local/bin/git branch --show-current)" > .git/branches_history/${3}
      fi
    
   elif [ "${1}" == "rebase" ] && [ "${2}" == "--onto" ]; then
      mkdir -p .git/branches_history
      if [ "${5}" != "" ]; then
         # git rebase --onto <new-base> <old-base> <branch-to-be-rebased>
         echo "${5} rebased onto ${3}" >> .git/branches_history/${5}
      else 
         # git rebase --onto <new-base> <old-base>
         echo "$(/usr/local/bin/git branch --show-current) rebased onto ${3}" >> .git/branches_history/$(/usr/local/bin/git branch --show-current)
      fi
      
   elif [ "${1}" == "branch" ]; then
       if [ "${2}" == "-d" ] || [ "${2}" == "-D" ] || [ "${2}" == "--delete" ] || [ "${2}" == "--force-delete" ]; then
          # git branch -d <branch> | git branch -D <branch>
          rm -rf .git/branches_history/${3} &> /dev/null
       elif [ "${2}" == "history" ]; then
             if [ "${3}" != "" ]; then 
                # git branch history <branch>
                branchName=${3}
             else
                # git branch history
                branchName=$(/usr/local/bin/git branch --show-current)
             fi
             cat .git/branches_history/${branchName}
             # return because git branch history is not a real git command, so git doesn't have to do anything
             return 0
       fi
   fi
   # perform the real git command
   /usr/local/bin/git "${@}"
}

So, imagine that from branchA you create a new branch named branchB :

(branchA) $ git checkout -b branchB

Then, you rebase this new branch on branchC , so the new base branch will change:

(branchB) $ git rebase --onto branchC branchA

To know the base branch of the current branchB just do:

(branchB) $ git branch history

branchB created from branchA
branchB rebased onto branchC

Thanks to @kkarpieszuk for the idea

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM