64

I have a commit xyz in my local branch that I want to check if it is included in a remote release repository; can I do that in some easy way? I could clone the remote repo, but I'm hoping for a nicer+faster way. git ls-remote seemed promising, but found nothing of value to me there. Thanks!

Jonas Byström
  • 25,316
  • 23
  • 100
  • 147
  • Does this answer your question? [How to list branches that contain a given commit?](https://stackoverflow.com/questions/1419623/how-to-list-branches-that-contain-a-given-commit) – phuclv Oct 04 '21 at 03:22

4 Answers4

94

Let's suppose that the remote that refers to the remote repository is called origin. In that case, first update all your remote-tracking branches with:

git fetch origin

Now you can use the useful --contains option to git branch to find out which of the remote branches contains that commit:

git branch -r --contains xyz

(The -r means to only show remote-tracking branches.) If the commit xyz is contained in one or more of your remote-tracking branches, you'll see output like:

  origin/test-suite
  origin/HEAD -> origin/master
  origin/master

If it's contained in your local repository, but not one of the remote-tracking branches, the output will be empty. However, if that commit isn't known in your repository at all, you'll get the error malformed object name and a usage message - perhaps a bit confusing if you're not expecting it...

Mark Longair
  • 446,582
  • 72
  • 411
  • 327
  • 3
    Is there a way to do this with git plumbing? –  Sep 10 '12 at 20:28
  • 1
    @dpk: I'd probably iterate over the remote-tracking branches with `git for-each-ref` and test whether the commit is contained in each branch by checking if `git merge-base ` is the same as ``. – Mark Longair Sep 11 '12 at 08:08
  • https://github.com/git/git/commit/62c0fd46a875050e904831a73ef4375ed26cfaf6 (https://github.com/git/git/commit/3bb0923f06c55ea44569f547cefa9e1a59069ff2) will avoid the error message and the usage message. But only for Git 2.18 (Q2 2018) – VonC Apr 14 '18 at 00:52
  • On a fresh clone of a large repo, I had to also run `git gc` before `git branch -r --contains xyz` would run efficiently. – pqn Mar 20 '21 at 20:20
15

Like Mark said,

 git branch -a --contains commitish

However, beware for branches that contain a cherry-picked/rebased/merged version of the commit.

This could come in handy

 git log --cherry-pick --left-right <commitish> ^remote/branchname

It will list the commit ONLY if it doesn't exist (as a cherrypick) in the remote branch. See the man page for log for an explanation on how --cherry-pick identifies equivalent commits

Of course merges/rebases with conflict resolutions or squashes cannot be automatically detected like this

sehe
  • 374,641
  • 47
  • 450
  • 633
  • the seond line (`cherry-pick`) only works, if the commit contents are equal (apart from white-spaces), which is not necessarily the case, when for example cherry-picking. – hoijui Nov 15 '19 at 15:05
  • @hoijui I would expect whitespace to be significant. When cherry-picking you would expect the change-set to be identical unless there were manual edits (e.g. there were merge conflicts). But yes, cherry-pick detection is limited. It's still very very nice in everyday use – sehe Nov 17 '19 at 18:26
  • 1
    yeah, I think it is nice too, I just wanted to note it here because I tried and coincidentally I stumbled over exactly this issue, and spent a lot of time finding out why my cherry-pick detection does not work. the original commit changed 3 lines, one of them was already done by an other commit, so without any conflict, the cherry-pick had only two lines left. -> not a critique, just a useful hint. – hoijui Nov 18 '19 at 20:01
8

The existing answers require the entire remote repository to be downloaded locally. If the remote has many commits that are not yet cloned locally, this could take a very long time. An example is something like the linux-stable repository, which has many independent branches that are never merged. Someone tracking a stable kernel might clone only the single branch for that kernel. Needing to fetch all the branches, for every stable series kernel, to see if the commit exists would require downloading much more data.

There does not appear to be a good way to do this without fetching the entire remote repo. The ability is there, based on the way git fetch-pack and git send-pack work, but there doesn't seem to be a way to use it in the way desired.

Pushing a branch to a remote repository does not upload commits the remote already has, and this is done without downloading the entire remote repository first. Trying to fetch a remote commit doesn't need to download the entire remote repository to determine if the requested commit exists or not.

The latter can be used to achieve what was asked for in some cases.

git fetch origin <commit ID>

If the remote does not have that commit, then this fails. The remote repository does not need to be cloned locally to do that. If it the remote does have the commit, then it fetches it. There's no option just see if the fetch would work but not fetch anything. Of course, if the commit is already available locally, then nothing needs to be fetched, and this is not a costly operation.

Some remote repositories will not allow something that is not the head of a branch or a tag to be requested. In that case this won't work.

TrentP
  • 4,240
  • 24
  • 35
  • 1
    Unfortunately, this doesn't seem to work if you already have a copy of the commit revision locally. git fetch will only fetch commits missing from your local copy of the repository. – scottclowe Jun 21 '21 at 02:37
0

I looked into your intial proposal here something that may works, and does not requires to donwload any extra file.

git ls-remote "git@gitlab.com:my_awsome_repo" | grep $commit_hash

That you can then process around a bit to check if the thing you looked for was actually a commit and not just random stuff in output

# check that 'commit_hash' is an actual commit
result="$(git ls-remote "git@gitlab.com:my_awsome_repo" | grep $commit_hash)"
if [ -z "$result" ]; then
  echo "Error"
  exit 1
fi

# check that 'commit_hash' is an actual commit
result="$(echo "$result" | sed "s/\t.*$//")"
if [ "$commit_hash" != "$result" ]; then
  echo "Error"
  exit 1
fi

If you have configured an secure link with your repo, it works as well =)

  • 1
    this only works if the commit is the head of a remote ref. if you pushed another commit past the one you're looking for to a branch/tag/GH PR, it will be on the remote but not show up in `ls-remote` – raylu May 19 '23 at 17:50