I've added some additional markers for commit4^2
and commit4~
in the commit graph here to make my answer more clear:
* commit4 (merge commit to `main`, via a PR)
|\
| * featureCommit2 (`commit4^2`, ie: the feature branch the person
| | was working on for the PR)
| * featureCommit1
|/
* commit3 (`commit4~`, ie: branch `main` before the PR above merged in)
* commit2
* commit1 (initial commit)
1. graphical depiction of a branch, showing forking and merges
Use git lg
, an alias from Coderwall.com: https://coderwall.com/p/euwpig/a-better-git-log
# Add it as an alias
git config --global alias.lg \
"log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
# Use it
git lg
2. List of only the "primary" commits
Use:
git lg --first-parent # single-line log version
git log --first-parent # full-log version
Either of the commands just above will produce more output information than shown here, but here is the gist of which commits will be output in the example from the question:
* commit4 (merge commit to `main`, via a PR)
* commit3
* commit2
* commit1 (initial commit)
This is from my Q&A here: How to view a linear git log
(exclude feature commits), showing only commits to main
or merges to main
, thanks to @larsks's answer here.
3. A list of just the new/unique commits in someone's PR for any given merge commit
This is essentially the opposite as 2. above, and took me quite some time to think about and figure out.
Use this:
# Show everything in commit4's right parent (the feature branch),
# but NOT in its left parent (branch `main` before the merge)
git lg ^commit4~ commit4^2
# Show everything in commit4 (the merge commit created by the PR merging,
# plus the feature branch that was merged), but NOT in its left parent
# (branch `main` before the merge)
git lg ^commit4~ commit4
In a real example, you would use the full or short commit hashes instead of the word commit4
above. So, it might look like this, for example:
git lg ^a1b2c3d~ a1b2c3d^2
# or
git lg ^a1b2c3d~ a1b2c3d
More specifically:
This:
git lg ^commit4~ commit4^2
...produces this:
* featureCommit2
* featureCommit1
And this:
git lg ^commit4~ commit4
...produces this:
* commit4
* featureCommit2
* featureCommit1
You might think of this as showing only the "right-hand parent" or "right-parent" commits, or as a type of git log --second-parent
(which doesn't exist) command.
If you go online on GitHub, Bitbucket, or Gitlab to the PR which made commit4
, you'll see that it shows only these commits, either commit4
plus featureCommit2
and featureCommit1
, or just featureCommit2
and featureCommit1
, depending on the online system you are using.
Explanation
As I first wrote in my answer here: How to cherry-pick a single commit, multiple commits, or a range of commits:
commit^2
is the immediate right parent of the two parents involved in the merge which created commit
. In this case, this would be the branch of feature commits that the person who opened the PR was working on before merging their PR.
- And
commit~
is the immediate left parent of the two parents involved in the merge which created commit
. In this case, this would be the main
branch as it was before this PR was merged into main
.
The carat (^
) symbol in front of a commit means "not" that commit, or to "exclude" that commit.
So:
commit4
means "commit4",
commit4~
means "one commit before commit4
", AKA: "commit4
's left parent" (if commit4
is a merge commit), and otherwise just "commit4
's only parent".
^commit4~
means "not in commit4
's left parent", and
commit4^2
means "in commit4
's right parent".
So:
git lg ^commit4~ commit4^2
means "show me all commits which are in commit4
's right parent (ie: excluding commit4
itself), but not in commit4
's left parent". This includes all ancestor commits before the specified commit, too, all the way back in time, just as though you had specified a branch instead of a commit hash. And:
git lg ^commit4~ commit4
means "show me all commits which are in or under commit4
(including commit4
itself), but not in commit4
's left parent". Again, this includes all ancestor commits before the specified commit, too, all the way back in time, just as though you had specified a branch instead of a commit hash.