159

Here are two different questions but I think they are related.

  1. When using Git, how do I find which changes I have committed locally, but haven't yet pushed to a remote branch? I'm looking for something similar to the Mercurial command hg outgoing.

  2. When using Git, how do I find what changes a remote branch has prior to doing a pull? I'm looking for something similar to the Mercurial command hg incoming.

For the second: is there a way to see what is available and then cherry-pick the changes I want to pull?

Benjamin Pollack
  • 27,594
  • 16
  • 81
  • 105
ejunker
  • 10,816
  • 11
  • 41
  • 41
  • 11
    Looking at the answers, there seems to be some confusion as to what `hg incoming` and `hg outgoing` actually do. The nearest Git equivalent I found is the `--dry-run` option. Just `git pull --dry-run` and you'll see a list of all the things that need to happen. – Roman Starkov Sep 29 '12 at 10:16

11 Answers11

118

Starting with Git 1.7.0, there is a special syntax that allows you to generically refer to the upstream branch: @{u} or @{upstream}.

To mimic hg incoming:

git log ..@{u}

To mimic hg outgoing:

git log @{u}..

I use the following incoming and outgoing aliases to make the above easier to use:

git config --global alias.incoming '!git remote update -p; git log ..@{u}'
git config --global alias.outgoing 'log @{u}..'
Richard Hansen
  • 51,690
  • 20
  • 90
  • 97
  • git log ..@{u} gives me these errors. (I have both origin and an upstream repository in my git config). error: No upstream branch found for '' error: No upstream branch found for '..' error: No upstream branch found for '..' fatal: ambiguous argument '..@{u}': unknown revision or path not in the working tree. Use '--' to separate paths from revisions – Henrik Oct 01 '11 at 13:42
  • 6
    You'll get those errors if your local branch isn't configured with an upstream. To fix, run `git branch --set-upstream foo origin/foo`. – Richard Hansen Oct 01 '11 at 18:18
  • `git log @{u}..` lists every single change in the repo for me. There's no way they don't exist yet. – Roman Starkov Sep 28 '12 at 15:14
  • @romkyns: It's possible your local branch has the wrong remote branch configured as the upstream. Make sure `git rev-parse --symbolic-full-name @{u}` prints the appropriate remote reference. Also, `git log @{u}..` shows the commits that aren't reachable by the upstream branch, which can include commits that are already in the remote repository (if they are reachable by a different reference). This will happen right after you merge in an already-pushed branch. – Richard Hansen Sep 28 '12 at 17:22
  • @RichardHansen I'm afraid I'm too noob to know what would be appropriate for a remote reference, however this was a freshly cloned repo on which I did only a `checkout ` and `merge `. At this point, I did the `log @{u}..` and saw every change listed. – Roman Starkov Sep 28 '12 at 17:35
  • @romkyns: If by "saw every change" you mean that you saw all of the commits from ``, then this is expected. Because you just did the merge, the commits on `` are now reachable from your local `` but aren't yet reachable from the remote `origin/`. Thus, these commits are "outgoing" to the remote `origin/` branch. Even though they won't be new to the remote repository, they'll be new to that remote branch. – Richard Hansen Sep 28 '12 at 22:35
  • @romkyns: I realize this is confusing; Git is inherently hard to understand. I highly recommend reading [Pro Git](http://git-scm.com/book) -- it'll help you understand this stuff. – Richard Hansen Sep 28 '12 at 22:37
  • I see, your comment makes it clear. My confusion stems from the fact that `hg outgoing` does not do that. It just lists commits that need to be sent to the remote repo. I think `git push --dry-run` is much closer in spirit to `hg outgoing`. – Roman Starkov Sep 29 '12 at 10:15
  • You still need to `git fetch` all changes before executing `git log ..@{u}` for this to work. – anatoly techtonik Feb 20 '13 at 11:58
  • @anatolytechtonik Indeed. The suggested *incoming* alias includes a `git remote update -p`, which fetches changes. But I'd rather `git fetch` instead, to only fetch changes for the current branch's upstream: `git config --global alias.incoming '!git fetch && git log ..@{u}'` – Myk Melez Apr 24 '17 at 21:23
100

Git can't send that kind of information over the network, like Hg can. But you can run git fetch (which is more like hg pull than hg fetch) to fetch new commits from your remote servers.

So, if you have a branch called master and a remote called origin, after running git fetch, you should also have a branch called origin/master. You can then get the git log of all commits that master needs to be a superset of origin/master by doing git log master..origin/master. Invert those two to get the opposite.

A friend of mine, David Dollar, has created a couple of git shell scripts to simulate hg incoming/outgoing. You can find them at http://github.com/ddollar/git-utils.

Benjamin Pollack
  • 27,594
  • 16
  • 81
  • 105
Jordi Bunster
  • 4,886
  • 3
  • 28
  • 22
44

Not a full answer but git fetch will pull the remote repo and not do a merge. You can then do a

git diff master origin/master
Martin Redmond
  • 13,366
  • 6
  • 36
  • 32
35
  1. Use "git log origin..HEAD"

  2. Use "git fetch" followed by "git log HEAD..origin". You can cherry-pick individual commits using the listed commit ids.

The above assumes, of course, that "origin" is the name of your remote tracking branch (which it is if you've used clone with default options).

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 3
    (And if you’re not tracking the remote branch, it’s “git log origin/master..HEAD”.) – plindberg Aug 17 '10 at 13:16
  • 4
    "origin" is not the name of the remote tracking branch, it's the name of the remote. And just specifying the remote name doesn't work, you have to specify the remote tracking branch, which would be origin/master. – robinst May 25 '11 at 09:53
22

There's also this, for comparing all branches:

git log --branches --not --remotes=origin

This is what the git log man page says about this:

Shows all commits that are in any of local branches but not in any of remote tracking branches for origin (what you have that origin doesn’t).

The above is for outgoing. For incoming, just swap:

git log --remotes=origin --not --branches
robinst
  • 30,027
  • 10
  • 102
  • 108
9

I would do

$ git fetch --dry-run

for hg incoming and

$ git push --dry-run

for hg outgoing.

chris
  • 4,988
  • 20
  • 36
1

git incoming

$ git fetch && git log ..origin/master --stat
OR
$ git fetch && git log ..origin/master --patch

git outgoing

$ git fetch && git log origin/master.. --stat
OR
$ git fetch && git log origin/master.. --patch
prayagupa
  • 30,204
  • 14
  • 155
  • 192
1

git-out is a script that emulates hg outgoing quite accurately. It parses on "push -n" output, so it produces accurate output if you need to specify additional arguments to push.

stepancheg
  • 4,262
  • 2
  • 33
  • 38
0

When the "git log" and @{u} answers initially gave me "unknown revision" errors, I tried out Chris/romkyns suggestion of git push --dry-run.

You will get an output such as "5905..4878 master->master". 5905 is the latest commit that the remote has and commits through (and including) 4878 will be applied to the remote.

You can then use 5905..4878 as arguments to several other git commands to get more details:

git diff 5905..4878 # Gives full code changes in diff style

git log --online 5905..4878 # Displays each commit's comment
pierce.jason
  • 750
  • 6
  • 11
0

Incoming commits across all branches can be shown with the following approach.

The command git fetch-diff becomes available by adding an executable called git-fetch-diff to your PATH, containing:

#!/bin/bash

set -e

# get hashes before fetch
old_hashes=$(git log --all --no-color --pretty=format:"%H")

# perform the fetch
git fetch

# get hashes after fetch
new_hashes=$(git log --all --no-color --pretty=format:"%H")

# get the difference
added_hashes=$(comm -1 -3 <(echo "$old_hashes") <(echo "$new_hashes"))

# print added hashes
[ ! -z "$added_hashes" ] && echo "$added_hashes" | git log --stdin --no-walk --oneline

Commit hashes are compared before and after the fetch. The difference is piped back to git log for pretty printing. The appearance of the printed log can be further tuned to your liking with arguments such as --pretty=<format> and --graph.

Note: You might want to cap how far git log will go back in time depending on how much a bash variable can hold on your system, or for performance reasons. This can be done by adding the argument --max-count=<count>.

gospes
  • 3,819
  • 1
  • 28
  • 31
-1

When you do git fetch, all the contents including branches,tags ( refs) are stored temporarily in .git/FETCH_HEAD whose content can be viewed with command: git log FETCH_HEAD If you don't use suffix -a with git fetch then by default, FETCH_HEAD's content's will be overwritten by new contents. From these contents, you can view and decide to which branch you want to merge them if you do or you can simple cherry-pick if you want only a few commits from what has been brought by fetch.