3

How can I convert all the remote branches in a local git repo into local tracking branches, without one by one checking each one out.

One reason why you might want to do this (the reason I want to do this) is so that you can take a clone of the local repo and have in that new clone all the branches from the original remote origin.

Because "clone" only clones local branches.

Edit: a couple of scripted answers have been provided (for which - thanks!) ... I was really hoping for an in-git way, so that it is completely portable (I have users who are "windows only", and so far have survived without having to use a bash (git-bash or otherwise)).

GreenAsJade
  • 14,459
  • 11
  • 63
  • 98
  • You might want to set up the "first" clone (from which you plan to re-clone) as a mirror, rather than a regular working repository. A mirror is like a bare clone except that it copies all refs from the remote directly. (More precisely, it sets the `fetch` refspec to `+refs/*:refs/*` instead of, e.g., `+refs/heads/*:refs/remotes/origin/*`.) – torek Nov 04 '13 at 23:08
  • Hmm - is it "right" that the local developers (who clone from the mirror in this case) will be pushing back to this mirror? (I don't have much of an idea what a "mirror" is, but I had some vague idea that it is read-only) – GreenAsJade Nov 05 '13 at 06:01
  • It's all a question of how you want to work things, but note that you don't have to push to the same URL you pull from. For instance, each repo's `remote.origin.url` has a corresponding `remote.origin.pushurl`. If you want the mirror—let's call it `M` (for mirror), and the "prime" repo `P`, and a developer clone `D`—to be a "pure mirror", then yes, you would not want pushes from `D` to `M` as they'll be lost when `M` fetches from `P`; so `D` should push to `P`. You can set up `M` with a pre-receive hook that says "rejected, push to `P` instead". – torek Nov 05 '13 at 06:09
  • Thanks for your advice. I actually want the developers to push to M, and to be using this as their local "origin". I want to push occasionally (lets say, at release time) to P, and also to fetch from there occasionally if there has been some remote development pushed into P from elsewhere... often when the tools don't let you do something easily, it's because it's a bad idea. Is this a bad idea? – GreenAsJade Nov 05 '13 at 11:40
  • It's messy, but that *can* be done, with some caveats and modifications: (1) keep `M` set up as a mirror, but don't update it from `P` automatically (this lets it accumulate developer changes); (2) use a secondary system to take changes from both `M` and `P` and merge them and send to both `P` *and* `M`; and (3) set `M` up read-only for the duration of step 2, but read/write by developers at other times. As to whether that's a good or bad idea, it depends on too many other variables to say. – torek Nov 05 '13 at 11:56

4 Answers4

5

This answer was supplied to me by jast on #git at freenode:

git push . refs/remotes/origin/*:refs/heads/*

Note: as mentioned in the comment below, this does not create tracking branches, though it does at least make the branchs in the local repo be "local" and note "remote".

GreenAsJade
  • 14,459
  • 11
  • 63
  • 98
  • 1
    I was hoping this would work, but I found the resulting branches were not tracking branches. – cforbish Nov 04 '13 at 13:00
  • Here's a dumb question: how do you know that they are not tracking branches? After I did the above command, the branches behave the way I want... – GreenAsJade Nov 05 '13 at 12:10
  • They are not tracking in that a `git pull` does not automatically point the local branch to the new remote branch location afterwords. Also a `git push` by itself will not know what remote branch to push to. – cforbish Nov 05 '13 at 12:59
  • Yes, I see. Is there a way to tell, without actually trying a push, whether a branch is tracking or not? – GreenAsJade Nov 05 '13 at 13:05
  • 1
    You can run `git config branch.${branch}.remote`. If it returns nothing, it is not tracking. If it returns something (normally `origin`), it has a remote counterpart. Also I gave an answer to your original question. It will setup the remote tracking branches. – cforbish Nov 05 '13 at 13:25
  • Yes, thanks for the answer. As I edited into my question, I have been hanging out for an in-git solution. Around about now I'm thinking there isn't one, which is making me question my original goal... – GreenAsJade Nov 05 '13 at 20:38
4

The best way to do this probably is with a script:

#!/bin/bash
IFS=$'\n'
for branch in `git branch -r`; do
    if [[ ${branch} =~ ^\ *(.+)/(.+)$ ]]; then
        git show-branch "${BASH_REMATCH[2]}" > /dev/null 2>&1
        if [ $? -ne 0 ]; then
            git branch ${BASH_REMATCH[2]} ${BASH_REMATCH[1]}/${BASH_REMATCH[2]}
        fi
    fi
done
cforbish
  • 8,567
  • 3
  • 28
  • 32
  • It's not all that likely for people who have to ask here how to do this, but this code presumes multipart branch names are remotes, which isn't necessarily true. – jthill Nov 07 '13 at 03:57
2

I think that @cforbish's answer could only be improved by saying that with that script you should produce commands like this:

# git branch <local-branch-name> <remote-name>/<remote-branch-name>

For example, if you have the following remote branches:

# git remote -v
  remote-repo <repo-directory> (fetch)
  remote-repo <repo-directory> (push)
# git branch -r
  remote-repo/branch1
  remote-repo/branch2
  remote-repo/branch3

You could have your local tracking branches by running:

# git branch branch1 remote-repo/branch1
# git branch branch2 remote-repo/branch2
# git branch branch3 remote-repo/branch3
# git branch
  branch1
  branch2
  branch3
Community
  • 1
  • 1
Andrés S.
  • 61
  • 4
1

I like shell-command-builders for stuff like this. This is uglier than the earlier version but it also works on bare-bones shells, and has the added advantage of getting the args on branch commands it builds in the right order so they actually work.

One thing -- scriplets like this are "in-git solutions".

git-track-all-remote-branches () 
{ 
    awk '
     $0=="////"{doneloading=1;next}
     !doneloading {drop[$0]=1;next}
     !drop[$0] {
            print "b='\''"$0"'\''; git branch -t ${b##*/} $b"
     }'  <<///EOD///
$(git for-each-ref --format="%(upstream:short)" refs/heads)
////
$(git for-each-ref --format="%(refname:short)" refs/remotes)
///EOD///

}

A fairly recent checkout feature is, if you checkout a bare name that isn't currently a branch, but matches exactly one remote branch, it'll automatically set up a tracking branch for it:

$ git branch
  master
$ git branch -r
  origin/notyet
  origin/master
$ git checkout notyet
Checking out files: 100% (2/2), done.
Branch notyet set up to track remote branch notyet from origin.
Switched to a new branch 'notyet'
jthill
  • 55,082
  • 5
  • 77
  • 137
  • One reason for looking for an in-git solution is that it is more portable - I know that most of the time git users have a bash shell available, but I am working with a team for whom this is a long shot. For the same reason, I'm trying to avoid scary looking scripts :) It's great that git sets up the tracking branch on checkout ... I was hoping not to have to check out every branch just to have it be local and tracking, though! – GreenAsJade Nov 05 '13 at 20:40
  • The number of sometimes-useful five-liners that simple ... I couldn't begin to count. The git devs are after bang-for-the-buck features, where bang can be anything but one rating is `widely-useful * saves-time`. This one's easy to implement and not widely useful. The above took me maybe ten minutes to implement, you can reach competence if not fluency with awk in one good day, and you'll be able to fill your next need in less time than it'd take you to ask for help, let alone get it. I'll make this prettier. – jthill Nov 05 '13 at 21:45