Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
411 views
in Technique[技术] by (71.8m points)

version control - Where does a Git branch start and what is its length?

Every now and then I'm asked, on what commit a certain branch on git starts or if a certain commit has been created on a specific branch. The end point of a branch is pretty clear: that's where the branch label sits. But - where did it start? The trivial answer would be: on that commit where we created that branch. But that information is, as far as I know now, and that's why I'm asking the question, lost after the first commits.

As long as we know the commit where we branched off, we can draw the graph to make it clear:

A - B - C - - - - J     [master]
     
      D - E - F - G     [branch-A]
           
            H - - I     [branch-B]

I've created branch-B at commit E so that's the "start". I know that, because I did it. But can others recognize it the same way? We could draw the same graph like that:

A - B - C - - - - J     [master]
     
             F - G     [branch-A]
            /
        D - E
             
              H - I     [branch-B]

So, looking at the graph now, which branch started at E, which one at B? Is commit D a member of both branches or can we clearly decide whether it belongs to branch-A or branch-B?

This sounds somewhat philosophical but it really isn't. Supervisors sometimes like to know, when a branch has been started (it usually marks the start of a task) and to which branch some changes belong to (to get the purpose of some change - was it required for the work) and I'd like to know if git offers infos (tools, commands) or definitions to answer those questions correctly.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In Git, you could say that every branch starts at the root commit, and that would be quite literally true. But I guess that's not very helpful for you. What you could do instead is to define "the start of a branch" in relation to other branches. One way you can do this is to use

git show-branch branch1 branch2 ... branchN

and that will show you the common commit between all specified branches at the bottom of the output (if there is, in fact, a common commit).

Here's an example from the Linux Kernel Git documentation for show-branch

$ git show-branch master fixes mhf
* [master] Add 'git show-branch'.
 ! [fixes] Introduce "reset type" flag to "git reset"
  ! [mhf] Allow "+remote:local" refspec to cause --force when fetching.
---
  + [mhf] Allow "+remote:local" refspec to cause --force when fetching.
  + [mhf~1] Use git-octopus when pulling more than one heads.
 +  [fixes] Introduce "reset type" flag to "git reset"
  + [mhf~2] "git fetch --force".
  + [mhf~3] Use .git/remote/origin, not .git/branches/origin.
  + [mhf~4] Make "git pull" and "git fetch" default to origin
  + [mhf~5] Infamous 'octopus merge'
  + [mhf~6] Retire git-parse-remote.
  + [mhf~7] Multi-head fetch.
  + [mhf~8] Start adding the $GIT_DIR/remotes/ support.
*++ [master] Add 'git show-branch'.

In that example, master is being compared with the fixes and mhf branches. Think of this output as a table, with each branch represented by its own column, and each commit getting its own row. Branches that contain a commit will have a + or - show up in their column in the row for that commit.

At the very bottom of the output, you'll see that all 3 branches share a common ancestor commit, and that it is in fact the head commit of master:

*++ [master] Add 'git show-branch'.

This means that both fixes and mhf were branched off of that commit in master.

Alternative solutions

Of course that's only 1 possible way to determine a common base commit in Git. Other ways include git merge-base to find common ancestors, and git log --all --decorate --graph --oneline or gitk --all to visualize the branches and see where they diverge (though if there are a lot of commits that becomes difficult very quickly).

Other questions from original poster

As for these questions you had:

Is commit D a member of both branches or can we clearly decide whether it belongs to branch-A or branch-B?

D is a member of both branches, it's an ancestor commit for both of them.

Supervisors sometimes like to know, when a branch has been started (it usually marks the start of a task)...

In Git, you can rewrite the history of the entire commit tree(s) and their branches, so when a branch "starts" is not as set in stone as in something like TFS or SVN. You can rebase branches onto any point in time in a Git tree, even putting it before the root commit! Therefore, you can use it to "start" a task at any point in time in the tree that you want.

This is a common use case for git rebase, to sync branches up with the latest changes from an upstream branch, to push them "forward" in time along the commit graph, as if you had "just started" working on the branch, even though you've actually been working on it for a while. You could even push branches back in time along the commit graph, if you wanted to (though you might have to resolve a lot of conflicts, depending on the branch contents...or maybe you won't). You could even insert or delete a branch from right in the middle of your development history (though doing so would probably change the commit shas of a lot of commits). Rewriting history is one of the primary features of Git that makes it so powerful and flexible.

This is why commits come with both an authored date (when the commit was originally authored), and a committed date (when the commit was last committed to the commit tree). You can think of them as analogous to create time-date and last-modified time-date.

Supervisors sometimes like to know...to which branch some changes belong to (to get the purpose of some change - was it required for the work).

Again, because Git allows you to rewrite history, you can (re)base a set of changes on pretty much any branch/commit in the commit graph that you want. git rebase literally allows you to move your entire branch around freely (though you might need to resolve conflicts as you go, depending on where you move the branch to and what it contains).

That being said, one of the tools you can use in Git to determine which branches or tags contains a set of changes is the --contains:

# Which branches contains commit X?
git branch --all --contains X

# Which tags contains commit X?
git tag --contains X

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...