Check if local git repo is ahead/behind remote
From this answer.
- Do a fetch:
git fetch
. - Get how many commits current branch is behind:
behind_count = $(git rev-list --count HEAD..@{u})
. - Get how many commits current branch is ahead:
ahead_count = $(git rev-list --count @{u}..HEAD)
. (It assumes that where you fetch from is where you push to, seepush.default
configuration option). - If both
behind_count
andahead_count
are 0, then current branch is up to date. - If
behind_count
is 0 andahead_count
is greater than 0, then current branch is ahead. - If
behind_count
is greater than 0 andahead_count
is 0, then current branch is behind. - If both
behind_count
andahead_count
are greater than 0, then current branch is diverged.
Explanation:
git rev-list
list all commits of giving commits range.--count
option output how many commits would have been listed, and suppress all other output.HEAD
names current branch.@{u}
refers to the local upstream of current branch (configured withbranch.<name>.remote
andbranch.<name>.merge
). There is also@{push}
, it is usually points to the same as@{u}
.<rev1>..<rev2>
specifies commits range that include commits that are reachable from but exclude those that are reachable from . When either or is omitted, it defaults to HEAD.
You can do this with a combination of git merge-base
and git rev-parse
. If git merge-base <branch> <remote branch>
returns the same as git rev-parse <remote branch>
, then your local branch is ahead. If it returns the same as git rev-parse <branch>
, then your local branch is behind. If merge-base
returns a different answer than either rev-parse
, then the branches have diverged and you'll need to do a merge.
It would be best to do a git fetch
before checking the branches, though, otherwise your determination of whether or not you need to pull will be out of date. You'll also want to verify that each branch you check has a remote tracking branch. You can use git for-each-ref --format='%(upstream:short)' refs/heads/<branch>
to do that. That command will return the remote tracking branch of <branch>
or the empty string if it doesn't have one. Somewhere on SO there's a different version which will return an error if the branch doesn't haven't a remote tracking branch, which may be more useful for your purpose.
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
For future reference. As of Git v2.17.0
git status -sb
contains the word behind . So that can be used directly to check for pulls.
Note: Remember to run git fetch
before running git status -sb