Knowing from which branch the current branch was created from

We are using a forking workflow. If a user, from his fork, wants to merge something into the master repository, I want to validate the source of his code.

 

For example, if he wants to merge his code in the branch 'releases/3.0.2/', I want to make sure that his branch 'Feature_x_y_z' was branched from the branch 'releases/3.0.2/'.

 

Is there a way to check from which branch the pull request's source branch was created from?

4 answers

1 accepted

In git, branches are just pointers to a commit and a commit is considered to be 'on a branch' if it's an ancestor of the commit the branch ref is currently pointing to. As such branch memberships are fluent. A commit that is only on your feature branch today, will be on master when you merge it. In about a years time, it will be 'on' all open feature branches at that time (because it's in the history leading up to those branches). If you've deleted the original feature branch it was created on, it will no longer be on that branch.

Git does not track what branch a commit was created on and does not track where a branch was branched off from. There is no information in git that you can use answer that question.

You can do a best guess by finding the 'closest' branch (smallest distance to a common ancestor), but this can easily lead to the wrong conclusion. Consider this case:

o - o - o - o - o - o <- master
  \    \
   \    o <- feature/a
    o <- feature/b

In the diagram it looks clear that feature/a and feature/b must be branched off from master, but alas.. in git's data model this is identical to:

o - o - o <- feature/a
  \    \
   \    o - o - o - o <- master
    o <- feature/b

and in this diagram, it looks like master and feature/b are branched off from feature/a

You simply cannot determine the 'parent branch' of a branch in git. If you really want it, you could consider adding meta-data to each commit (using git notes) to track on what branch it was created. With that information, you could answer the question. But it's a cumbersome process that is hard/inconvenient to enforce.

 

Agree that that's a great answer, but... it doesn't seem like such a bone-headed question to me that there can't be an answer. I see lots of companies that want to manage the flow of change through their branches. Is there a better way of accomplishing this?

I know there's a plugin that will show gitview diagrams so that the master, dev, etc, branches are displayed as they really were - I assume it must do some version of adding the branch-created-on info into the commit metadata.

gitk can determine correctly, the parent branch of a branch. So there must be a way. I'm looking for it :)

Ali Kazi I'm New Here Oct 24, 2017

How do software like SourceTree show the visual info then? They must be getting this kind of data from somewhere even though it might be complicated.

2 votes

As Balazs says... this is a much harder question than you might think. http://stackoverflow.com/a/3162929/1018918 has some good info.

In that answer the author substitutes two better questions, which you could implement using the Stash API. Is the context for this a mergecheck plugin or a pre-receive hook or something?

You might be able to just check that all the commits in the PR (given a source ref and target ref) are on the PR branch.... I think that may be sufficient.

Update:

Given:

image2015-5-12 20:20:4.png

When I create a PR from feat1 to releases/3.0.2, it shows me two commits. When I change the target to master, it shows me 3 commits. 

You can get this info from com.atlassian.stash.commit.CommitService#getCommitsBetween, where the CommitsBetweenRequest has a ctor for a PR, or from PullRequestService if you have an already created PR.

If these are the same commits as those between releases/3.0.2 and the fromRef in my PR, then that's where I branched from... although I may have intermediate branches on the way, but I'm not sure that matters.

However, if master was pointing to the same commit as releases/3.0.2, then it would be true for master.

I'm interested in the answer to this too, and I wonder if there is not a much better way. I hate to mention people but I think a good person for this question is @Michael Heemskerk.

 

I think I did not understand what you meant. Check if the commits in the PR are on the PR branch? You mean check if the commits in the PR (which are from the 'Feature_x_y_z' branch) are in the 'releases/3.0.2/' branch? They won't be. The commits in the PR are only in the feature branch.

When you create a PR it works out what commits need to be merged from source to target branch. If that list are all on the feature branch, I think that's what you want.

i told you it was tricky... answer updated.

There is no way to check this built into Stash. You can use git log to figure this out, but it gets rather complicated.

0 votes

@Michael Heemskerk - thanks for your full answer.

Can we consider another case? Let's say we have a pre-commit hook, whereby we want to examine each commit that is pushed, imagine we want to check that it's signed or the commit message is greater than 2 characters or something. That sounds like a possible requirement.

In the case of a new branch feature/a being added with a commit:

o - o - o - o - o - o <- master
       \
        o <- feature/a

The refChange we get will look like [ 00000000 .. A ] , assuming A is the sha1 for the tip of feature/a.

Using commitService to get the new commits we will get all the ancestors of feature/a too, which is obviously not acceptable. 

We can get the commits on feature/a using:

git rev-list feature/a ^master ^refs/heads/branch1 ... ^refs/heads/branchN

ie using a CommitsBetweenRequest and excluding every other branch than feature/a. This works AFAICT but seems "wrong". This is the approach used here: http://engineering.onlive.com/2014/04/18/why-post-receive-git-hooks-are-broken/

I get that under the covers Stash is using standard git hooks, and it passes on all the information the hook receives, but I wonder if there is scope for Stash being more clever and providing information about the list of commits.

Presumably this is possible, as at some point git goes through the pushed commits and squirts them on to the correct parent. If we had that parent for each RefChange life would be simple.

There must be a hook somewhere that examines each commit, and works for the case of newly-added branches - does anyone know of one?

 

 

 

 

 

bq. Let's say we have a pre-commit hook, ... Nitpicking, but to be exact, it's pre-receive hook. A pre-commit hook runs in your local repo when you do {{git commit}} bq. I get that under the covers Stash is using standard git hooks, and it passes on all the information the hook receives, but I wonder if there is scope for Stash being more clever and providing information about the list of commits. As you said, Stash is using 'plain' git hooks. We do have some smarts to detect which commits have been added, but those smarts are applied at a later stage (during indexing) and can't be used here. Given that you get a list of {{RefChange}}, it's not too hard to retrieve all commits that are about to be added by this push. Remember that when the pre-receive-hook is called, the refs haven't been updated yet. They're still pointing to the old values. This means that the following git command should only return the commits that are about to be added: {code} git rev-list <sha1> <sha2> <sha3> --not --all {code} where {{<sha1>}}, {{sha2}} and {{sha3}} are the {{RefChange.getToHash()}} hashes for the provided {{RefChange}}s. The {{--not --all}} means that all current refs (branches and tags) are excluded. So, this command reads as: 'give me all commits that are reachable from sha1, sha2 or sha3 and are not reachable from any current ref'

Thank you... do you know how to do that in a post-receive hook where the refs have already been updated?

Perhaps there is not a good answer to this question, as hipchat notifications from stash seem to suffer from this problem: https://jira.atlassian.com/browse/STASH-7455

Suggest an answer

Log in or Join to answer

Stay in touch

Be the first to know what's trending on Atlassian Community