Git:如何使外部存储库和嵌入式存储库作为公共/独立存储库工作



我有一个大项目(假设A repo),它有一个来自B repo的子文件夹。 当我从A repo提交时,我会遇到如下警告

warning: adding embedded git repository: extractor/annotator-server
hint: You've added another git repository inside your current repository.
hint: Clones of the outer repository will not contain the contents of
hint: the embedded repository and will not know how to obtain it.
hint: If you meant to add a submodule, use:
hint:
hint:   git submodule add <url> extractor/annotator-server
hint:
hint: If you added this path by mistake, you can remove it from the
hint: index with:
hint:
hint:   git rm --cached extractor/annotator-server
hint:
hint: See "git help submodule" for more information.

我看过git-submodulegit-subtree

在另一个 git 存储库中维护 Git 存储库

https://www.atlassian.com/blog/git/alternatives-to-git-submodule-git-subtree

但我不喜欢它们,因为它们需要额外的配置。


我想要的是,例如:

结构如下:

A/
--- a.py
--- B/
--- B/b.py

当我改变B/b.py.

  1. 如果我在路径A/上,git add可以检测到B/b.py更改,git push仅将其提交到 A 存储库。

    git add .   (would add changes under A/  )
    git push   (would push changes under A/  )
    git pull   (would pull changes under A/  )
    git clone XXX:A  (would clone all files under A/ ,    A/B/ is just looks like plain folder with all files, not a repo )
    
  2. 如果我在路径A/B/上,git add只向 B 存储库添加B/b.py更改,git push仅将其提交到 B 存储库。

    git add .   (would add changes under B/ , but not add changes to A repo)
    git push   (would push changes under B/ , but not push changes to A repo)
    git pull   (would clone changes under B/ ,  )
    git clone XXX:B  (would clone all files under B/  )
    
  3. 一旦我想在另一台机器上 snyc A 和 B,只需做

    git clone A
    rm -rf A/B/
    git clone B ./B
    git add . && git commit 'sync with B'
    

换句话说,A 和 B 充当独立的存储库。

但事实是,A 仓库将 B 仓库视为子模块:

回购 https://github.com/eromoe/test

B 回购 https://github.com/eromoe/test2

如何强制 A 存储库跟踪A/下的所有文件

,B 存储库跟踪A/B/下的所有文件? 我希望 A 和 B 充当自包含存储库,而无需任何其他配置。

可以使用以下命令将文件从 test2 存储库添加到测试存储库,如下所示:

# In local test repo
rm -rf test2
git clone https://github.com/eromoe/test2
git add test2/
git commit -am 'add files from test2 repo to test repo'
git push

注意:

您应该使用git add test2/(带斜杠,而不是git add test2)。

git add test2/会将test2文件夹及其文件视为普通文件夹和测试存储库的文件(创建模式100644)。

git add test2会将test2文件夹视为测试存储库的子模块(创建模式160000)。

可能,git 提醒了存储库。它对我有帮助:

git rm --cached your_folder_with_repo git commit -m "删除缓存的存储库" git 添加your_folder_with_repo/git commit -m "Add folder" git push

2023 年 9 月 2 日更新:"正确"的方法是通过git submodules。但是,他们需要一些学习和经验。如果赶时间或只是存档项目,请使用下面的蛮力方法。如果您想学习git submodule使用 git 进行正确的"存储库内存储库"软件开发,请参阅标题为"有关git submodule的更多信息"部分中此答案的底部。

手动、蛮力方法:

对于登陆此页面的任何人,其目标只是在更大的父存储库或其他东西中存档一堆 git 存储库,最简单的蛮力解决方案是将所有嵌套的.git文件夹重命名为其他任何内容 - 例如:..git。现在,git add -A将像父 git 项目中的任何其他普通文件夹一样将它们全部添加,并且您可以轻松地git commit父存储库中的所有内容。做。

自动、蛮力方法:

使用 git-disable-repos.sh

(https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles 的一部分)。

我刚刚在周末写了这个脚本,并且已经在许多项目中使用它。效果很好!有关详细信息和安装,请参阅文件顶部的注释,并运行git disable-repos -h以获取帮助菜单。

安装:

git clone https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles.git
cd eRCaGuy_dotfiles/useful_scripts
mkdir -p ~/bin
ln -si "${PWD}/git-disable-repos.sh" ~/bin/git-disable-repos
# If this is the first time using your ~/bin dir, log out and
# log back in now. Otherwise, just re-source your .bashrc file:
. ~/.bashrc

以下是标准使用模式:

cd path/to/parent/repo
# Do a dry-run to see which repos will be temporarily disabled
git disable-repos --true_dryrun
# Now actually disable them: disable all git repos in this dir and below
git disable-repos --true
# re-enable just the parent repo
mv ..git .git
# quit tracking the subrepo as a single file (required
# if you previously tried to add it to your main repo before
# disabling it as a git repo)
git rm --cached path/to/subrepo
# add all files, including the now-disabled sub-repos, to the parent repo
git add -A
# commit all files
git commit

这会将所有子存储库(包括其(现在..git).git 文件夹和所有 git 工件作为常规文件提交到父 git 存储库。您拥有100%的控制权!只想更新 1 个子存储库?然后 cd 进入它并将其一个..git文件夹重命名回.git,手动,然后像往常一样使用该子存储库,然后在完成后再次运行git disable-repos --true(或手动将重命名从.git重命名回..git),并将其提交到父存储库中。我的git disable-repos脚本的美妙之处在于,如有必要,它可以快速而无缝地一次禁用或启用 100 个子存储库,而手动执行此操作是不切实际的。

也许我的用例很奇怪:我只需要将大量东西提交到一个存储库中,直到我可以在以后单独清理和分离每个子存储库,但它可以完成我需要它做的事情。

这是git disable-repos -h的完整帮助菜单输出

$ git disable-repos -h
'git disable-repos' version 0.3.0
- Rename all ".git" subdirectories in the current directory to "..git" to temporarily
"disable" them so that they can be easily added to a parent git repo as if they weren't 
git repos themselves (".git" <--> "..git").
- Why? See my StackOverflow answer here: https://stackoverflow.com/a/62368415/4561887
- See also the "Long Description" below.
- NB: if your sub-repo's dir is already being tracked in your git repo, accidentally, stop 
tracking it with this cmd: 'git rm --cached path/to/subrepo' in order to be able to 
start tracking it again fully, as a normal directory, after disabling it as a sub-repo 
with this script. To view all tracked files in your repo, use 'git ls-files'. 
- References: 
1. https://stackoverflow.com/questions/1274057/how-to-make-git-forget-about-a-file-that-was-tracked-but-is-now-in-gitignore/1274447#1274447
2. https://stackoverflow.com/questions/27403278/add-subproject-as-usual-folder-to-repository/27416839#27416839
3. https://stackoverflow.com/questions/8533202/list-files-in-local-git-repo/14406253#14406253
Usage: 'git disable-repos [positional_parameters]'
Positional Parameters:
'-h' OR '-?'         = print this help menu, piped to the 'less' page viewer
'-v' OR '--version'  = print the author and version
'--true'             = Disable all repos by renaming all ".git" subdirectories --> "..git"
So, once you do 'git disable-repos --true' **from within the parent repo's root directory,** 
you can then do 'mv ..git .git && git add -A' to re-enable the parent repo ONLY and 
stage all files and folders to be added to it. Then, run 'git commit' to commit them. 
Prior to running 'git disable-repos --true', git would not have allowed adding all 
subdirectories since it won't normally let you add sub-repos to a repo, and it recognizes 
sub-repos by the existence of their ".git" directories.  
'--true_dryrun'      = dry run of the above
'--false'            = Re-enable all repos by renaming all "..git" subdirectories --> ".git"
'--false_dryrun'     = dry run of the above
'--list'             = list all ".git" and "..git" subdirectories
Common Usage Examples:
1. To rename all '.git' subdirectories to '..git' **except for** the one immediately in the current 
directory, so as to not disable the parent repo's .git dir (assuming you are in the parent 
repo's root dir when running this command), run this:
git disable-repos --true  # disable all git repos in this dir and below
mv ..git .git             # re-enable just the parent repo
Be sure to do a dry run first for safety, to ensure it will do what you expect:
git disable-repos --true_dryrun
2. To recursively list all git repos within a given folder, run this command from within the 
folder of interest:
git disable-repos --list
3. Assuming you tried to add a sub-repo to your main git repo previously, BEFORE you deleted or 
renamed the sub-repo's .git dir to disable the sub-repo, this is the process to disable 
the sub-repo, remove it from your main repo's tracking index, and now re-add it to your 
main repo as a regular directory, including all of its sub-files and things:
Description: remove sub-repo as a sub-repo, add it as a normal directory, and commit
all of its files to your main repo:
Minimum Set of Commands (just gets the job done without printing extra info.):
git disable-repos --true  # disable all repos in this dir and below 
mv ..git .git             # re-enable just the main repo
# quit tracking the subrepo as a single file
git rm --cached path/to/subrepo
# start tracking the subrepo as a normal folder
git add -A
git commit
Full Set of Commands (let's you see more info. during the process):

git disable-repos --true  # disable all repos in this dir and below 
mv ..git .git             # re-enable just the main repo
git ls-files path/to/subrepo  # see what is currently tracked in the subrepo dir 
# quit tracking the subrepo as a single file
git rm --cached path/to/subrepo
git status
# start tracking the subrepo as a normal folder
git add -A
git status
git commit

Long Description: 
I want to archive a bunch of small git repos inside a single, larger repo, which I will back up on 
GitHub until I have time to manually pull out each small, nested repo into its own stand-alone
GitHub repo. To do this, however, 'git' in the outer, parent repo must NOT KNOW that the inner
git repos are git repos! The easiest way to do this is to just rename all inner, nested '.git' 
folders to anything else, such as to '..git', so that git won't recognize them as stand-alone
repositories, and so that it will just treat their contents like any other normal directory
and allow you to back it all up! Thus, this project is born. It will allow you to quickly
toggle the naming of any folder from '.git' to '..git', or vice versa. Hence the name of this
project: git-disable-repos. 
See my answer here: 
https://stackoverflow.com/questions/47008290/how-to-make-outer-repository-and-embedded-repository-work-as-common-standalone-r/62368415#62368415
This program is part of: https://github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles

其他更复杂的工具:

对于任何寻求更"专业"解决方案的人来说,这些似乎是最受欢迎的解决方案,首先是最受欢迎的(因此似乎最受支持的解决方案?

  1. git submodule- https://git-scm.com/docs/git-submodule - 内置于git的规范,官方支持的工具。
  2. git subtree- https://www.atlassian.com/git/tutorials/git-subtree
  3. git subrepo- https://github.com/ingydotnet/git-subrepo

其中哪一个是最好的?我不能说,但它们对我来说都看起来很混乱,所以我选择了我上面描述的手动、蛮力选项,因为在这种情况下,它最符合我的预期目的,直到有一天我能找到时间将每个子存储库分解为他们自己的单独维护的存储库在 GitHub 上。

更多关于git submodule

2023 年 9 月 2 日更新:我已经使用git submodule几年了。要了解基本的命令和内容,请参阅我的eRCaGuy_dotfiles存储库中标题为">Git 子模块和 Git LFS:如何克隆此存储库以及所有 git 子模块和 git lfs 文件"的部分。(另外,不要使用git lfs。请参阅我的问题 这里 和 回答 这里,两者都包含解释和原因。

2020 年 9 月 21 日更新:Martin Owen 在 2016 年 5 月的这篇文章("Git 子模块与 Git 子树")包含git submodulegit subtree的良好比较,并且通常倾向于git submodule。然而,提交人当时甚至不知道git subrepo,除了在评论中提到这一点外,他没有提到它。

git submodule似乎是内置于git中的规范,官方支持的工具。虽然看起来它肯定有一个学习曲线,但我计划在我的下一个项目中使用它,现在我已经准备好打开该项目并再次开始工作,这取决于 sub-git 存储库。我计划从这里开始了解它:

  1. Atlassian 的 Bitbucket 的简短介绍:https://www.atlassian.com/git/tutorials/git-submodule
  2. 官方git submodule文档在这里:https://git-scm.com/book/en/v2/Git-Tools-Submodules

另请参阅:

  1. 我关于如何更新存储库中的所有 git 子模块的答案(两种方法可以做两件截然不同的事情!

其他参考资料:

  1. https://medium.com/@porteneuve/mastering-git-subtrees-943d29a798ec
  2. 什么时候使用 git 子树?
  3. https://webmasters.stackexchange.com/questions/84378/how-can-i-create-a-git-repo-that-contains-several-other-git-repos
  4. Git 将嵌套的 git 存储库视为常规文件/文件夹
  5. Git:如何使外部存储库和嵌入式存储库作为公共/独立存储库工作?
  6. https://www.atlassian.com/git/tutorials/git-subtree

关键字:git add subrepo; git add sub repository; git addnested repository; git add .git 文件夹和文件

如果你不关心 B A 使用的确切版本,你可以保留你当前的设置(嵌套的 git 存储库)。

你将看到"嵌入式存储库"警告,但除此之外,两个存储库都将按预期运行,每个存储库仅添加、提交和推送其存储库。
注意:您可以使用git config advice.addEmbeddedRepo使该警告短路/为空

我删除了该特定文件夹中的.git。然后在我运行命令之后

git add folder_which_has_deleted_dot_git
git commit -m "Changed to standalone repo"

然后之后,我能够跟踪该文件夹并将其转换为通用/独立存储库。

更详细地阐述罗斯特·珊的回答。

我在 Ubuntu 20.04 上处理 Rails 应用程序时遇到了这个问题。

当我运行命令git add .时,出现错误:

hint: You've added another git repository inside your current repository.
hint: Clones of the outer repository will not contain the contents of
hint: the embedded repository and will not know how to obtain it.
hint: If you meant to add a submodule, use:
hint: 
hint:   git submodule add <url> letsencrypt_cred
hint: 
hint: If you added this path by mistake, you can remove it from the
hint: index with:
hint: 
hint:   git rm --cached letsencrypt_cred
hint: 
hint: See "git help submodule" for more information.

这是我修复它的方法

从 git 中取消我尝试推送到的存储库的所有已暂存文件:

git rm --cached letsencrypt_cred

git rm -f --cached letsencrypt_cred (to force removal)

提交当前目录中的所有文件:

git commit -m "modify credentials"

添加要推送到的远程存储库:

git remote add origin https://github.com/promisepreston/letsencrypt_cred.git

将文件推送到远程存储库

git push -u origin main

git push -u origin master

就这样。

我希望这有帮助

您已在另一个 git 存储库中添加了一个 git 存储库

外部 git 存储库将忽略内部 git 存储库。

内部 git 存储库称为子模块。

假设您有这些文件和目录

project 
|- file1.html
|- file2.css
|- resources
|- info1.json
|- info2.json

如果在项目目录中创建了一个 git 仓库,并且资源目录中有一个 git 仓库,则资源目录中的所有文件将被项目目录中的 git 仓库忽略。

另一个 git 存储库中的 git 存储库称为子模块。换句话说,一个带有 git 存储库的目录位于另一个目录中,也有一个 git 存储库。子模块不必位于直接子目录中,它可以高于一级、二级或更多级。

有时这是您想要做的,但是如果您想做的是将多个目录中的文件一起收集到一个存储库中,那么您只需要整个项目的一个 git 存储库。所以子模块不是你想要发生的。

创建 git 存储库时,它会创建一个名为 .git 的隐藏目录,这就是 git 工具知道它正在使用 git 存储库的方式。有关您过去版本的代码、GitHub 遥控器的位置等的所有信息。存储在此 .git 目录中的文件中。

如果你在项目目录中有一个 git 存储库,在资源目录中有另一个,你的文件系统实际上看起来像这样,

project 
|- .git
|- file1.html
|- file2.css
|- resources
|- .git
|- info1.json
|- info2.json

如果从项目目录添加和提交文件,则会在已添加和提交的文件下看到资源目录的条目,但不会添加 info1.json 和 info2.json 文件。

如果希望项目目录有一个包含所有内容的 git 存储库,请执行以下步骤。

在资源目录(包含 git 存储库的内部目录)中:删除 .git 文件夹。您需要启用隐藏文件才能在资源管理器/查找器中看到此信息。

使用命令提示符或 git bash (Windows) 或终端 (mac、linux) 并导航到项目目录。您需要位于带有子模块的目录上方的目录中 - 因此在本例中,项目目录(外部目录,应包含所有文件的目录)。然后运行命令

git rm --缓存资源

但将资源替换为您自己的目录名称。--cached 部分非常重要,如果你错过了它,它将不可逆转地删除你的资源目录!

现在,您应该能够使用 git add 命令将资源目录中的所有文件添加到主项目的存储库中,并提交这些文件。

这些都是很棒的解决方案,但是如果像我一样,您只是想将一堆东西推送到 github 作为备份(我的 ubuntu vm 坏了,我只能从终端访问东西),一种更简单的解决方案在许多情况下可以很好地工作,那就是存档您的文件。如果您有 7zip 或其他东西,一个简单的7z a backup.7z *就可以了。然后,您可以添加、提交和推送该backup.7z文件

我想简单地将另一个存储库添加为包含一堆文件的目录/模块,所以我只是删除了另一个存储库中的.git文件,它就像一个魅力!

修改上面刘玛丽娜回答的片段:

# In local test repo
rm -rf test2
git clone https://github.com/eromoe/test2
cd test2
rm -rf .git
cd ..
git add test2/
git commit -m "add files from test2 repo to test repo"
git push origin main

免责声明:我不是git专家。上述解决方案适用于我的情况,因此,我发布了它。如果此解决方案在存储库中引入任何不良影响,请在下面发表评论。

最新更新