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:
- define the most recent common ancestor
$ git merge-base master test-branch
c22faff7468d6d5caef217ac6b82f3ed95e9d902
- 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 namecanCommit
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
What's The Difference Between /Usr/Include/Linux and The Include Folder in Linux Kernel Source
/Usr/Bin/Env Questions Regarding Shebang Line Pecularities
Bash Script to Create Symbolic Links to Shared Libraries
How to Build for Linux 32-Bit with Go1.6.2
How to Use Valgrind for Memory Profile
Linux G++ Compiler Redirect Stderr and Stdout Creates Empty File
Best Approach of Image Versioning in Yocto
Output Data Register Value in Nasm
Socket Getting Created with Same Ip and Port on Local Host
Bash Script to Rename All Files in a Directory
How to Set CPU Load on a Red Hat Linux Box
Svn Checkout Fails with "Chunk Delimiter Was Invalid" - What Can Be Done