Reputation: 1229
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 ?
Upvotes: 1
Views: 1395
Reputation: 51810
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"
Upvotes: 0
Reputation: 45659
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
.
Upvotes: 2