1

I want to do something that Gitk already does, except in the command line. I want to display all the commits from one branch, plus all commits from another branch, but no other history. Let's be more specific : I am talking about a feature branch plus the main branch, but no other stuff (however much else there is). How to achieve this ?

By "Commit from a branch" I mean a commit that was explicitly committed (and pushed) on the branch, not one that was brought by a merge. It is enough for me to see that a commit is actually a merge, as I expect to see in the commit message with which other branch this is a merge, which tells me enough here.

To achieve this in Gitk : I create a view in which I list the branches I want to see, and I check "Limit to first parent".

An example.

          L-M : other stuff
             \
            A-B-C-I : feature
               / 
     ... -D-E-F-J : main
      /   /   
     / G-H-K : more other stuff
    /
... potentially even more stuff contributing to main via merges (not explicitly known)
       

C is the result of the merge of B and F.

E is the result of the merge of D and H.

B is the result of the merge of A and M.

I want to see A, B, C, I, D, E, F and J, but not G, H,K, L and M.

            A-B-C-I - feature
               / 
          D-E-F-J - main

Bonus : do not see J either ... Bonus to the bonus : ... but somehow know that F was once main and label it as such.

            A-B-C-I - feature
               / 
          D-E-F - was main

Edit : If I use --first-parent main feature I almost get what I want, except the second parent merge relationships are not shown (even though they exist, Gitk shows them). What now ?

8
  • Firstly, G is part of main and feature (as is L after the edit). It's part of their history. Is there some other branch pointing to K, so you can at least filter out everything reachable from that? Commented Jan 28, 2021 at 13:50
  • Secondly, why did you explicitly ask to see commits on main and then also ask not to see J? Is this a different question? Did you not really want to see main at all, but just commits reachable from both main and feature? Commented Jan 28, 2021 at 13:52
  • Can you describe in more details what you view in gitk ? Commented Jan 28, 2021 at 14:48
  • 2
    No, E has two ancestors, D and H. There is no sense in which git knows that D is "more main" than H (and therefore G). You know this, because it's metadata that you hold in your head. It isn't recorded in the commit history. If you want git to figure this stuff out for you, you need to first figure out how to add enough metadata to your repo that it has something to go on. Commented Jan 28, 2021 at 16:36
  • 1
    @Useless : I agree with your last comment, to the following extent : there is no notion of a branch object in git, and since there are so many ways to rewrite and combine references, you can never be 100% sure about the actual origin of a branch. That being said : one information that generally works, and is recorded in the history of a commit, is the "first parent" relation. And with regards to this: D could be "more main" than H. Commented Jan 29, 2021 at 16:23

2 Answers 2

2

There's no way to 100% reliably meet all of your requirements. LeGEC's answer addresses the specific example in your original question, but I don't guess your follow-up to that comes as much surprise.

For your basic requirement, you can try something like

git log --graph --oneilne --first-parent main feature

and if you're following typical conventions then this will likely give you what you want. But there are circumstances in which it won't. I'll come back to that.

For your "bonus", you can do something like

git log --graph --oneline --first-parent $(git merge-base main feature) feature

So instead of main you say that you want the history up to the last commit that is reachable from both branches.

I don't know a way to dynamically mark the merge base commit in the log (the additional bonus). You could tag your merge bases I guess.

git tag was_on_master $(git merge-base main feature)
git log --graph --oneline --first-parent feature was_on_master
git tag -d waS_on_master

So now... when won't it work, and why is it this way?

In git, commits do not "belong to" branches. There is no history telling you which branch a commit "was created on". The only relationship between branches and commits is that a commit either is, or is not, reachable from a given branch. In the case of merge commits, you can also use the order of the parent pointers to distinguish which "direction" the merge probably happened in.

If you're on branch_A and you say git merge branch_B, then the commit you're on (the history "from" branch_A) will be the first parent of the merge. That's what --first-parent will later follow. And, if when you give the merge command you actually have branch_A checked out (as opposed to having the same commit checked out in detached HEAD state), branch_A automatically moves onto the merge; so the assumption is that typically "first parent means the history of this branch".

But there are situations where someone might do something that messes that up. Suppose you have

... A <--(branch_A)
        
... B <--(branch_B)

and then you say

git checkout branch_A
git merge branch_B
git checkout branch_B
git merge branch_A

Unless your configuration prevents it, the 2nd merge will be handled as a fast-forward, resulting in

... A -- M <--(branch_A)(branch_B)
        /
   ... B

Now no matter which branch is checked out, git log --first-parent will go to A.

Sign up to request clarification or add additional context in comments.

Comments

0

An answer which should fit what you describe on your graph :

git log --oneline --graph main feature ^K ^M

To exclude J, just don't list main :

git log --oneline --graph feature ^K ^M

In git log arguments, ^xxx (note that the ^ is placed before the commit-ish, not after) means "exclude from the list of commits xxx and all of its ancestors"

2 Comments

Interesting. What if I do not know or want to list all the other branches that I do not want to see ? Can I specify (or script) that ?
That would be a script around git for-each-ref

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.