一个目录中可以有两个Git仓库吗?

121

一个目录可以有2个git仓库吗?我觉得不行,但还是想问一下。基本上,我想要检入我的主目录配置文件(例如.emacs),这些文件应该在我工作的所有机器上都是通用的,但还想要另一个仓库来存储本地文件(例如.emacs.local),其中包含特定于机器的配置。我能想到的唯一方法是将本地配置放在子目录中,并从主git仓库中忽略该子目录。还有其他想法吗?


1
git subtree 可以完成这项工作。 - Sydalmighty
1
如果你没有处理太多的文件,你也可以创建符号链接/联接。 - thdoan
12个回答

220

这篇文章讲得相对详细:

https://github.com/rrrene/gitscm-next/blob/master/app/views/blog/progit/2010-04-11-environment.markdown

如果你是从命令行工作,那么这比你想象的要简单。假设你想要两个 git 仓库:

.gitone
.gittwo
您可以这样设置:

您可以这样设置:

git init .
mv .git .gitone
git init .
mv .git .gittwo
您可以这样将文件添加并提交到一个分支:

您可以这样将文件添加并提交到一个分支:

git --git-dir=.gitone add test.txt
git --git-dir=.gitone commit -m "Test"

所以git的选项首先出现,然后是命令,最后是git命令的选项。你可以很容易地给git命令起一个别名,例如:

因此,选项之前先写git,命令在中间,然后是git命令的选项。你可以简单地为git命令设置别名,比如:

```bash git config --global alias. ```

请注意替换<alias-name>和<git-command>。

请注意,这里使用了反斜杠字符`\`来转义特殊字符,以确保在Shell中正确解释命令。

请注意,--global选项会将别名添加到全局.gitconfig文件中。

注意: 以上内容仅供参考,请根据您的具体需求进行修改。

#!/bin/sh
alias gitone='git --git-dir=.gitone'
alias gittwo='git --git-dir=.gittwo'

因此,您可以通过更少的打字来选择其中之一进行提交,例如 gitone commit -m "blah"

似乎变得更加棘手的是忽略文件。由于 .gitignore 通常位于项目根目录中,您需要找到一种方法来切换它,而不会切换整个根目录。或者,您可以使用 .git/info/exclude,但是您执行的所有忽略操作都不会被提交或推送 - 这可能会损坏其他用户。其他使用任何一个仓库的人可能会推送 .gitignore,这可能会导致冲突。对我来说,最好的解决方法还不清楚。

如果您喜欢像 TortoiseGit 这样的 GUI 工具,您也会遇到一些挑战。您可以编写一个小脚本,将 .gitone 或 .gittwo 临时重命名为 .git,以满足这些工具的假设。


1
将其设置为别名会抛出以下错误: $ git config --global alias.pub '--git-dir=~/Server/www/.gitpublic' $ git pub add . fatal: alias 'pub' changes environment variables 您可以在别名中使用“!git”来解决此问题。在这种情况下,您应该在哪里设置“!git”? - JaredBroad
2
@JaredBroad 这很有意思 - 我建议你使用Bash alias,而不是git alias。像 alias gitone='git --git-dir=.gitone' 这样的。 - Chris Moschini
15
您可以配置仓库使用它们自己的排除文件,并跟踪这些文件,例如:gitone config core.excludesfile gitone.excludegitone add gitone.exclude。我制作了一个扩展此解决方案的脚本:https://github.com/capr/multigit。 - user519179
2
@JaredBroad 我是这样做的 git config --global alias.youralias '!git --git-dir="/d/MyProject/_git"' 然后 git youralias status :) - starikovs
1
如果你假设.gitignore文件通常是设置并忘记的,那么你可以为每个repo制作不同的副本,然后将相关版本作为别名的一部分复制到目录中。这也适用于根目录中可能会发生冲突的其他文件,例如README.md.gitattributes - thdoan
显示剩余4条评论

46
如果我理解你所做的内容,你可以在一个存储库中处理所有内容,为每台机器使用单独的分支,并使用包含通用主目录配置文件的分支。初始化仓库并将公共文件提交到其中,可能将MASTER分支重命名为Common。然后从那里为您使用的每台机器创建一个单独的分支,并将特定于该机器的文件提交到该分支中。每当更改公共文件时,将公共分支合并到每个机器分支中,并推送到其他机器(如果有许多,请编写脚本)。然后在每台机器上,检出该机器的分支,该分支还将包括公共配置文件。

2
虽然子模块也可以使用,但我认为这是最适合我的方法。本地文件将遵循一个模板,当我在主分支上更改模板时,我可以将它们合并到机器本地分支中,逐步更新本地配置文件。感谢您的帮助! - Joe Casadonte
2
同时使用Mercurial和Git。 - Linc

17

请查看git子模块

子模块允许将外部存储库嵌入源代码树的专用子目录中,始终指向特定提交。


12
对于需要放在您目录根目录下的文件来说不是一个好的选择。唯一的机会是填充符号链接到这些文件的根目录。 - WhyNotHugo

8
RichiH写了一个名为vcsh的工具,它使用git的虚拟裸仓库来将多个工作目录放在$HOME下,以管理点文件,这与csh无关。如果您有多个目录,可以使用gitslave作为替代方案来避免使用git子模块的繁琐操作。gitslave会一直保持从属仓库处于分支的最新状态,并且不需要三步过程来更改子仓库中的内容(切换到正确的分支,做出并提交更改,然后进入超级项目并提交新的子模块提交)。请参考:vcshgitslave

7

如果你不知道自己在做什么,使用变量 GIT_DIR 是可能的,但有很多注意事项。


5

我偏爱在子目录中使用存储库,并使用递归符号链接的方法:

git clone repo1
cd somerepo
git clone repo2
cd repo2
./build

'repo/build'文件的格式如下:

#!/bin/bash 
SELF_PATH="$(dirname "$(readlink -f "$0")" )"  # get current dir 
cd .. && git stash && git clean -f -d ''       # remove previous symlinks
cp -sR "$SELF_PATH"/* ../.                     # create recursive symlinks in root

caution: dont use 'git add .'


4
“是的,一个目录中可以有两个git仓库。”
“我假设一个远程仓库在GitHub上,另一个在GitLab上。我还使用了两个不同的SSH密钥来连接这些远程仓库。”
“您可以将两个远程仓库放在GitHub / GitLab中之一(并使用单个SSH密钥)- 不会有太大变化。”
“先决条件:”
  • Public SSH keys (id_ecdsa.pub / id_rsa.pub / id_ed25519.pub , etc.) are present in your GitHub and GitLab profiles

  • Private SSH keys (id_ecdsa / id_rsa / id_ed25519 , etc.) are added and persisted in your OS's keychain

  • SSH config file has keys specified for GitHub and GitLab:

    Host github.com
      Hostname github.com
      AddKeysToAgent yes
      UseKeychain yes
      IdentityFile ~/.ssh/id_ecdsa
    
    Host gitlab.com
      Hostname gitlab.com
      AddKeysToAgent yes
      UseKeychain yes
      IdentityFile ~/.ssh/id_rsa
    
这是Chris的答案模拟工作流程的详细解释:
  • Initialize git in a directory:

    git init

  • Connect git to one remote repository (located in GitHub)

    git remote add origin git@github.com:your-username/your-repo.git

  • Rename .git to something like .github

    mv .git .github

  • Initialize git again

    git init

  • Connect git to the other remote repository (located in GitLab)

    git remote add origin git@gitlab.com:your-username/your-repo.git

  • Rename .git to something like .gitlab

    mv .git .gitlab

  • Verify that current directory is connected to two different remote repositories

    git --git-dir=.github remote -v

    git --git-dir=.gitlab remote -v

  • Pull remote (GitHub and GitLab) repositories

    git --git-dir=.github pull origin main
    git --git-dir=.gitlab pull origin main
    
  • Add a file to both repositories

    git --git-dir=.github add README.md
    git --git-dir=.gitlab add README.md
    
  • Write commit message

    git --git-dir=.github commit -m "operational overview"
    git --git-dir=.gitlab commit -m "operational overview"
    
  • Push to remote

    git --git-dir=.github push -u origin main
    git --git-dir=.gitlab push -u origin main
    
我们在这里唯一要做的额外事情就是使用--git-dir标志。
如果您计划经常执行此操作,可以在您的shell配置文件(如.zprofilebashrc等)中添加一个别名:
export github="git --git-dir=.github"
export gitlab="git --git-dir=.gitlab"

未来的操作,如pullpushaddcommit可以像这样执行:github pull origin maingitlab pull origin main等。

4

是的,子模块可能是你想要的。另一个选择是将你的工作副本放在子目录中,然后从你的主目录中指向感兴趣的文件。


2
另一个选项是将它们放在不同的文件夹中,并从一个文件夹创建符号硬链接到另一个文件夹。
例如,如果有以下存储库:
1. Repo1 / FolderA 2. Repo1 / FolderB
和:
1. Repo2 / FolderC
您可以将Repo1中的文件夹FolderA和FolderB链接到Repo2中。对于Windows,要在Repo1上运行的命令为:
User@Repo1$ mklink /J FullPath/Repo2/FolderA FullPath/Repo1/FolderA
User@Repo1$ mklink /J FullPath/Repo2/FolderB FullPath/Repo1/FolderB
User@Repo1$ printf "/FolderA/*\n/FolderB/*\n" >> .gitignore

对于主要仓库中的文件,您需要为每个文件创建符号链接,并将它们添加到存储库.gitignore中以避免噪音,除非您希望保留。


1

声明:这不是广告。我是提供的库的开发者。

我创建了一个Git扩展,用于处理您想要将多个存储库混合到一个文件夹中的情况。该库的优点在于,可以跟踪存储库和文件冲突。您可以在github上找到它。还有两个示例存储库可供尝试。


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