如何组织多个Git仓库,以便它们能够一起备份?

100
使用SVN时,我有一个大型仓库存储在服务器上,并在几台机器上进行检出。 这是一个相当不错的备份系统,使我能够轻松地在任何一台机器上工作。 我可以检出特定的项目,提交并更新“主”项目,或者我可以检出整个项目。
现在,我有一堆针对各种项目的git仓库,其中几个在github上。 我还通过git-svn命令导入了我提到的SVN仓库。
基本上,我喜欢将所有我的代码(不仅仅是项目,还包括随意片段和脚本、一些像我的CV、我写过的文章、我制作的网站等等)放在一个大的仓库中,以便我可以轻松地在远程机器或内存棒/硬盘上克隆,作为备份使用。
问题是,由于它是私人仓库,而且git不允许检出特定文件夹(我可以将其作为单独的项目推送到github,但更改将同时出现在主仓库和子仓库中),所以存在困扰。
我可以使用git子模块系统,但它的行为不符合我的要求(子模块是指向其他仓库的指针,实际上不包含实际代码,因此对于备份没有用处)。
目前,我有一个git仓库文件夹(例如,~/code_projects/proj1/.git/ ~/code_projects/proj2/.git/),在对proj1进行更改后,我执行git push github,然后将文件复制到~/Documents/code/python/projects/proj1/并进行单个提交(而不是在各个仓库中进行无数次提交)。 然后执行git push backupdrive1git push mymemorystick等操作。
所以问题是:您如何使用git仓库管理个人代码和项目,并使它们保持同步并备份?
6个回答

75

我强烈建议不要在给定的Git仓库中放置无关的数据。创建新仓库的开销非常低,这是一种功能,使得可以完全保持不同谱系的分离。

反对这种想法意味着最终会出现不必要的混乱历史记录,这会使管理变得更加困难,并且由于结果稀释了"考古学"工具而变得不那么有用。 此外,正如您所提到的,Git假定“克隆的单位”是仓库,并且实际上必须这样做,因为它是分布式的。

一种解决方案是将每个项目/包等作为其自己的bare仓库(即没有工作树)保存在受保护的层次结构下,例如:

/repos/a.git
/repos/b.git
/repos/c.git

一旦确立了一些约定,对整个层次结构应用管理操作(备份、打包、Web发布)就变得微不足道了,这在某种程度上类似于“单体” SVN 代码库的作用。与此同时,使用这些代码库也会有一些类似于 SVN 工作流的工作方式,但又可以使用本地提交和分支。

svn checkout   --> git clone
svn update     --> git pull
svn commit     --> git push

你可以在每个工作的克隆中拥有多个远程仓库,以便于多个参与方之间的同步:

$ cd ~/dev
$ git clone /repos/foo.git       # or the one from github, ...
$ cd foo
$ git remote add github ...
$ git remote add memorystick ...

你可以从每个“源”进行拉取(fetch) 操作,然后在本地工作和提交(commit),当你准备好时,可以使用以下命令将其推送(备份)到这些远程服务器中的每个服务器(请注意,此操作将相同的提交和历史记录推送到每个远程服务器!):

$ for remote in origin github memorystick; do git push $remote; done

将现有的工作库~/dev/foo变成裸库最简单的方法可能是:

$ cd ~/dev
$ git clone --bare foo /repos/foo.git
$ mv foo foo.old
$ git clone /repos/foo.git

这基本上等同于svn import——但不会丢弃现有的“本地”历史记录。

注意:子模块是一种包含共享的相关历史记录的机制,因此我确实认为它们不是解决您试图解决的问题的合适工具。


19
我总是不断地有很多独立的代码库,还要编写简单的脚本来帮助管理它们,这让我感觉到 Git 中似乎缺少了某些东西。但具体是什么以及该怎么做,我还无法确定。 - DonGar
你也管理许多不同的项目吗?在分布式世界中,项目和存储库之间的一对一关系似乎是合理的,但我仍然会将裸存储库排列在一个共同的目录树中以便于备份和管理。(换句话说,Git/Hg/Bzr强制将管理与项目任务分开,而大多数SVN工作流则混淆了两者;现在常见的做法是将管理部分委托给GitHub或其他类似的提供商。) - Damien Diederen
2
只有在您托管自己的项目和/或它们全部都是开源的情况下,这个想法才有意义。否则,您将需要在 GitHub 上拥有无限制的私人项目,这可能会变得昂贵。 - dkinzer
2
不必使用 "for remote in origin github memorystick; do git push $remote; done" 这样的命令,我们也可以配置一个特殊的远程仓库,通过单个命令将代码推送到多个远程仓库:https://dev59.com/JHVD5IYBdhLWcg3wQZUg#779812 。在某些情况下,这可能更加方便。 - imz -- Ivan Zakharyaschev
2
我认为缺失的东西是git可以通过子树将其对象分开,以便一个“仓库”可以由单独同步但可分离的单元组成(可单独下载而不需要其他内容),使人们可以在不知道其余部分的情况下处理特定子集。 - peterk
@Dongar,老实说,我认为这只是取决于谁编写它以及他们习惯什么,Linux 的人们对编写一些简单的脚本来管理某些东西几乎不在话下,这基本上就是 Unix 的方式。 - ConstantineK

28

我想对Damien的答案进行补充,他推荐如下:

$ for remote in origin github memorystick; do git push $remote; done

你可以设置一个特殊的远程仓库,使用一个命令即可推送到所有真实的远程仓库;我在http://marc.info/?l=git&m=116231242118202&w=2上找到它:

所以对于 "git push" (在多次推送相同分支时有意义),你实际上可以做到像我这样:

  • .git/config 包含:

[remote "all"]
url = master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
url = login.osdl.org:linux-2.6.git
  • 现在git push all master将会把"master"分支推送到这两个远程仓库中的所有仓库上。

  • 你也可以通过以下方式避免重复输入URL:

    [url "<actual url base>"]
        insteadOf = <other url base>
    

    3

    我也对处理这个问题的建议方法很感兴趣,并将描述我使用的当前设置(使用SVN)。 我基本上创建了一个包含迷你文件系统层次结构及其自己的bin和lib目录的存储库。 树的根目录中有一个脚本,可以设置您的环境以将这些bin、lib等其他目录添加到正确的环境变量中。 因此,根目录基本上看起来像:

    ./bin/            # prepended to $PATH
    ./lib/            # prepended to $LD_LIBRARY_PATH
    ./lib/python/     # prepended to $PYTHONPATH
    ./setup_env.bash  # sets up the environment
    

    现在,在/bin和/lib目录中有多个项目及其对应的库。我知道这不是一个标准的项目,但是我们组里的其他人很容易就可以检出仓库,运行'setup_env.bash'脚本,并在他们的本地检出中拥有所有项目的最新版本。他们不必担心安装/更新/usr/bin或/usr/lib,这样就可以轻松地拥有多个检出和每个检出的非常局部化的环境。有人也可以只需删除整个仓库,而不必担心卸载任何程序。
    这对我们来说运作良好,我不确定是否会更改它。问题在于这个仓库中有许多项目。是否有git/Hg/bzr标准方法来创建这样的环境并将项目分解为自己的仓库?

    3
    我还没有尝试嵌套Git存储库,因为我还没有遇到需要这样做的情况。根据我在git频道上读到的#git channel,似乎嵌套存储库会让git感到困惑,也就是说,您正在尝试在git存储库中进行git-init操作。管理嵌套git结构的唯一方法是使用git-submodule或Android的repo实用程序。

    至于您所描述的备份责任,我建议您将其“委派”出去...对我来说,我通常将每个项目的“origin”存储库放在工作的网络驱动器上,由IT技术人员定期进行备份,使用他们选择的备份策略。这很简单,我不必担心它。;)


    2
    使用mr来同时管理多个Git仓库怎么样呢:
    mr(1)命令可以像操作一个合并的仓库一样,对一组仓库进行checkout、update或执行其他操作。它支持任意组合的subversion、git、cvs、mercurial、bzr、darcs、vcsh、fossil和veracity仓库,而且可以轻松添加对其他版本控制系统的支持。它极易通过简单的shell脚本实现配置。以下是它能够完成的一些示例:
    - 更新git仓库时,从两个不同的upstream中pull并将它们合并在一起。 - 并行运行几个仓库更新操作,大大加快更新速度。 - 记住由于离线电脑导致失败的操作,以便在回到在线状态后重试。

    1

    有另一种方法可以拥有嵌套的git仓库,但它并不能解决你想要的问题。不过,对于其他正在寻找我所提供的解决方案的人来说:

    在顶层git仓库中,只需在.gitignore文件中隐藏包含嵌套git仓库的文件夹即可。这样就可以轻松地拥有两个独立的(但嵌套的)git仓库。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接