In our workflow, no \"direct\" commits are made into the master branch. The master branch only receives merges from Pull Requests.
We can think of each merge then as a n
I use the following script:
git log --merges --first-parent master \
--pretty=format:"%h %<(10,trunc)%aN %C(white)%<(15)%ar%Creset %C(red bold)%<(15)%D%Creset %s"
Explaining each argument:
--merges
: only "merge" commits (more than 1 parent);--first-parent master
: only merges applied to master
. This removes the entries where someone merged master
into their branches;--pretty-format
: applies the following formatting:
%h
: the commit short hash;%<(10,trunc)%aN
: author name, truncated at 10 chars;%<(15)%ar
: the relative commit time, padded to 15 chars;%<(15)%D
: the tag names, also padded to 15 chars;%s
: first line of the commit message.The result is pretty satisfying:
Git exposes such feature through the git log command. This command accepts some switches that filter the rendered commits according to the number of parents commits.
One of them would fit your request:
--min-parents=2
.The following shows the merge commits (ie. commits with more than one parent) reachable from the vNext
branch of the LibGit2Sharp project
$ git log vNext --merges --oneline
090b2de Merge pull request #348 from jamill/credential_callback_fix
0332c35 Merge pull request #260 from ben/great-renaming
3191c1b Merge pull request #239 from ben/libgit2-upgrade-81eecc3
1d544e8 Merge branch 'vNext'
238d259 Merge remote-tracking branch 'origin/master'
Leveraging the same output through the GitHub API is possible, but would be somewhat more complex.
This would require to retrieve all the commits from a branch, paginating through all the results (in order to retrieve all the commits meta data) while filtering out the ones that only expose only one parent node.
As a starting point, the following url shows the latest 30 commmits of the vNext
branch.
If you want to focus on merge commits which are the result of pull requests being merged, you might consider the new Git 2.27 (Q2 2020) git log --show-pulls
option.
"git log
" has learned "--show-pulls
" that helps pathspec limited history views; a merge commit that takes the whole change from a side branch, which is normally omitted from the output, is shown in addition to the commits that introduce real changes.
See commit 8d049e1 (10 Apr 2020) by Derrick Stolee (derrickstolee).
(Merged by Junio C Hamano -- gitster -- in commit 9af3a7c, 22 Apr 2020)
revision: --show-pulls adds helpful merges
Signed-off-by: Derrick Stolee
The default file history simplification of "
git log -- <path>
" or "git rev-list -- <path>
" focuses on providing the smallest set of commits that first contributed a change.
The revision walk greatly restricts the set of walked commits by visiting only the first TREESAME parent of a merge commit, when one exists. This means that portions of the commit-graph are not walked, which can be a performance benefit, but can also "hide" commits that added changes but were ignored by a merge resolution.
(TREESAME: no discernible difference for the pathspec among the respective trees; "the same between trees")
The
--full-history
option modifies this by walking all commits and reporting a merge commit as "interesting" if it has any parent that is not TREESAME.
This tends to be an over-representation of important commits, especially in an environment where most merge commits are created by pull request completion.Suppose we have a commit
A
and we create a commitB
on top that changes our file.
When we merge the pull request, we create a merge commitM
.
If no one else changed the file in the first-parent history betweenM
andA
, thenM
will not be TREESAME to its first parent, but will be TREESAME toB
. Thus, the simplified history will be "B
". However, M will appear in the--full-history
mode.However, suppose that a number of topics
T1
,T2
, ...,Tn
were created based on commitsC1
,C2
, ...,Cn
betweenA
andM
as follows:A----C1----C2--- ... ---Cn----M------P1---P2--- ... ---Pn \ \ \ \ / / / / \ \__.. \ \/ ..__T1 / Tn \ \__.. /\ ..__T2 / \_____________________B \____________________/
If the commits
T1
,T2
, ...Tn
did not change the file, then all ofP1
throughPn
will be TREESAME to their first parent, but not TREESAME to their second.
This means that all of those merge commits appear in the--full-history
view, with edges that immediately collapse into the lower history without introducing interesting single-parent commits.The
--simplify-merges
option was introduced to remove these extra merge commits.
By noticing that the rewritten parents are reachable from their first parents, those edges can be simplified away.
Finally, the commits now look like single-parent commits that are TREESAME to their "only" parent. Thus, they are removed and this issue does not cause issues anymore.However, this also ends up removing the commit
M
from the history view!
Even worse, the--simplify-merges
option requires walking the entire history before returning a single result.Many Git users are using Git alongside a Git service that provides code storage alongside a code review tool commonly called "Pull Requests" or "Merge Requests" against a target branch.
When these requests are accepted and merged, they typically create a merge commit whose first parent is the previous branch tip and the second parent is the tip of the topic branch used for the request.
This presents a valuable order to the parents, but also makes that merge commit slightly special. Users may want to see not only which commits changed a file, but which pull requests merged those commits into their branch.
In the previous example, this would mean the users want to see the merge commit "M
" in addition to the single- parent commit "C
".Users are even more likely to want these merge commits when they use pull requests to merge into a feature branch before merging that feature branch into their trunk.
In some sense, users are asking for the "first" merge commit to bring in the change to their branch. As long as the parent order is consistent, this can be handled with the following rule:
"Include a merge commit if it is not TREESAME to its first parent, but is TREESAME to a later parent."
These merges look like the merge commits that would result from running "git pull <topic>" on a main branch.
Thus, the option to show these commits is called "--show-pulls
".
This has the added benefit of showing the commits created by closing a pull request or merge request on any of the Git hosting and code review platforms.To test these options, extend the standard test example to include a merge commit that is not TREESAME to its first parent. It is surprising that that option was not already in the example, as it is instructive.
In particular, this extension demonstrates a common issue with file history simplification. When a user resolves a merge conflict using "
-Xours
" or otherwise ignoring one side of the conflict, they create a TREESAME edge that probably should not be TREESAME.
This leads users to become frustrated and complain that "my change disappeared!"In my experience, showing them history with
--full-history
and--simplify-merges
quickly reveals the problematic merge.
As mentioned, this option is expensive to compute.The
--show-pulls
option might show the merge commit (usually titled "resolving conflicts") more quickly.
Of course, this depends on the user having the correct parent order, which is backwards when using "git pull master" from a topic branch.There are some special considerations when combining the
--show-pulls
option with--simplify-merges
.
This requires adding a newPULL_MERGE
object flag to store the information from the initial TREESAME comparisons. This helps avoid dropping those commits in later filters. This is covered by a test, including how the parents can be simplified. Since "struct object
" has already ruined its 32-bit alignment by using 33 bits across parsed, type, and flags member, let's not make it worse.
PULL_MERGE
is used in revision.c with the same value (1u<<15
) as REACHABLE
in commit-graph.c.
The REACHABLE flag is only used when writing a commit-graph file, and a revision walk using --show-pulls
does not happen in the same process. Care must be taken in the future to ensure this remains the case.
Update Documentation/rev-list-options.txt with significant details around this option. This requires updating the example in the History Simplification section to demonstrate some of the problems with TREESAME second parents.
See a full example here.