Show commits made on the branch initially (filter out merged commits)

In a git repository with multiple branches, I merged the feature branch into master and merged it back from master into the feature branch. How can I now use git log

to see only the commits that were made in the feature branch?
I need to filter out those comments made in other branches, even though they have been merged back into that feature branch.

$ git status
On branch some_feature

$ git checkout master
$ git merge some_feature
$ git checkout some_feature
$ git merge master

$ # Lots of coding and days gone by

$ git log --some_magic_here

      

If it matters, the machine I want to run git log

on is not the same machine that was used to code this function. I need this feature to do a code review in another developer job. The filter author=SomeDev

doesn't help as I am looking at one specific feature branch.

Thank.

+3


source to share


1 answer


TL; DR: you want --first-parent

(see git rev-list

documentation
) and a few other selection criteria.

Git doesn't keep track of "which branch the commit was made on." For example, let's say you are on a branch B

and you do this:

$ git checkout --detach B
[message about "detached head"]
[edit some file here, and git add result]
$ git commit -m message
$ git branch -f B HEAD
$ git checkout B

      

This will add a new commit to the end of the branch B

, even if the commit was made when it wasn't on the branch at all. Or suppose there are two branches B1

and B2

that point to a commit C7

:

... <- C5 <- C6 <- C7   <-- B1, B2

      

If you are creating a new commit C8

that has a parent C7

then specify what the branch B2

points to C8

, that commit is now on the branch B2

. If you made a new commit while branching B1

then B1

also points to C8

, but if you then use git reset

to move B1

to revert to C7

, it is no longer possible to say 1 that the commit was originally made to B1

, not B2

.

All that said, if you maintain good consistency when merging branches, you can do graph traversals on the commit graph by finding a commit "between" specific merges and then working through one particular parent chain to determine which branch commits were likely made. For example, with feature

and master

, as described, suppose commits are sequentially executed on feature

and then feature

merged periodically into master

c --no-ff

to ensure that each merge-commit on master

is a true merge-commit and not just fast-forward. Then the graph will look something like this:

... --X--*------*-o---o-----*--*   <-- master
       \       /   \       /  /
        A--B--C--D--E--F--G--H     <-- feature

      

(where "time" increases as one moves to the right, and nodes *

are merge commits made on the branch master

, with o

nodes representing other commits on master).

In this case, you can find the commits that were (possibly) made on feature

by starting at the tip feature

(commit H

) and crossing only the primary commits - this is A

through H

, and then discarding the commits that can be found by following only the first parents on master

. This second criterion removes the marked X

above (and any prior commits). (Note that in the case of a merge, a commit is E

its first parent D

and its second parent is the o

commit above it from left to master

. For all merges to, the master

first parent is on the same line, and the second parent is below and left on feature

.)

How much of this is enough depends on how much you disciplined you (and others) in the fixation process. If it master

has its own internal branches and merges, you might have a more similar graph:

... --X---Y---*-----*-o---o---------*--*   <-- master
       \   \ /     /   \           /  /
        \   Z     /     \         /  /
         \       /       \       /  /
          A--B--C--D------E--F--G--H       <-- feature

      

Now you can't just use the first or not the first parent test as easily as commit is Y

or is Z

necessarily the second parent of the merge on master

, but note that is Z

not available from feature

without a first pass E

, which is a "directly on" merge commit feature

whose second parent commit is on master

.



In other words, I use the slightly peculiar phrase "directly on" branches to identify the commits that can be found by starting at the tip of the branch and working backwards only through the first parent. For feature

this (still) A

through H

, plus, unfortunately, X

his (first) parents.

The command git rev-list

that is one of git's very central commands is kind of a big sister version git rev-parse

that is also very central, has a flag that specifically implements this "right on" concept, viz --first-parent

. The rev-list command goes 2 through some part (s) of the commit graph: you tell it about some commit and it discovers that the parents are both the commit parents and the parents of the parents and their parents, and so on. If you specify --first-parent

, it will only find the first parent for each commit. (This obviously only affects merge commits, since by definition an odd merge has at most one parent commit.)

In your specific case, you also said that you want to remove the cream on the branches feature

, i.e. commit that are directly on feature

. To do this, just say rev-list to exclude the merge commit from its output, with --no-merges

.

Eliminating the commit is a X

little more difficult before, but it turns out to be not that hard if you know which branch contains the commits X

you want to exclude are included. The argument --not

(including several alternate spellings) tells rev-list to exclude. So:

git rev-list --first-parent feature --not master --no-merges

      

identifies a list of commits "directly on" ( A

through H

), but not directly on master

( X

and before), and then only lists those that are not merged (thus excluding the commit E

).

You probably want to continue with this procedure, for example using commit timestamps ( --since

and --until

). You may (or may not) change the order in which the revisions are listed, perhaps using --topo-order

to put them in graph order rather than date order, for example.

Combining this together with git log

Luckily for you, it git log

effectively calls git rev-list

with your revision specifiers. So instead of first using git rev-list

and then somehow feeding the results to git log

, you can just give the same specifiers to git log

. In fact, when you determine what delimiters you want (with things like --since

and --until

), it is git log

much easier to work with , since a large pile of raw SHA-1 is not very useful for humans.


1 In fact, the logs that keep the history of branch movements do give you the ability to tell, but only on one local machine; the question said to do this on a different system where databases are not available. In any case, reflog entries will expire after some time, 90 days by default for those of interest.

2 Unless, of course, you are using --no-walk

.

+4


source







All Articles