yaoningvital / blog

my blog
31 stars 4 forks source link

Git 工具 - 子模块 #210

Open yaoningvital opened 4 years ago

yaoningvital commented 4 years ago

原文地址:https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E5%AD%90%E6%A8%A1%E5%9D%97

子模块允许你将一个 Git 仓库作为另一个 Git 仓库的子目录。 它能让你将另一个仓库克隆到自己的项目中,同时还保持提交的独立。

我们将要演示如何在一个被分成一个主项目与几个子项目的项目上开发。

一、在 主项目 上添加 子项目

1、用 git submodule add <repos-url> <path>,在主项目中添加子项目

我们首先将一个已存在的 Git 仓库添加为正在工作的仓库的子模块。 你可以通过在 git submodule add 命令后面加上想要跟踪的项目的相对或绝对 URL 来添加新的子模块。

这里我要添加的子项目仓库地址是:https://hyperv28.msdi.cn/tfs/Power5DBIM/PRP_Front/_git/biz-contract

$ git submodule add https://hyperv28.msdi.cn/tfs/Power5DBIM/PRP_Front/_git/biz-contract
Cloning into 'https://hyperv28.msdi.cn/tfs/Power5DBIM/PRP_Front/_git/biz-contract'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.

注意:上面的命令行显示,除了第一行的命令是正确的外,下面的信息都是假的,是模拟的,实际情况是类似的数据。下面的命令行演示类似。

默认情况下,子模块会将子项目放到一个与仓库同名的目录中,本例中是 “biz-contract”。 如果你想要放到其他地方,那么可以在命令结尾添加一个不同的路径。

如果这时运行 git status,你会注意到几件事。

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   .gitmodules
    new file:   DbConnector

首先应当注意到新的 .gitmodules 文件。 该配置文件保存了项目 URL 与已经拉取的本地目录之间的映射:

[submodule "biz-contract"]
    path = biz-contract
    url = https://hyperv28.msdi.cn/tfs/Power5DBIM/PRP_Front/_git/biz-contract

如果有多个子模块,该文件中就会有多条记录。 要重点注意的是,该文件也像 .gitignore 文件一样受到(通过)版本控制。 它会和该项目的其他部分一同被拉取推送。 这就是克隆该项目的人知道去哪获得子模块的原因。

git status 输出中列出的另一个是项目文件夹记录。 如果你运行 git diff,会看到类似下面的信息:

$ git diff --cached biz-contract
diff --git a/biz-contract b/biz-contract
new file mode 160000
index 0000000..c3f01dc
--- /dev/null
+++ b/DbConnector
@@ -0,0 +1 @@
+Subproject commit c3f01dc8862123d317dd46284b05b6892c7b29bc

2、用 git commit -am "commit message",在主项目提交

当你提交时,会看到类似下面的信息:

$ git commit -am 'added biz-contract module'
[master fb9093c] added biz-contract module
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 DbConnector

git commit -a的含义是:

-a --all Tell the command to automatically stage files that have been modified and deleted, but new files you have not told Git about are not affected. 告诉命令自动暂存已修改和删除的文件,但未告知Git的新文件不受影响。

注意 biz-contract 记录的 160000 模式。 这是 Git 中的一种特殊模式,它本质上意味着你是将一次提交记作一项目录记录的,而非将它记录成一个子目录或者一个文件。

3、用 git push origin master ,在主项目 推送 这些更改

二、克隆含有子模块的项目

接下来我们将会克隆一个含有子模块的项目。 当你在克隆这样的项目时,默认会包含该子模块目录,但其中还没有任何文件。

方式一

git clone 克隆一个含有子模块的项目

$ git clone https://hyperv28.msdi.cn/tfs/Power5DBIM/PRP_Front/_git/PRP_Front
Cloning into 'PRP_Front'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 14 (delta 1), reused 13 (delta 0)
Unpacking objects: 100% (14/14), done.
...........

其中有 biz-contract 目录,不过是空的。

git submodule init ,初始化本地配置文件

$ git submodule init
Submodule 'biz-contract' (https://hyperv28.msdi.cn/tfs/Power5DBIM/PRP_Front/_git/biz-contract) registered for path 'biz-contract'

git submodule update ,从该项目中抓取所有数据并检出父项目中列出的合适的提交

$ git submodule update
Cloning into 'biz-contract'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Submodule path 'DbConnector': checked out 'c3f01dc8862123d317dd46284b05b6892c7b29bc'

现在 biz-contract 子目录是处在和之前提交时相同的状态了。

方式二

git clone 克隆一个含有子模块的项目

git submodule update --init

git submodule update --init 这条命令是 将 git submodule initgit submodule update 合并成一步完成。

方式三

git clone 克隆一个含有子模块的项目

git submodule update --init --recursive

git submodule update --init --recursive这条命令是 将 git submodule initgit submodule update 合并成一步完成,并且检出任何嵌套的子模块。

方式四

git clone --recurse-submodules <repos-url>,自动初始化并更新仓库中的每一个子模块

这种方式是用一条命令就完成了上面的 clone、init、update、recursive 所有的功能。

如果给 git clone 命令传递 --recurse-submodules 选项,它就会自动初始化并更新仓库中的每一个子模块, 包括可能存在的嵌套子模块。

$ git clone --recurse-submodules https://github.com/chaconinc/MainProject
Cloning into 'MainProject'...
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 14 (delta 1), reused 13 (delta 0)
Unpacking objects: 100% (14/14), done.
Checking connectivity... done.
Submodule 'DbConnector' (https://github.com/chaconinc/DbConnector) registered for path 'DbConnector'
Cloning into 'DbConnector'...
remote: Counting objects: 11, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 11 (delta 0), reused 11 (delta 0)
Unpacking objects: 100% (11/11), done.
Checking connectivity... done.
Submodule path 'DbConnector': checked out 'c3f01dc8862123d317dd46284b05b6892c7b29bc'

三、在主项目中拉取到子项目的更新

从子模块的远端拉取上游修改(在包含子模块的项目上工作)。

现在我们有一份包含子模块的项目副本,我们将会同时在主项目和子模块项目上与队员协作。

现在假设子模块 biz-contract 有提交更新。那么怎么在主项目中拉取到子项目的更新呢?

方式一

在子模块目录,运行 git fetch,手动抓取更新

think@think-PC MINGW64 /e/zny/projects/PRP_Front/biz-contract (master)
$ git fetch
remote: Azure Repos
remote: Found 5 objects to send. (1 ms)
Unpacking objects: 100% (5/5), done.
From https://hyperv28.msdi.cn/tfs/Power5DBIM/PRP_Front/_git/biz-contract
   7bb1bce..01afc9e  master     -> origin/master

在子模块目录,运行 git merge origin/master,合并上游分支来更新本地代码

think@think-PC MINGW64 /e/zny/projects/PRP_Front/biz-contract (master)
$ git merge origin/master
Updating 096824b..01afc9e
Fast-forward
 src/pages/index.tsx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

这时,子项目的更新已经 拉取 并且 合并到了本地。

在主项目根目录下,可以通过 git status查看修改的内容:

think@think-PC MINGW64 /e/zny/projects/PRP_Front (master)
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   biz-contract (new commits)

no changes added to commit (use "git add" and/or "git commit -a")

方式二

git submodule update --remote (Git进入子模块,抓取并更新)

如果不想像方式一 一样,在子目录中手动抓取与合并,那么还有种更容易的方式。 运行 git submodule update --remote,Git 将会进入子模块然后抓取并更新。

这种方式不仅是方式一中 git fetchgit merge两条命令的和,这种方式还将自动抓取和更新 所有的子模块。而方式一中是进入某一特定的子模块,抓取和更新的也是这个子模块的更新,而不是这个主项目下的所有子模块。

think@think-PC MINGW64 /e/zny/projects/PRP_Front (master)
$ git submodule update --remote
remote: Azure Repos
remote: Found 5 objects to send. (1 ms)
Unpacking objects: 100% (5/5), done.
From https://hyperv28.msdi.cn/tfs/Power5DBIM/PRP_Front/_git/biz-contract
   01afc9e..d8217b6  master     -> origin/master
Submodule path 'biz-contract': checked out 'd8217b64416b748036b3906724b692877fbd50b5'
Submodule path 'src/pages/biz-prpPlan': checked out '2fac9daee790473db30895d57235b1f49c7ae93a'

然后在主项目下,执行 git status,Git 会显示子模块中有“新提交”:

think@think-PC MINGW64 /e/zny/projects/PRP_Front (master)
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   biz-contract (new commits)
        modified:   src/pages/biz-prpPlan (new commits)

no changes added to commit (use "git add" and/or "git commit -a")

四、在主项目中更新子项目内容,在子项目中拉取该更新

1、首先在主项目的子目录下,进行修改的提交和推送

比如,在主项目中,src/pages/biz-contract是一个子项目,我修改了src/pages/biz-contract/views/test.txt文件,将其内容修改为 '111',如下所示: image

在主项目的子目录下,用 git status,查看改动的内容

在主项目的子项目目录中,可以先用git status命令查看一下改动的内容。

think@think-PC MINGW64 /e/zny/projects/PRP_Front_Contract/src/pages/biz-contract (master)
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   views/test.txt

no changes added to commit (use "git add" and/or "git commit -a")

在主项目的子目录下,用 git add .git commit -m <commit_message>提交更新

think@think-PC MINGW64 /e/zny/projects/PRP_Front_Contract/src/pages/biz-contract (master)
$ git add .

think@think-PC MINGW64 /e/zny/projects/PRP_Front_Contract/src/pages/biz-contract (master)
$ git commit -m "主项目中修改了子项目的内容"
[master a75585e] 主项目中修改了子项目的内容
 1 file changed, 1 insertion(+), 1 deletion(-)

在主项目的子目录下,用 git push推送

think@think-PC MINGW64 /e/zny/projects/PRP_Front_Contract/src/pages/biz-contract (master)
$ git push
Enumerating objects: 7, done.
Counting objects: 100% (7/7), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 374 bytes | 93.00 KiB/s, done.
Total 4 (delta 1), reused 0 (delta 0)
remote: Analyzing objects... (4/4) (13 ms)
remote: Storing packfile... done (94 ms)
remote: Storing index... done (128 ms)
To https://hyperv28.msdi.cn/tfs/Power5DBIM/PRP_Front/_git/biz-contract
   70b16f7..a75585e  master -> master

2、到子项目中,用git pull就能拉取到更新(在主项目中对子项目做的更新)

think@think-PC MINGW64 /e/zny/projects/biz-contract (master)
$ git pull
remote: Azure Repos
remote: Found 11 objects to send. (3 ms)
Unpacking objects: 100% (11/11), done.
From https://hyperv28.msdi.cn/tfs/Power5DBIM/PRP_Front/_git/biz-contract
   6c49fa9..a75585e  master     -> origin/master
Updating 6c49fa9..a75585e
Fast-forward
 views/test.txt | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

image