Search code examples
gitgit-branchupstream-branch

How read the current upstream for a git branch


I'm searching a git command to know the upstream associated with an existing branch (if any).
(some kind of "read" command associated with the "write" command git branch --set-upstream-to=...)
The reason is I use a branch connected with several remote repos, and I'd like to check if the branch is already connected with the right upstream before changing it.


Solution

  • TL;DR: use git rev-parse

    $ git rev-parse --abbrev-ref master@{u}
    weird/master
    

    If no upstream is set, you get:

    fatal: no upstream configured for branch 'master'
    

    (and a nonzero exit code). Redirect stderr to /dev/null to discard the error message if you don't want it:

    if master_upstream=$(git rev-parse --abbrev-ref master@{u} 2>/dev/null); then
        master_has_upstream=true
    else
        master_has_upstream=false
    fi
    

    for instance.

    Explanation

    Anthony Sottile's answer usually gets you the correct name, but not quite always. In particular, watch what happens when the remote.origin.fetch setting for origin is not the norm:

    $ git init
    Initialized empty Git repository in .../tmp/tt/.git/
    $ git remote add origin git://github.com/git/git
    $ git config remote.origin.fetch '+refs/heads/*:refs/remotes/weird/*'
    $ git fetch
    remote: Counting objects: 231294, done.
    remote: Compressing objects: 100% (663/663), done.
    remote: Total 231294 (delta 0), reused 662 (delta 0), pack-reused 230631
    Receiving objects: 100% (231294/231294), 93.03 MiB | 3.54 MiB/s, done.
    Resolving deltas: 100% (170261/170261), done.
    From git://github.com/git/git
     * [new branch]          maint      -> weird/maint
     * [new branch]          master     -> weird/master
     * [new branch]          next       -> weird/next
     * [new branch]          pu         -> weird/pu
     * [new branch]          todo       -> weird/todo
     * [new tag]             v2.14.2    -> v2.14.2
    [lots more tags snipped]
    

    Note that while the remote is named origin, the remote-tracking branches are named weird/master, weird/next, and so on. And it actually works:

    $ git checkout master
    Branch master set up to track remote branch master from origin.
    Already on 'master'
    $ git status
    On branch master
    Your branch is up-to-date with 'weird/master'.
    
    nothing to commit, working tree clean
    

    But what's in .git/config still looks like you would expect if the remote-tracking branch name were origin/master:

    [branch "master"]
        remote = origin
        merge = refs/heads/master
    

    Using:

     branch="$(git branch | grep '\*' | cut -d' ' -f2-)"
    

    works well enough (although one should often use git symbolic-ref --short HEAD to get the current branch name: see below).

    remote="$(git config "branch.${branch}.remote")"
    

    This part works perfectly—it gets the name of the remote.

    remote_branch="$(git config "branch.${branch}.merge" | cut -d/ -f3-)"
    

    This is where we go wrong. What we need is to use git rev-parse plus the gitrevisions syntax for "the upstream of a specified branch", which is to append @{u} or @{upstream} to the branch name. Normally git rev-parse turns this into a hash ID, but with --abbrev-ref, it prints a short version of the name, or with --symbolic-full-name, it prints the long version:

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

    (I have no idea why this is spelled --abbrev-ref in one case and --symbolic-full-name in another.)

    Note that when using git rev-parse on HEAD, if HEAD is detached, the answer is the symbol HEAD. That is, in any Git repository, git rev-parse HEAD always succeeds, even when printing symbolic names. This is not true for git symbolic-ref though:

    $ git checkout --detach
    HEAD is now at ea220ee40... The eleventh batch for 2.15
    $ git rev-parse --abbrev-ref HEAD
    HEAD
    $ git rev-parse --symbolic-full-name HEAD
    HEAD
    $ git symbolic-ref HEAD
    fatal: ref HEAD is not a symbolic ref
    

    So for resolving HEAD (to find the current branch), choose which command to use based on the behavior you want in the "no current branch" case.