You've cross-tagged this as both git and jgit (and then also java, which seems completely inappropriate here, but I'll leave that to others) and I can only give a pure-git answer, which may be unhelpful.
In Git, a branch—by which I mean the branch name, not the structure—can have at most one upstream set. When a branch has an upstream set, the branch is said to track the upstream. This upstream is normally a remote-tracking branch name, e.g., master
may track origin/master
. The name origin/master
is a remote-tracking branch, so we say that "branch master
tracks remote-tracking branch origin/master
": this has far too many occurrences of the words "branch" and "tracks", each with a different meaning, but we're kind of stuck with it.
A branch that tracks another branch need not track (i.e., have as an upstream) a remote-tracking branch at all. For instance, local branch feature/zorg
might track local branch develop
. The only things that having an upstream does for you are to automate more of fetch
, merge
, and rebase
(and hence pull
which is just fetch
followed by one of the other two), and give you more information when running git branch -v
(or -vv
, etc) and git status
. Of course, these are fairly significant, and hence are a reason to set an upstream.
The upstream—the name of the other branch that the given branch is tracking—is set in two parts: a remote, which is the name of a remote if the upstream is a remote-tracking branch, and a branch (name). If the remote is set to .
, the branch is tracking another local branch.
These two parts may be obtained individually with git config
or git config --get
(both do the same thing):
$ git config --get branch.master.remote
origin
$ git config --get branch.master.merge
refs/heads/master
Note that the second part has not undergone the mapping(s) specified by the fetch
rule for the given remote:
$ git config --get-all remote.origin.fetch
+refs/heads/*:refs/remotes/origin/*
Since this mapping says that refs/heads/*
becomes refs/remotes/origin/*
, we can determine that our name for the remote-tracking branch that corresponds to refs/heads/master
on remote origin
is in fact refs/remotes/origin/master
. (Of course, this mapping is pretty standard and some software might just assume it, but it's better practice to actually do the mapping using the setting(s) for remotes.origin.fetch
. There may be more than one, hence the --get-all
here.)
Again, if the remote
part is set to .
, the upstream is actually a local branch, and no mapping should be applied to the merge
part.
In shell script, then, you could do something like:
branch="${1:-master}"
has_upstream=true
remote="$(git config --get branch.$branch.remote)" || has_upstream=false
merge="$(git config --get branch.$branch.merge)" || has_upstream=false
if ! $has_upstream; then
echo "branch $branch has no upstream"
else
case "$remote" in
.) echo "branch $branch tracks local branch $merge";;
*) echo "branch $branch tracks $merge on $remote";;
esac
fi
This does not help with the mapping, if you'd like to check the remote-tracking branch (you may of course need to run git fetch
to update it), but you can get that using git rev-parse
:
$ git rev-parse --symbolic-full-name master@{u}
refs/remotes/origin/master
Note that you need to strip refs/remotes/
from the result when it is a remote-tracking branch, if you want to use the short form. Of course if the upstream is a local branch, the full name will begin with refs/heads/
rather than refs/remotes/
. If you want the current branch's upstream, a simple @{u}
suffices; the name@{u}
syntax allows you to find other branches' upstream names.
The @{u}
suffix makes rev-parse
fail if there is no configured upstream:
$ git rev-parse --symbolic-full-name target@{u}
fatal: no upstream configured for branch 'target'
which offers another way to tell if an upstream is configured.