How to check real git diff before merging from remote branch?
Since this topic comes up frequently, and can be confusing without a bit of background on how git works, I thought I'd try to explain the simplest case possible, but with sufficient depth that newcomers will have enough of a handle on it to do additional research.
If you set up your git repository via a normal 'clone' and have the default refspecs, meaning that your remote is named 'origin' and you pull/fetch from the branch 'master', you may sometimes need to see what's in the remote repository before pulling it down.
Since the "git pull" does an automatic merge (unless there are conflicts), it can be nice to see what's "incoming" next. If you're not familiar with how git works, and how refspecs in particular are managed, this can be a bit non-intuitive.
Suppose someone makes a change in the remote repository (for sake of illustration, adding a line to the remote repository by committing a change and pushing it), and you type:
$ git diff origin/master
You probably won't see any changes; however if you do the following:
$ git fetch; git diff ..origin/master
you'll see the difference between what's been committed to your local git repository and what's in the remote repository. You will NOT see any changes which are in your local filesystem or staged in your index.
Ok, why do we do this? origin/master is a refspec (see man pages). In short, this is what we refer to in order to compare against, pull or fetch from, and push to. All of the following are functionally equivalent:
origin/master
remotes/origin/master
refs/remotes/origin/master
To begin to untangle this, just take a peek at your repository's .git directory structure. A typical layout looks like this:
.git/refs
.git/refs/heads
.git/refs/heads/master
.git/refs/remotes
.git/refs/remotes/origin
.git/refs/remotes/origin/HEAD
.git/refs/remotes/origin/master
.git/refs/tags
Look at .git/refs/remotes/origin/HEAD; in the default case it will point to the branch you use to pull from and push to. In my case, since I'm on master, the contents of this text file look like this:
ref: refs/remotes/origin/master
This tells me that the HEAD of my remote is identified by the refspec 'refs/remotes/origin/master'(which happens to have the aliases mentioned above).
This doesn't tell us much; what's the state of the remote repository? Look at the state of the remote master:
$ cat .git/refs/heads/master
6d0fb0adfdfa5af861931bb06d34100b349f1d63
Ok, it's a SHA1 hash; probably a commit. How does it get put in this file? Well, whenever you do a pull or a fetch, this file is updated with the most recent commit from the remote which was pulled or fetched. This explains why we have to git fetch
prior to performing the diff. Remember, git fetch
just updates your local copy of a remote branch, but doesn't merge it with your working copy. It is completely safe. A git fetch; git merge
is equivalent to a git pull
.
Once you do the fetch, git will be able to see the most recent commit in the remote repository as of the time of the fetch.
You can use various combinations of specifiers to git to see your diffs as you desire (the following examples use the local working copy as the implicit first commit):
$ git diff remote/origin
This shows the incoming remote additions as deletions; any additions in your local
repository are shown as additions.
$ git diff ...remote/origin
Shows incoming remote additions as additions; the triple-dot excludes changes
committed to your local repository.
$ git diff ..remote/origin
Shows incoming remote additions as additions; the double-dot includes changes
committed to your local repository as deletions (since they are not yet pushed).
For info on ".." vs "..." see git help diff
as well as the excellent documentation at git-scm revision selection: commit ranges Briefly, for the examples above, double-dot syntax shows all commits reachable from origin/master but not your working copy. Likewise, the triple-dot syntax shows all the commits reachable from either commit (implicit working copy, remote/origin) but not from both.
I'm going through this step by step because I'm fairly new to git and this is exactly the type of thing that had me confused... I'm sure that git experts can find flaws with the details... I just hope this answer bridges the gap for some people who find all the various posts a bit terse.
From the documentation:
git diff [--options] <commit>...<commit> [--] [<path>…]
This form is to view the changes on the branch containing and up to the second
<commit>
, starting at a common ancestor of both<commit>
. "git diff A...B" is equivalent to "git diff $(git-merge-base A B) B". You can omit any one of<commit>
, which has the same effect as using HEAD instead.
did you try this?
What you want to do is, as suggested by Evgen Bodunov:
git diff ...remote/branch
This will diff changes from remote/branch and ignore changes from your current HEAD.