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

Posted

技术标签:

【中文标题】Git:如何使外部存储库和嵌入式存储库作为通用/独立存储库工作?【英文标题】:Git: How to make outer repository and embedded repository work as common/standalone repository? 【发布时间】:2018-04-11 00:31:27 【问题描述】:

我有一个大项目(比如说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

Maintaining Git repo inside another git repo

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

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


我想要的是,例如:

结构如下:

A/
--- a.py

--- B/
--- B/b.py

当我更改 B/b.py 时。

    如果我在路径 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 )
    

    如果我在路径 A/B/ 上,git add 仅将 B/b.py 更改添加到 B 存储库,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/  )
    

    一旦我想在另一台机器上同步 A 和 B,就这样做

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

换句话说,A 和 B 充当独立的 repo。

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

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

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


如何强制 A repo 跟踪 A/ 下的所有文件,而 B repo 跟踪 A/B/ 下的所有文件?我希望 A 和 B 作为一个独立的 repo ,没有任何其他配置。

【问题讨论】:

用 Git 是不可能的。请改用 SVN。也许SVN也不能满足你的需求。 我只想将整个子 .git 结构保存到 git 存储库中......为什么不可能。子模块不存储您的本地分支或未提交的文件。 【参考方案1】:

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

修改上面 Marina Liu 的answer 的 sn-p:

# 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 专家。上述解决方案适用于我的情况,因此我发布了它。如果此解决方案在存储库中引入任何不良影响,请在下方发表评论。

【讨论】:

【参考方案2】:

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

【讨论】:

【参考方案3】:

手动蛮力方法:

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

自动的蛮力方法:

使用git-disable-repos.sh

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

我刚刚在周末编写了这个脚本,并且已经在许多项目中使用了它。它工作得很好!有关详细信息和安装,请参阅文件顶部的 cmets,并运行 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 个子存储库,而手动执行此操作是不切实际的。

也许我的用例很奇怪:我只需要将大量的东西提交到一个 repo 中,直到我可以在以后单独清理和分离每个 subrepo,但它可以满足我的需要。

这是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 *** answer here: https://***.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://***.com/questions/1274057/how-to-make-git-forget-about-a-file-that-was-tracked-but-is-now-in-gitignore/1274447#1274447
        2. https://***.com/questions/27403278/add-subproject-as-usual-folder-to-repository/27416839#27416839
        3. https://***.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://***.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

其他更复杂的工具:

对于任何寻求更“专业”解决方案的人来说,这些似乎是最受欢迎的解决方案,排名靠前的是最受欢迎的(因此似乎也是最受支持的?):

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

其中哪一个是最好的?我不能说,但它们都让我感到困惑,所以我选择了我上面描述的手动蛮力选项,因为在这种情况下,它最符合我的预期目的,直到我有时间打破每个子-repos 有一天会变成他们自己在 GitHub 上单独维护的 repos。

更多关于git submodule:

2020 年 9 月 21 日更新:Martin Owen 在 2016 年 5 月 ("Git Submodules vs Git Subtrees") 撰写的这篇文章很好地比较了 git submodulegit subtree,并且普遍支持 git submodule。但是,当时作者连git subrepo都不知道,除了在cmets中提到过,也没有提过。

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

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

其他参考资料:

    https://medium.com/@porteneuve/mastering-git-subtrees-943d29a798ec When to use git subtree? https://webmasters.stackexchange.com/questions/84378/how-can-i-create-a-git-repo-that-contains-several-other-git-repos Git treat nested git repos as regular file/folders Git: How to make outer repository and embedded repository work as common/standalone repository? https://www.atlassian.com/git/tutorials/git-subtree

关键字:git add subrepo; git 添加子存储库; git 添加嵌套存储库; git 添加.git 文件夹和文件

【讨论】:

嗨,我真的很喜欢这个答案,我也有类似的用例,但我无法使用你的脚本。 git disable-repos --true_dryrun 当我运行这个命令时,我得到一个 disable-repos is not a git command 错误 @hargun3045,您是否先按照安装说明进行操作?如果是这样,并且这是第一次创建 ~/bin 目录,您必须使用 . ~/.bashrc 重新获取 ~/.bashrc 文件和/或注销并重新登录以使其生效。然后,再次尝试该命令。 这是为了使~/.profile 将这个新的~/bin 目录添加到您的可执行路径中,如下所示:github.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/blob/…。【参考方案4】:

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

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

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

【讨论】:

【参考方案5】:

详细说明rost shan's answer。

我在 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

就是这样。

我希望这会有所帮助

【讨论】:

【参考方案6】:

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

git rm --cached your_folder_with_repo git commit -m "删除缓存的仓库" git 添加 your_folder_with_repo/ git commit -m "添加文件夹" git 推送

【讨论】:

【参考方案7】:

您可以使用以下命令将文件从 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 文件夹和它的文件视为普通文件夹和测试repo 文件(创建模式100644)。

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

【讨论】:

这就是为什么git add . 不起作用,谢谢。嗯..如果有办法强制git add .添加所有文件将是最理想的。 感谢您强调“test2”和“test2/”之间的区别 这很有帮助,但如果我希望它从内部test2/ repo(即:路径test2/.git/*)添加.git 文件夹怎么办? ,就好像它只是一个普通的文件夹一样? 我自己的问题的回答:***.com/a/62368415/4561887【参考方案8】:

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

您将收到“嵌入式存储库”警告,但除此之外,两个存储库都将按照您的预期运行,每个存储库都只会添加、提交和推送它们的存储库。 注意:您可以使用 git config advice.addEmbeddedRepo 使该警告缩短/为空

【讨论】:

使用git version 2.12.2.windows.2,默认设置就像使用子模块一样。只需查看我问题中的示例repo,或者您可以尝试克隆github.com/eromoe/test,您可以看到test2 repo下的文件未添加到测试仓库。 @Mithril 是的,它应该是这样工作的:A 应该忽略嵌套的 B 存储库。 @Mithril 在您的测试仓库中,您所记录的只是另一个仓库的 gitlink (SHA1),而没有记录其 URL。请参阅我的旧答案***.com/a/35334520/6309 @Mithril 这仍然意味着,默认情况下,A 和 B 将充当独立的 repo:您可以忽略该 gitlink 记录。 (或者在你的测试.gitignore 中添加test2,在这种情况下,test2 甚至不会出现在test 中)

以上是关于Git:如何使外部存储库和嵌入式存储库作为通用/独立存储库工作?的主要内容,如果未能解决你的问题,请参考以下文章

具有通用存储库和依赖注入和 SoC 的 EF6 Code First

如何将外部文件添加到 Jenkins 作业?

Git归档存储库作为一个功能齐全的存储库?

如何暂存(git add)除嵌入式存储库之外的所有更改和新文件?

Github API 列出所有存储库和 repo 的内容

text 自定义BASH PS1提示以显示当前的GIT存储库和分支