Mr. Boy
Mr. Boy

Reputation: 63720

How can I see when Github commits are from a pull-request/merge instead of direct commits?

We prefer a feature-branch workflow with GitHub (like the github workflow mainly). One of my roles is checking this is being done neatly but when I look at https://github.com/<our-org>/<repo>/commits/master I don't see anything demarcating when a commit is directly on the repo Vs when a PR is reviewed and accepted.

I definitely don't see a way to link commits to PRs or similar. Is this possible/easy and am I just missing it on the web UI?

Upvotes: 1

Views: 299

Answers (1)

Schwern
Schwern

Reputation: 164769

The simple answer is that you can tell Github to disallow direct pushes to master. That'll automate your job. All commits to master must come from a PR. https://help.github.com/articles/about-protected-branches/

You can also require that certain automated status checks pass before a PR can be merged like automated code review and CI. https://help.github.com/articles/about-required-status-checks/


The more complicated answer...

There is nothing explicitly different about a PR than any other commit. But depending on how you merge your PRs and view your commits there will be plenty of clues.

git log and the Github commit history lie to you. Git history is interconnected commits with actual branches looking something like this.

A - B - F - G - H - I [master]
     \         /
      ----C - D 

Each letter is a commit. They're connected to each other. The repo starts at A. [master] is a "branch label" but it's really just a label pointing at commit I. We can see that C, D, and E were done as part of a branch and H is the commit that merged it into master.

All this can be seen in the history even after the branch is "deleted" because that just deletes the label. The history can still be seen if you know how to look. git log --graph --decorate --all, and various other repository viewing tools, reveal the true history.

$ git log --graph --decorate --all --oneline

*   f164d0e (HEAD -> master) Merge branch 'feature'
|\  
| * ae7b325 feature 2
| * 562cb51 feature 1
* | 2504fb9 master 4
* | a3e73ae master 3
|/  
* e8bd7c4 commit 2
* 46edb1d commit 1

We can see clearly the merge point and what commits were done in a branch. When you merge a PR Github will add more information to the message of the merge commit such as the PR # so it can be traced back to its source. This is of INVALUABLE use to people later on.

Unfortunately Github and git log (by default) make up a linear history that is neat and easy to read and wrong. This is a source of much of the confusion with Git.

$ git log --all --oneline

f164d0e (HEAD -> master) Merge branch 'feature'
2504fb9 master 4
a3e73ae master 3
ae7b325 feature 2
562cb51 feature 1
e8bd7c4 commit 2
46edb1d commit 1

Aside from the commit messages, all indication of branches and merges is lost. So be sure to look at the true history.


The other way you can lose history is in how you merge your PRs. Squash, rebase, and fast-forward merges will all lose the history for the branch.

Squash

This is the worst. It takes all the commits in the branch and smashes them into one commit. If you had a branch with carefully considered commits and messages forming a clear explanation of why each change was made, it will all be jumbled together.

Before squash

* eb54d0d (feature) feature 2
* 218a926 feature 1
| * 2504fb9 (HEAD -> master) master 4
| * a3e73ae master 3
|/  
* e8bd7c4 commit 2
* 46edb1d commit 1

After squash

* d6054cf (HEAD -> master) Squashed commit of the following:
* 2504fb9 master 4
* a3e73ae master 3
* e8bd7c4 commit 2
* 46edb1d commit 1

While this might be very convenient for the person doing the merge, it's terrible for anyone later trying to figure out what was done in that branch and why.

Rebase

There are many, many good uses for rebase. Updating a feature branch, for example, is better done with a rebase to avoid a lot of unnecessary bookkeeping merges in the history. But when a branch is complete it should be merged, not rebased. Rebasing eliminates that the branch existed.

Before rebase

* eb54d0d (feature) feature 2
* 218a926 feature 1
| * 2504fb9 (HEAD -> master) master 4
| * a3e73ae master 3
|/  
* e8bd7c4 commit 2
* 46edb1d commit 1

After rebase

* aff85f0 (HEAD -> master) feature 2
* aa8bc45 feature 1
* 2504fb9 master 4
* a3e73ae master 3
* e8bd7c4 commit 2
* 46edb1d commit 1

At least the individual commits are still there, but the topological evidence that they were done as a single unit as part of a branch to fix an issue are gone.

Fast Forward

Similar to a rebase, if Git doesn't need to merge, it won't. It'll just move the branch label forward. This happens if there was no work done on master since the branch was made. For example.

Before

* eb54d0d (feature) feature 2
* 218a926 feature 1
* e8bd7c4 (HEAD -> master) commit 2
* 46edb1d commit 1

After fast forward

$ git merge feature

* eb54d0d (HEAD -> master, feature) feature 2
* 218a926 feature 1
* e8bd7c4 commit 2
* 46edb1d commit 1

Upvotes: 1

Related Questions