20

假设您有一个带有中央主 git 存储库的场景,开发人员和 CI 引擎从中克隆。即非常接近传统的非分布式版本控制系统设置,具有中央集线器和大量节点。

现在说服务器被盗或被闪电击中或任何其他会导致中央存储库连同其所有集中备份一起消失的事情。您只剩下各种克隆,幸运的是其中一个已完全更新,因此您创建了一个空白的 git 存储库替换服务器,用作未来的中央存储库并开始处理克隆。

完全更新的克隆知道所有带有“git branch -a”的“remotes/origin”分支,但只有一个本地分支。(这让我担心 - 丢失分支信息)。

重新建立一个新的中央 git 存储库的步骤是什么,其行为方式与旧的、分支和所有类似?

4

2 回答 2

20

只需创建一个空存储库,然后从完全更新的克隆中执行以下操作:

git remote add new-origin url://to/new/origin

git push --tags new-origin refs/remotes/origin/*:refs/heads/*

显然,如果新来源与原始来源相同,则必须注意不要从来源获取。

于 2012-08-20T08:48:46.887 回答
8

首先,请不要安装 gitosis:使用最新的V3+ Gitolite,一个更完整的授权层:请参阅“ gitosis vs gitolite? ”和“为什么需要 Gitosis 或 Gitolite? ”。

其次,您不会丢失任何分支信息。

这是解决同一问题的另一种方法,更长,但它说明了您如何仍然保留所有分支。

您的任何克隆都可能没有所有本地分支,没错,但所有这些分支仍位于这些克隆的远程命名空间中,因为它们是从同一个(现已消失的)“中央”存储库克隆的。

一旦你在新的中央服务器上克隆回来(作为一个裸仓库)这些仓库之一,你所需要的只是清理一下它,你可以将它作为新的祝福仓库引用。

注意:如果您无法从服务器访问本地存储库,请捆绑所述本地存储库,并将代表该捆绑包的一个文件复制回您的服务器:您将能够从所述捆绑包中克隆。
请参阅“ git bundle: bundle tags and heads ”以正确创建捆绑包。


简洁版本

# 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

就是这样。准备好出发。


长版(演示)

让我们创建一个 repo,有 4 个分支:masterb1b2b3,每个分支都有自己的文件:

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

现在,如果我克隆r1intor2r2into r3,是的,r3会丢失分支信息:

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

但是在您的情况下,大多数存储库都是从受祝福的存储库中克隆的直接结果。

r2拥有所有必要的分支(一个本地,4 个远程跟踪分支,因为没有“本地跟踪分支”):

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

如果我可以clone --mirror r2进入一个裸仓库 r4,我仍然会得到所有的分支。
请参阅“之间有什么区别git clone --mirrorgit clone --bare”了解原因。

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

它的遥控器仍然指向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)

但这不再需要了。
让我们确保r2(或r1就此而言)不再可访问:

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

现在我们可以通过将本地分支指向远程跟踪分支来恢复本地分支
: 请参阅“将所有远程 git 分支跟踪为本地分支”:

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.

让我们master为该裸存储库创建默认分支:
请参阅“ Git:在裸存储库中更改活动分支的正确方法? ”和“如何更改 Git 远程HEAD以指向除“<code>master”之外的其他内容</a >”。

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)

origin不再需要任何引用“ ”的内容。
让我们摆脱远程跟踪分支:
参见“
删除列出的分支git branch -a”和“删除远程分支?

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).

让我们恐慌并检查我们的本地分支是否仍然引用我们刚刚“删除”的内容:(
参见“在 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

是的,一切都很好。

于 2012-08-24T21:49:24.450 回答