Say you have a scenario with a central master git repository, which developers and CI-engines clone from. I.e. very close to a traditional non-distributed version control syste
First, please, don't install gitosis: go with the latest V3+ Gitolite, a much more complete authorization layer: see "gitosis vs gitolite?" and "Why do you need Gitosis or Gitolite?".
Second, you don't loose any branch information.
Here is another approach to the same issue, longer, but which illustrates how you still keep all the branches.
Any of your clone may not have all the local branches, true, but all those branches are still in the remote namespace of those clones, since they have cloned from the same (now gone) "central" repo.
Once you have cloned back (as a bare repo) on the new central server one of those repos, all you need is clean it up a little and you can reference it as your new blessed repo.
Note: if you cannot access to a local repo from your server, bundle said local repo, and copy the one file representing that bundle back to your server: you will be able to clone from said bundle.
See "git bundle: bundle tags and heads" to create the bundle properly.
# Let's re-create a bare "blessed" repo on the server
git clone --mirror /path/to/a/local/repo repo.git
# or git clone --mirror /path/to/repo.bundle repo.git
# restore the local branches
remote=origin ; for brname in `git branch -r | grep $remote | grep -v HEAD | awk '{gsub(/[^\/]+\//,"",$1); print $1}'`; do git branch --set-upstream $brname $remote/$brname ; done
# delete the remotes branches
# (your blessed repo doesn't track anything)
remote=origin ; for brname in `git branch -r | grep $remote | grep -v HEAD | awk '{gsub(/[^\/]+\//,"",$1); print $1}'`; do git branch -r -d origin/$brname ; done
# delete the remote 'origin'
# Not needed for your blessed repo
git remote rm origin
# Let's make sure master is the current branch
# for that bare repo:
git symbolic-ref HEAD refs/heads/master
That's it. Ready to go.
Let's create a repo, with 4 branches: master
, b1
, b2
, b3
, each with their own files:
C:\Users\VonC\prog\git\tests>mkdir c
C:\Users\VonC\prog\git\tests>cd c
C:\Users\VonC\prog\git\tests\c>git init r1
Initialized empty Git repository in C:/Users/VonC/prog/git/tests/c/r1/.git/
C:\Users\VonC\prog\git\tests\c>cd r1
C:\Users\VonC\prog\git\tests\c\r1>echo m > m.txt && git add . && git commit -m "first commit"
[master (root-commit) 1ffe5c1] first commit
1 file changed, 1 insertion(+)
create mode 100644 m.txt
C:\Users\VonC\prog\git\tests\c\r1>git checkout -b b1
Switched to a new branch 'b1'
C:\Users\VonC\prog\git\tests\c\r1>echo f1 > f1.txt && git add . && git commit -m "f1 in b1"
[b1 1e64d01] f1 in b1
1 file changed, 1 insertion(+)
create mode 100644 f1.txt
C:\Users\VonC\prog\git\tests\c\r1>git checkout -b b2 master
Switched to a new branch 'b2'
C:\Users\VonC\prog\git\tests\c\r1>echo f2 > f2.txt git add . && git commit -m "f2 in b2"
[b2 4462b8f] f2 in b2
1 file changed, 1 insertion(+)
create mode 100644 f2.txt
C:\Users\VonC\prog\git\tests\c\r1>git checkout -b b3 master
Switched to a new branch 'b3'
C:\Users\VonC\prog\git\tests\c\r1>echo f3 > f3.txt && git add . && git commit -m "f3 in b3"
[b3 7ada753] f3 in b3
1 file changed, 1 insertion(+)
create mode 100644 f3.txt
Now if I clone r1
into r2
, and r2
into r3
, yes, r3
would loose branch informations:
C:\Users\VonC\prog\git\tests\c>git clone r1 r2
Cloning into 'r2'...
done.
C:\Users\VonC\prog\git\tests\c>git clone r2 r3
Cloning into 'r3'...
done.
C:\Users\VonC\prog\git\tests\c>cd r3
C:\Users\VonC\prog\git\tests\c\r3>git br -a
* b3
remotes/origin/HEAD -> origin/b3
remotes/origin/b3
But in your case, most of the repos out there are the direct result of a clone from the blessed repo.
r2
has all the necessary branches (one local, 4 remote tracking branches, since there is no "local tracking branches"):
C:\Users\VonC\prog\git\tests\c\r2>git br -a
* b3
remotes/origin/HEAD -> origin/b3
remotes/origin/b1
remotes/origin/b2
remotes/origin/b3
remotes/origin/master
If I can clone --mirror r2
into a bare repo r4
, I will still get all the branches.
See "What's the difference between git clone --mirror and git clone --bare" for why.
C:\Users\VonC\prog\git\tests\c>git clone --mirror r2 r4
Cloning into bare repository 'r4'...
done.
C:\Users\VonC\prog\git\tests\c>cd r4
C:\Users\VonC\prog\git\tests\c\r4>git br -a
* b3
remotes/origin/HEAD
remotes/origin/b1
remotes/origin/b2
remotes/origin/b3
remotes/origin/master
Its remote still points to r2
VonC@HOSTNAME ~/prog/git/tests/c/r4 (BARE:b3)
$ git remote -v
origin C:/Users/VonC/prog/git/tests/c/r2 (fetch)
origin C:/Users/VonC/prog/git/tests/c/r2 (push)
But that isn't needed anymore.
Let's make sure r2
(or r1
for that matter) is no longer accessible:
VonC@HOSTNAME ~/prog/git/tests/c/r4 (BARE:b3)
$ cd ..
VonC@HOSTNAME ~/prog/git/tests/c
$ mv r1 r1.old
VonC@HOSTNAME ~/prog/git/tests/c
$ mv r2 r2.old
VonC@HOSTNAME ~/prog/git/tests/c
$ cd r4
Now we can restore the local branches, by pointing them to the remote tracking branches:
See "Track all remote git branches as local branches":
VonC@HOSTNAME ~/prog/git/tests/c/r4 (BARE:b3)
$ remote=origin ; for brname in `git branch -r | grep $remote | grep -v HEAD | awk '{gsub(/[^\/]+\//,"",$1); print $1}'`; do git branch --set-upstream $brname $remote/$brname ; done
Branch b1 set up to track remote ref refs/remotes/origin/b1.
Branch b2 set up to track remote ref refs/remotes/origin/b2.
Branch b3 set up to track remote ref refs/remotes/origin/b3.
Branch master set up to track remote ref refs/remotes/origin/master.
Let's make master
the default branch for that bare repo:
See "Git: Correct way to change Active Branch in a bare repository?", and "How do I change a Git remote HEAD to point to something besides “master”".
VonC@HOSTNAME ~/prog/git/tests/c/r4 (BARE:b3)
$ git symbolic-ref HEAD refs/heads/master
VonC@HOSTNAME ~/prog/git/tests/c/r4 (BARE:master)
Anything referring to 'origin
' is no longer needed.
Let's get rid of the remote tracking branches:
See "Delete branches listed by git branch -a" and "Deleting remote branches?"
VonC@HOSTNAME ~/prog/git/tests/c/r4 (BARE:master)
$ remote=origin ; for brname in `git branch -r | grep $remote | grep -v HEAD | awk '{gsub(/[^\/]+\//,"",$1); print $1}'`; do git branch -r -d origin/$brname ; done
Deleted remote branch origin/b1 (was 1e64d01).
Deleted remote branch origin/b2 (was 4462b8f).
Deleted remote branch origin/b3 (was 7ada753).
Deleted remote branch origin/master (was 1ffe5c1).
Let's panic and check if our local branches do still reference what we just "deleted":
(See "Display the most recent commit of each branch in git")
VonC@HOSTNAME ~/prog/git/tests/c/r4 (BARE:master)
$ git br -v
b1 1e64d01 f1 in b1
b2 4462b8f f2 in b2
b3 7ada753 f3 in b3
* master 1ffe5c1 first commit
Yeap, all good.
Just create an empty repository and, from the fully updated clone, do:
git remote add new-origin url://to/new/origin
git push --tags new-origin refs/remotes/origin/*:refs/heads/*
Obviously, if the new origin is at the same url as the original one, you must be careful to not fetch from origin.