实际上我正在尝试:
- 有一个主存储库,在主存储库中我想创建一个新的(子)存储库。所以我做了以下操作: - 第一步)创建文件夹(ABC)。 - 第二步)在主存储库中忽略文件夹(ABC)。 - 第三步)在文件夹(ABC)中,运行git init(创建一个新的存储库)
这是创建子存储库的正确方法(或者可能是正确的方向)吗?
git子模块和我上面所做的类似吗?
提前感谢。
Git并没有定义"子仓库",所以您可以按照自己的喜好进行定义。
Git确实定义了在.gitignore
中列出目录的含义,这个特定模式可以正常工作。您所做的是设置了两个完全不相关的仓库。
在典型的设置中,这两个仓库会并排存在:
$HOME/
[various dot files, personal files, etc]
src/
project-A/
.git/
[all of Git's files]
.gitignore
README.md
[various files]
project-B/
.git/
[all of Git's files]
.gitignore
README.md
[various files]
project-B
移动到project-A
的工作树中。请记住,任何标准的存储库都有这三个部分:
.git
存储库本身,包含Git用于自己目的的文件。它包含主要的Git存储库数据库之一,其中保存了所有提交和其他Git对象。它具有Git需要完成自己任务的所有文件。(您可以随时查看这些文件,但通常情况下,除非您知道自己在做什么,否则不应直接编辑它们。但有些文件是明显的,这种情况下,直接编辑也往往可以正常工作。例如,您可能想查看文件.git/config
,它具有相当简单的格式,类似于Windows INI文件。查看文件.git/HEAD
的内容也很有教育意义。)
工作树,这是您实际工作的地方。这是一个普通的目录树,包含普通的文件。Git会使用从提交中取出的文件填充它,以便您可以处理这些文件。您还可以在此处存储Git不知道的文件:这些文件称为未跟踪的文件。Git会抱怨它们,除非您在.gitignore
文件中列出它们或它们的名称模式,其主要功能是使Git停止抱怨它们(并确保您不会意外地将它们放入索引中,对此,请参见下一点)。
索引,这是Git保存下一个要提交的文件的位置。索引具有每个文件的副本,从当前提交中解冻,准备好进入下一个提交。但是,这些副本采用了一种特殊的、仅适用于Git的格式,类似于提交内部的文件。它们不像工作树中的文件那样普通。
在工作树中处理文件后,您可以随时使用git add
将该文件重新复制回索引中。这将使其变回特殊的Git-only格式,以便它准备好进入下一个提交。如果该文件之前不在索引中,则会将其复制到索引中(将其转换为Git-only格式),因此现在它在索引中。
文件在索引中的存在就是使该文件成为已跟踪的文件,因此在工作树中存在但不在索引中的文件是一个未跟踪的文件。正如我们刚才所说的那样,Git会抱怨它,除非您通过.gitignore
条目告诉Git不要抱怨这个文件。
-a
)或参数(例如.
)运行git add
命令,以表明要添加所有内容或添加多个文件。在这种情况下,git add
会检查任何当前未跟踪的文件-即目前不在索引中的任何文件-并根据.gitignore
设置,不会将该文件添加,以使其保持未跟踪状态。.gitignore
的含义不是忽略此文件,而是如果此文件未被跟踪,则不要对它未被跟踪进行投诉,并且在我批量添加或更新大量文件到索引中以便准备进入下一个提交时,不自动将其复制到索引中。
索引本身实际上是一个或多个文件,存在于.git
中,但由于两个原因,它值得单独列出。第一点是它非常重要且显眼,尽管您无法看到它。第二点是Git现在支持git worktree add
来创建其他工作树。您添加的每个工作树都有自己的索引。也就是说,索引和工作树是成对出现的:只有一个存储库,并且在该存储库中,您可以免费获取一个索引和工作树。然后您可以git worktree add
第二个索引和工作树,然后是第三个索引和工作树,以此类推。显然,索引至少逻辑上与存储库本身不同:它与工作树相关联,而不是与存储库相关联。project-B
放在project-A
中,您所做的就是让项目A有大量被忽略的文件。$HOME/
[various dot files, personal files, etc]
src/
project-A/
.git/
[all of Git's files]
.gitignore <-- lists `/project-B/`
project-B/
.git/
[all of Git's files] <-- these files are untracked in A
.gitignore <-- and this is untracked in A
README.md <-- and so is this
[various files] <-- and all of these
README.md
[various files]
<click some buttons in browser>
git clone <url> A # so now you have A/README.md and A/.git and so on
cd A
mkdir A
cd A
git init
A/
目录下,其中有一个.git/
子目录,其中保存着存储库数据库、索引和工作树。在这个工作树中,你可以创建和编辑各种文件,使用git add
将它们复制到索引中,以便它们进入下一次提交,然后运行git commit
将索引的内容冻结成一个新的快照。git clone
将存储库B的新克隆放在你的工作树中,因此你还必须选择项目B的路径——它将进入当前工作树中的目录。在这里,我们选择ssh://github.com/someuser/B
作为URL,project-B
作为目录。git submodule add ssh://github.com/someuser/B project-B
git clone
来创建project-B
,作为ssh://github.com/someuser/B
的克隆。这个克隆在几乎所有方面都是普通的克隆。例如,它有一个.git
。1我们只是将其称为“子模块”,因为它被另一个Git存储库使用。就克隆B而言,它不需要知道或关心这一点,它只是一个普通的克隆。A
工作树中,git submodule add
命令将创建一个名为.gitmodules
的文件,并将子模块B的URL和路径放入该文件。它将git add
此文件到A的索引中,以备下一次提交。最后,它将使用git add project-B
向A的索引添加一种特殊类型的gitlink条目,即子模块B的路径。A
中的索引有两个全新的条目:一个用于.gitmodules
,如果您查看该文件的工作树副本,则现在列出了子模块,还有一个用于名为project-B
的“文件”(实际上是gitlink)。如果您现在在项目A的工作树中运行git commit
,则会在存储库数据库中得到一个新的提交。这个新提交包含了已经在索引中的所有文件(例如README.md
等),加上这个新文件.gitmodules
和这个新的gitlink条目。
1在旧版本的Git中,子模块克隆中的此.git
是一个普通目录,它保存着该克隆的存储库数据库,就像任何普通的Git存储库工作树一样,其中数据库位于其.git
内部。在现代Git中,它是一个文件,告诉Git在项目A的.git
目录中隐藏子模块B的.git
存储库数据库。这种隐藏子模块B存储库的方式使得A的附加工作树可以共享子模块B存储库,而不仅仅是复制它。
cd project-B
命令进入项目 B 克隆的工作树并运行 git log
、git status
和其他各种 Git 命令,它们都可以正常工作并告诉你在项目 B 的此克隆中正在发生什么。project-B
目录的工作树中)进入“分离 HEAD”模式。如果你不熟悉分离 HEAD 模式,则现在需要学习一下。如果您已经熟悉了它,或者在学习了它之后回到这里,那么子模块项目 B 工作树的 HEAD 分离的“一个特定提交”是记录在超级项目中的 gitlink 中的哈希 ID。0123456...
。project-B
目录,那么你就在 B 的克隆版中,并且可以 git checkout
任何其他提交,甚至是在 B 中检出一个分支。这会更改分离的 HEAD,甚至将其附加到一个分支,以便现在存储库 B 有不同的提交被检出。因此,假设你这样做了:cd project-B
git checkout develop
... do some work ...
git add ... some files ...
git commit -m 'do work on B to make it more useful for A'
git push
将新的提交推回GitHub,因为项目B毕竟是一个常规的旧存储库。但现在,在project-B
(工作树目录)中的HEAD
提交不再是0123456...
,现在是8088dad...
。如果您回到项目A的工作树并运行git submodule status
,则会看到管理A的Git表示:嘿,子模块已经远离我想要的分离HEAD了,它不再是0123456...
!
这是真的,但如果这正是您想要的,现在是使用git add
更新项目A索引中的gitlink条目的时候了。git add project-B
8088dad...
,如果您在项目A工作树中运行git commit
,您将得到一个新的提交,其中包含有关项目A的以下信息:git commit -m 'update submodule B to 8088dad...'
(这不是最好的提交信息,最好的做法是说明您现在使用了子模块B的哪些功能,而不仅仅是“我切换到了8088dad提交”——但这只是一个示例,我甚至不知道您在使用哪些功能。)
有其他方法来更新子模块,然后在超级项目中记录一个新的提交,指示超级项目 Git 命令子模块 Git 检出这个特定提交。 git submodule
命令表达了其中许多方式。 但关键是,在超级项目存储库中存在许多提交,每个提交都说明:
url
...获取的子模块path
...hash-ID
。前两个信息在项目 A 的提交中记录在名为 .gitmodules
的文件中。每个提交都有其自己的该文件副本(尽管通常情况下,如果一百万个提交使用相同版本的文件,则存储库只有该版本的一个副本)。最后一条信息是直接在项目 A 的提交中记录的:每个提交保存一个原始哈希 ID,给出应在子模块路径中git checkout
的提交。
git submodule
的目的是允许您在超级项目 Git 中指定,您依赖于其他 Git 存储库。您记录要使用的存储库的 URL,以便新克隆将其用于 git clone
子模块。您记录要使用的路径,将克隆存储到超级项目 Git 中。并且,对于每个提交在超级项目中,您都记录该子模块的特定提交,以便克隆超级项目,然后检出该克隆中的某个特定历史提交时,也会克隆子模块并检出子模块克隆中的正确历史提交。
ABC
添加到主代码库的.gitignore
中来忽略它?你想达到什么样的目的? - Flimm