Check If a Git Branch Is Ahead of Another Using a Script

Check if a git branch is ahead of another using a script

You can use

git log branch2..branch1

If branch1 has no commit ahead of branch2, output will be empty.

You can also use git rev-list --count branch2..branch1

This will show the number of commits ahead

Source

git ahead/behind info between master and branch?

Part 1

As an answer on your question 1, here's a trick I found to compare two branches and show how many commits each branch is ahead of the other (a more general answer on your question 1):

For local branches:
git rev-list --left-right --count master...test-branch

For remote branches:
git rev-list --left-right --count origin/master...origin/test-branch

This gives output like the following:

2 1

This output means: "Compared to master, test-branch is 1 commit ahead and 2 commits behind."

You can also compare local branches with remote branches, e.g. origin/master...master to find out how many commits a local branch (here master) is ahead/behind its remote counterpart.

Part 2

To answer the second part of your question, the solution depends on what exactly you want to achieve.

To view commits

In order to have git rev-list return the exact list of commits unique on either side, replace the --count argument with something like --pretty=oneline, making the complete command to execute:

git rev-list --left-right --pretty=oneline master...test-branch

This will generate output like this:

<bba27b56ad7072e281d529d4845e4edf877eb7d7 unique commit 2 on master
<dad0b69ec50ea57b076bfecabf2cc7c8a652bb6f unique commit 1 on master
>4bfad52fbcf0e60d78d06661d5c06b59c98ac8fd unique commit 1 on test-branch

Here every commit sha is preceded by < or > to indicate which branch it can be found on (left or right, here master or test-branch respectively).

To view code

If you want to view a diff of all new commits only found on either branch, you'll need to do this in two steps:

  1. define the most recent common ancestor
$ git merge-base master test-branch
c22faff7468d6d5caef217ac6b82f3ed95e9d902

  1. diff either branch to the commit sha obtained above (short format will usually do)

To show the diff of all commits only found on master

git diff c22faff7..master

To show the diff of all commits only found test-branch

git diff c22faff7..test-branch

git: programmatically know by how much the branch is ahead/behind a remote branch

update

As pointed out by amalloy, recent versions of git support finding the matching tracking branch for a given branch by giving "branchname@{upstream}" (or "branchname@{u}", or "@{u}" for the tracking branch of HEAD). This effectively supercedes the script below. You can do:

git rev-list @{u}..
git rev-list --left-right --boundary @{u}...
gitk @{u}...

etc. For example, I have git q aliased to git log --pretty='...' @{u}.. to show me "queued" commits ready for pushing.

original answer

There doesn't seem to be an easy way to find the tracking branch in general, without parsing lots more git config than is practical in a few shell commands. But for many cases this will go a long way:

# work out the current branch name
currentbranch=$(expr $(git symbolic-ref HEAD) : 'refs/heads/\(.*\)')
[ -n "$currentbranch" ] || die "You don't seem to be on a branch"
# look up this branch in the configuration
remote=$(git config branch.$currentbranch.remote)
remote_ref=$(git config branch.$currentbranch.merge)
# convert the remote ref into the tracking ref... this is a hack
remote_branch=$(expr $remote_ref : 'refs/heads/\(.*\)')
tracking_branch=refs/remotes/$remote/$remote_branch
# now $tracking_branch should be the local ref tracking HEAD
git rev-list $tracking_branch..HEAD

Another, more brute-force, approach:

git rev-list HEAD --not --remotes

jamessan's answer explains how to find the relative differences between $tracking_branch and HEAD using git rev-list. One fun thing you can do:

git rev-list --left-right $tracking_branch...HEAD

(note three dots between $tracking_branch and HEAD). This will show commits on both "arms" with a distinguishing mark at the front: "<" for commits on $tracking_branch, and ">" for commits on HEAD.

Check if local git repo is ahead/behind remote

In the end, I implemented this in my C++11 git-ws plugin.

string currentBranch = run("git rev-parse --abbrev-ref HEAD"); 
bool canCommit = run("git diff-index --name-only --ignore-submodules HEAD --").empty();
bool canPush = stoi(run("git rev-list HEAD...origin/" + currentBranch + " --ignore-submodules --count")[0]) > 0;

Seems to work so far. canPull still needs to be tested and implemented.

Explanation:

  • currentBranch gets the console output, which is a string of the current branch name
  • canCommit gets whether the console outputs something (difference between current changes and HEAD, ignoring submodules)
  • canPush gets the count of changes between origin/currentBranch and the local repo - if > 0, the local repo can be pushed

Show git ahead and behind info for all branches, including remotes

I've been curious about this as well, so i just whipped up a git branch-status script that gives this information using git for-each-ref

#!/bin/bash
# by http://github.com/jehiah
# this prints out some branch status (similar to the '... ahead' info you get from git status)

# example:
# $ git branch-status
# dns_check (ahead 1) | (behind 112) origin/master
# master (ahead 2) | (behind 0) origin/master

git for-each-ref --format="%(refname:short) %(upstream:short)" refs/heads | \
while read local remote
do
[ -z "$remote" ] && continue
git rev-list --left-right "${local}...${remote}" -- 2>/dev/null >/tmp/git_upstream_status_delta || continue
LEFT_AHEAD=$(grep -c '^<' /tmp/git_upstream_status_delta)
RIGHT_AHEAD=$(grep -c '^>' /tmp/git_upstream_status_delta)
echo "$local (ahead $LEFT_AHEAD) | (behind $RIGHT_AHEAD) $remote"
done

Usage:

$ git branch-status
dns_check (ahead 1) | (behind 112) origin/master
master (ahead 2) | (behind 0) origin/master

How to check if a file is ahead of some previous commit in git

You could list the files changed between two branches (or even two commits):

 git diff --name-only branch1 branch2 | wc -l

That way, you get the number of files edited in branch2 since branch1 (or since commit1, which is the commit where none of your files were edited for translation)

How can we tell which branch is newer in Git?

Use git fetch to get the latest commits from the tip of your remote, then use git status to tell whether or not your remote or local is ahead.

If origin/A is ahead, then when you run git status, you'll be told that your local A branch is behind origin by a commit.

If your local A is ahead, then when you run git status, you'll be told that your local A branch is ahead of origin by a commit.

How can I check whether two branches are even?

To compare the files of a branch called branch located on GitHub against master on the same repository, you can use git diff:

git diff origin/branch origin/master

The following will work to compare the commits between the two branches:

git log --left-right --graph --cherry-pick --oneline origin/branch...origin/master

As mentioned in my comment above, it is possible that two branches can have identical files but different commits.

git status still says “Your branch is ahead of tag1 by N commits.” after git rebase tag2

It seems likely1 that you have accidentally created a branch name and a tag name that both print the same when shortened. [Edit: per update 3, this isn't the problem. Instead, somehow, you have the tag name set as the branch's upstream. I'm not sure how you got into that state—see the result of my own attempt to do that, below.] Once you have two such names, they can hold different hash IDs. The hash ID you get when parsing such a name is a little bit tricky (there are rules, which are outlined in the gitrevisions documentation, but there are exceptions to the rules as well). It's best to get back out of the situation, usually by renaming the inappropriate branch name.

Remember that a branch name like master is really the name refs/heads/master; a tag name like v2.25.0 is really refs/tags/v2.25.0. It is therefore possible to create a branch named v2.25.0 even though the tag exists, because the branch's full name is refs/heads/v2.25.0, not refs/tags/v2.25.0. These two names are different, but if you view the short versions of each, both will be v2.25.0.

The ahead or behind count message from git status is the result of running:

git rev-list --count --left-right <name1>...<name2>

Note that there are three dots between the two names.2 The two names are:

  • name1 is your current branch;
  • name2 is the upstream of your current branch.

The git rev-list command, in this form (using three dots), finds commits that are reachable from the left-side name but not the right-side name, and commits that are reachable from the right-side name but not the left-side name, and then counts (--count) them, but separately (--left-right) rather than combined.

This means that these counts depend on your current branch (of course—that's why it says "your branch ... is ahead of") and the upstream setting. You control the upstream setting with git branch --set-upstream-to, and you can read the upstream of your current branch with:

$ git rev-parse --abbrev-ref @{u}
origin/master
$ git rev-parse --symbolic-full-name @{u}
refs/remotes/origin/master

To help with the case when you have accidentally made both a branch name and a tag name that look the same when abbreviated, use the --symbolic-full-name variant.

The upstream of a branch is [Edit: or should be] always another branch name or remote-tracking name, as tag names are forbidden:

git branch --set-upstream-to=v2.25.0
fatal: Cannot setup tracking information; starting point 'v2.25.0' is not a branch.

Remote-tracking names like origin/master are more typical, but you can set the upstream of any branch to any other branch name.

If the ahead count is nonzero, that's usually what you see, and is what you are seeing here. However, if both counts are nonzero, git status will use the word diverged. If the ahead count is zero and the behind count is nonzero, git status prints the behind count. If both counts are zero—so that the branch is in sync with its upstream—git status says Your branch is up to date with ....

For more about the three-dot syntax, see the gitrevisions documentation. To understand reachability, see Think Like (a) Git. For a short graphic illustration, consider this drawing:

          I--J   <-- branch1
/
...--G--H <-- master
\
K--L <-- branch2, origin/branch1

The name branch1 is "ahead 2" of master because from commit J we walk back to I and then H, which means the I-J commits are reachable from branch1 but not from master. Similarly, branch2 is 2 ahead of master, but its two are commits K-L. This means that master is 2 behind either of branch1 or branch2, with those two commits being I-J or K-L respectively. Meanwhile, branch1 has diverged from origin/branch1 because it is both 2 ahead (I-J) and 2 behind (K-L).


1You can get into similar tricky situations if you move a tag, because tags are meant to be universal across all Git repositories, but tags are also meant never to move. Once one Git repository has a tag, it will tend to assume that the copy that it has is correct, even if someone has forcibly moved a tag in some other Git repository that this Git repository is meant to match. But that would show different symptoms, because you cannot set the upstream of a branch to a tag name.

2The three-dot syntax produces a symmetric difference, in set-theoretical terms. Because this is symmetric, you can swap the two names, as long as you remember that the two counts that git rev-list --count --left-right will print are now swapped as well.

How to show `git status` for branches other than the current one?

To directly answer the question that you posed in the title: you can't run git status in other branches. git status shows you two key pieces of information: how you have changed the index (how you have staged changes) that are different from the most recent commit on your branch, and how you have changed the working directory and made changes that you haven't staged.

In other words, status shows you a combined diff of the HEAD to the index with a diff of the index to the working directory. So it only makes sense to get the equivalent of git status on the current branch, since it takes the index and working directory into account.

With that caveat out of the way, it sounds like you're only interested in ahead/behind count, and you want the ahead/behind count for some other branch compared to its upstream.

Even though ahead/behind count is one of the things git status shows as somewhat ancillary information, it is the easiest way to find the ahead/behind count for the current branch, as you noted.

To show ahead/behind for all branches, you can use the helpful script in this answer.



Related Topics



Leave a reply



Submit