背景
经常用git都知道,别人有个仓库如在github,轻点fork一下,则这个仓库的所有分支/tags都跑到自己名下的仓库了。但是在更多时候,是没有这个fork按键的,这时需要我们使用命令将仓库的origin备份到myorigin.(假定源仓库为origin,新仓库为myorigin)
一个误区
先来纠正一个误区,那就是很多人使用git push myorigin --all
(假设已经git remote add myorigin myorigin_url了),这个命令只能把本地已经存在的分支推到myorigin,而不能把origin所有的分支推到myorigin.
接下来我们用glide这个仓库来作测试,将glide的git仓库https://github.com/bumptech/glide.git
转移到我新建的测试仓库https://github.com/yanzi1225627/glide_test.git
.
方法一
执行git clone --bare https://github.com/bumptech/glide.git
,然后cd glide.git
,git branch -a
查看:
2.0
3.0
4.0a
gh-pages
* master
palette
一共有6个分支,git tag
查看下tag的信息:
v2.0-alpha
v2.0.0
v2.0.1
v2.0.2
v2.0.3
v2.0.4
v2.0.5
v3.0.0a
v3.1.0a
v3.2.0a
v3.3.0
v3.3.1
v3.4.0
v3.5.0
v3.5.1
v3.5.2
v3.6.0
v3.6.1
v3.7.0
一共19个tag.执行
git push --mirror https://github.com/yanzi1225627/glide_test.git
将本地的这个裸仓库push到自己的仓库上。可以看到git的信息如下:
To https://github.com/yanzi1225627/glide_test.git
* [new branch] 2.0 -> 2.0
* [new branch] 3.0 -> 3.0
* [new branch] 4.0a -> 4.0a
* [new branch] gh-pages -> gh-pages
* [new branch] master -> master
* [new branch] palette -> palette
* [new tag] v2.0-alpha -> v2.0-alpha
* [new tag] v2.0.0 -> v2.0.0
* [new tag] v2.0.1 -> v2.0.1
* [new tag] v2.0.2 -> v2.0.2
* [new tag] v2.0.3 -> v2.0.3
* [new tag] v2.0.4 -> v2.0.4
* [new tag] v2.0.5 -> v2.0.5
* [new tag] v3.0.0a -> v3.0.0a
* [new tag] v3.1.0a -> v3.1.0a
* [new tag] v3.2.0a -> v3.2.0a
* [new tag] v3.3.0 -> v3.3.0
* [new tag] v3.3.1 -> v3.3.1
* [new tag] v3.4.0 -> v3.4.0
* [new tag] v3.5.0 -> v3.5.0
* [new tag] v3.5.1 -> v3.5.1
* [new tag] v3.5.2 -> v3.5.2
* [new tag] v3.6.0 -> v3.6.0
* [new tag] v3.6.1 -> v3.6.1
* [new tag] v3.7.0 -> v3.7.0
把所有的分支和tag都push到自己的仓库了。这种方式实乃迁移git仓库的最正统方式,没有之一!
备注:1,这里第一步的git clone --bare
也可以用git clone --mirror
,这个稍后再讲;2,使用mirror clone下来的东西再push的时候会发现很多被拒绝的refs,这个正常,如下图所示
方法二
鉴于我们通常工作时不会用裸仓库,都是git clone https://github.com/bumptech/glide.git
下载的,这样即包含工作空间(work tree)又包括代码库(.git文件夹).
执行:
git remote add myorigin https://github.com/yanzi1225627/glide_test.git
git push myorigin +refs/remotes/origin/\*:refs/heads/\*
git push myorigin --tags
就ok了,注意这种方法branch和tags是分开推送的,而且会多出一个head分支,这个可以忽略。
关于bare和mirror
1.定时同步
假设我们需要定时从origin取下来东西,然后push到myorigin上。则可以在方法1的基础上,将git clone --bare https://github.com/bumptech/glide.git
换成git clone --mirror https://github.com/bumptech/glide.git
,然后执行:
git remote set-url --push origin https://github.com/yanzi1225627/glide_test.git
git fetch -p origin
git push --mirror
其中第一句话是指定origin对应的push地址,这样remote只有一个origin,但是fetch和push对应不同的地址。第二句话里的-p
的意思是:remove any remote tracking branches that no longer exist remotely
,即如果origin里远端有分支不存在了,则把对应的本地跟踪分支删掉.如果要做同步只需要定时执行后两句就可以了。
2.bare和mirror的区别
共同点
两者clone下来的都是裸仓库,不含工作空间。mirror clone下的空间略大于bare,但是基本一样。这两种方式都能clone下来所有的branch和tag.
区别
- mirror clone下来的东西除了branch和tag外,还有其他refs,如merge request之类的。
- mirror clone的时候会在仓库里指定fetch.
也许大家还不够明白,先来看看官方的解释:
--mirror
Set up a mirror of the source repository. This implies --bare. Compared to --bare, --mirror not only maps local branches of the source to local branches of the target, it maps all refs (including remote branches, notes etc.) and sets up a refspec configuration such that all these refs are overwritten by a git remote update in the target repository.
再来个更直白的解释:
git clone --mirror $URL
这句话等同于下面两句:
git clone --bare $URL
(cd $(basename $URL) && git remote add --mirror=fetch origin $URL)
也就是在bare的基础上,增加了fetch的设置,如图所示:
看到这应该真相大白了!
3.bare和no-bare互转
即如何从裸仓库恢复到带有工作空间的正常目录。
新建一个工程文件夹,然后将bare clone下来的xxx.git名字改为.git
拖到工程文件夹中。然后
git config --local core.bare false
git reset --hard 或 git checkout master
其实懂得了如何bare转no-bare,也完全可以no-bare转bare,这样在带工作空间的目录里,只要对.git目录稍作修改就可以进行备份了。
另外一种思路是参考http://haohetao.iteye.com/blog/1490001,使用如下命令可以快速的在bare和no-bare间转换:
//假设sw目录是带工作空间的git目录
//no-bare 转bare
git clone --bare /home/project/sw sw.git
touch sw.git/git-daemon-export-ok
//bare转no-bare
git clone /home/chen/sw.git sw
但是上述方法切记,在通过git clone --bare /home/project/sw sw.git
导bare时只会将本地存在的分支导出去,跟origin上的分支没有关系,所以这种方式仅供参考,必要时候配合使用.
4.使用裸仓库好处
基本上体积是带工作空间体积的1/2.