38、Git 中工作树、子模块和子树的使用指南

Git 中工作树、子模块和子树的使用指南

在本地环境中管理多个工作区和仓库实例时,Git 提供了多种实用功能,如工作树(Worktrees)、子模块(Submodules)和子树(Subtrees)。下面将详细介绍这些功能的使用方法。

1. 添加子树

在 Git 中添加子项目作为子树,最基本的操作是指定前缀、仓库的远程路径,还可以选择指定分支。以下是具体步骤:
1. 克隆远程项目

$ git clone ../remotes/myproj.git myproject

此命令将远程项目 myproj 克隆到本地的 myproject 目录。克隆完成后,进入该目录并查看文件:

$ cd myproject
$ ls
file1.txt  file2.txt  file3.txt
  1. 添加子树 :假设还有一个名为 subproj 的项目,现在将其 master 分支作为子树添加到 myproject 中:
~/subtrees/local$ cd myproject
~/subtrees/local/myproject$ git subtree add --prefix subproject ~/subtrees/remotes/subproj.git master

执行该命令后,Git 会从远程仓库获取数据并添加子树。查看目录结构和日志:

~/subtrees/local/myproject$ ls
file1.txt   file2.txt   file3.txt   subproject/
~/subtrees/local/myproject$ ls subproject
subfile1.txt  subfile2.txt
~/subtrees/local/myproject$ git log --oneline
7d4f436 Add 'subproject/' from commit '906b5234f366bb2a419953a1edfb590aadc32263'
906b523 Add subfile2
5f7a7db Add subfile1
fada8bb Add file3
ef21780 Add file2
73e59ba Add file1
  1. 使用 squash 选项添加子树 :默认情况下,添加子树会包含项目的完整历史。若想避免添加所有历史,可以使用 squash 选项。首先重置项目:
$ git reset --hard HEAD~1

然后添加新的远程引用:

~/subtrees/local/myproject$ git remote add sub_origin  ~/subtrees/remotes/subproj.git

使用 squash 选项添加子树:

~/subtrees/local/myproject$ git subtree add --prefix subproject --squash sub_origin master

查看日志:

$ git log --oneline
6b109f0 Merge commit 'f7c3147d6df0609745228cc5083bb6c7d0b07d1a' as 'subproject'
f7c3147 Squashed 'subproject/' content from commit 906b523
fada8bb Add file3
ef21780 Add file2
73e59ba Add file1
2. 更新子树

如果需要将远程的更改拉取到子树中,可以使用 git subtree pull 命令:

$ git subtree pull --prefix subproject sub_origin master --squash

该命令会将远程的最新内容拉取到子树区域,并再次压缩历史。若不想压缩历史,可以省略 --squash 选项。

此外,还有 git subtree merge 命令,可用于将提交合并到由 --prefix 参数指定的子项目中。 git subtree merge 用于合并本地对子项目的更改,而 git subtree pull 则用于从远程获取更改。

需要注意的是,Git 中还有一种名为 subtree 的合并策略,它与 git subtree 命令不同。 git subtree 是一个集成到 Git 中的脚本,而 subtree 合并策略用于合并两个树,其中一个是另一个的子树。

3. 使用子树拆分功能

git subtree split 子命令可用于将子项目的内容提取到一个单独的分支中。它会提取与 <prefix> 相关的内容和历史,并将结果内容放在新分支的根目录,而不是子目录中。

例如,现有如下项目结构:

~/subtrees/local/myproject$ ls
file1.txt   file2.txt   file3.txt   subproject/

若要提取 subproject 子目录的内容和历史,可以使用以下命令:

~/subtrees/local/myproject$ git subtree split --prefix=subproject --branch=split_branch

查看新分支的内容和日志:

~/subtrees/local/myproject$ git checkout split_branch
Switched to branch 'split_branch'
~/subtrees/local/myproject$ ls
subfile1.txt  subfile2.txt
~/subtrees/local/myproject$ git log --oneline
906b523 Add subfile2
5f7a7db Add subfile1
4. 从拆分内容创建新项目

可以将拆分出的内容转移到另一个项目中。以下是具体步骤:
1. 创建新的 Git 项目

~/subtrees/local/myproject$ cd ~/
~$ mkdir newproj
~$ cd newproj
~/newproj$ git init
  1. 将拆分出的分支内容拉取到新项目中
~/newproj$ git pull ~/subtrees/local/myproject split_branch

查看新项目的内容和日志:

~/newproj$ ls
subfile1.txt  subfile2.txt
~/newproj$ git log --oneline
906b523 Add subfile2
5f7a7db Add subfile1
5. 子树推送

git subtree push 命令支持将子项目目录拆分并推送到远程仓库。例如,将 subproject 目录拆分并推送到 sub_origin 远程引用的 new_branch 分支:

~/subtrees/local/myproject$ git subtree push --prefix=subproject sub_origin new_branch
6. 工作树的使用

工作树允许在不同区域同时处理多个分支,且都连接到同一个本地仓库。以下是使用工作树的步骤:
1. 准备工作 :需要有互联网访问权限,并完成相关的 GitHub 账户设置和仓库克隆。将 calc2 仓库拆分为三个项目: super_calc sub_ui sub_docs ,并在 GitHub 上进行 Fork。
2. 克隆项目 :在新目录中克隆 super_calc 项目:

$ git clone https://github.com/<your github userid>/super_calc.git
  1. 创建工作树 :同时处理 master 分支和 features 分支,为 features 分支创建新的工作树:
$ git worktree add -b features ../super_calc_features origin/features
  1. 编辑文件并提交 :进入新的工作树目录,编辑 calc.html 文件并提交更改:
$ cd ../super_calc_features
Edit calc.html and change
<title>Calc</title>
to
<title> github_user_id's Calc</title>
$ git commit –am "Updating title"
  1. 查看分支和日志 :切换回原工作树,查看分支和日志:
$ cd ../super_calc
$ git branch
$ git log --oneline features
  1. 移除工作树 :不再需要工作树时,先移除实际目录,再使用 prune 选项清除工作树引用:
$ rm –rf ../super_calc_features
$ git worktree prune
7. 子模块的使用

子模块是从原始项目(超级项目)链接到其他项目的一种方式。以下是使用子模块的步骤:
1. 准备工作 :需要有互联网访问权限,并完成相关的 GitHub 账户设置和仓库克隆。
2. 添加子模块 :在 super_calc 项目中添加 sub_ui 作为子模块:

$ git submodule add https://github.com/<your github userid>/sub_ui sub_ui
  1. 提交和推送 :提交并推送子模块映射和数据到本地和远程仓库:
$ git commit -m "Add submodule sub_ui"
$ git push
  1. 克隆包含子模块的项目 :克隆一个新的包含子模块的项目副本:
$ cd ..
$ git clone https://github.com/<your github userid>/super_calc super_calc2
  1. 初始化子模块 :进入新克隆的项目目录,查看子模块状态并初始化:
$ cd super_calc2
$ ls sub_ui
$ git submodule status
$ git submodule update --init
  1. 更新子模块代码 :进入子模块目录,编辑 calc.html 文件并提交更改:
$ cd sub_ui
Edit calc.html and change
<title>Advanced Calculator</title>
to
<title> your_name_here Advanced Calculator</title>
$ git commit -am "Update title"
  1. 更新超级项目 :返回超级项目目录,查看子模块状态并提交更新:
$ cd ..
$ git submodule status
$ git status
$ git commit -am "Update for submodule sub_ui"
$ git push

综上所述,工作树、子模块和子树都是 Git 中非常实用的功能,但在大多数情况下,更好的做法是在构建或部署过程中,将其他仓库构建的可交付成果作为工件使用。应仅在仓库之间存在真正的源依赖关系,且需要这种紧密的源连接时,才使用子模块和子树。过多的此类依赖可能表明需要对仓库之间的代码进行重构。

Git 中工作树、子模块和子树的使用指南

8. 各功能操作流程总结

为了更清晰地展示工作树、子模块和子树的操作流程,下面通过表格和流程图进行总结。

8.1 操作步骤表格
功能 操作步骤 命令示例
添加子树 1. 克隆远程项目
2. 添加子树
3. 可选:使用 squash 选项添加子树
1. git clone ../remotes/myproj.git myproject
2. git subtree add --prefix subproject ~/subtrees/remotes/subproj.git master
3. git subtree add --prefix subproject --squash sub_origin master
更新子树 拉取远程更改到子树 git subtree pull --prefix subproject sub_origin master --squash
子树拆分 提取子项目内容到单独分支 git subtree split --prefix=subproject --branch=split_branch
从拆分内容创建新项目 1. 创建新 Git 项目
2. 拉取拆分分支内容到新项目
1. git init
2. git pull ~/subtrees/local/myproject split_branch
子树推送 拆分并推送子项目目录到远程 git subtree push --prefix=subproject sub_origin new_branch
工作树使用 1. 准备工作
2. 克隆项目
3. 创建工作树
4. 编辑文件并提交
5. 查看分支和日志
6. 移除工作树
1. Fork 项目到 GitHub
2. git clone https://github.com/<your github userid>/super_calc.git
3. git worktree add -b features ../super_calc_features origin/features
4. 编辑文件并 git commit –am "Updating title"
5. git branch git log --oneline features
6. rm –rf ../super_calc_features git worktree prune
子模块使用 1. 准备工作
2. 添加子模块
3. 提交和推送
4. 克隆包含子模块的项目
5. 初始化子模块
6. 更新子模块代码
7. 更新超级项目
1. Fork 项目到 GitHub
2. git submodule add https://github.com/<your github userid>/sub_ui sub_ui
3. git commit -m "Add submodule sub_ui" git push
4. git clone https://github.com/<your github userid>/super_calc super_calc2
5. git submodule update --init
6. 编辑文件并 git commit -am "Update title"
7. git commit -am "Update for submodule sub_ui" git push
8.2 操作流程 mermaid 流程图
graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B{选择功能}:::decision
    B -->|添加子树| C(克隆远程项目):::process
    C --> D(添加子树):::process
    D --> E{是否使用 squash 选项}:::decision
    E -->|是| F(使用 squash 选项添加子树):::process
    E -->|否| G(完成添加子树):::process
    B -->|更新子树| H(拉取远程更改到子树):::process
    B -->|子树拆分| I(提取子项目内容到单独分支):::process
    B -->|从拆分内容创建新项目| J(创建新 Git 项目):::process
    J --> K(拉取拆分分支内容到新项目):::process
    B -->|子树推送| L(拆分并推送子项目目录到远程):::process
    B -->|工作树使用| M(准备工作):::process
    M --> N(克隆项目):::process
    N --> O(创建工作树):::process
    O --> P(编辑文件并提交):::process
    P --> Q(查看分支和日志):::process
    Q --> R(移除工作树):::process
    B -->|子模块使用| S(准备工作):::process
    S --> T(添加子模块):::process
    T --> U(提交和推送):::process
    U --> V(克隆包含子模块的项目):::process
    V --> W(初始化子模块):::process
    W --> X(更新子模块代码):::process
    X --> Y(更新超级项目):::process
    G --> Z([结束]):::startend
    H --> Z
    I --> Z
    K --> Z
    L --> Z
    R --> Z
    Y --> Z
9. 各功能适用场景分析

不同的功能在不同的场景下有其独特的优势,下面对工作树、子模块和子树的适用场景进行分析。

9.1 工作树适用场景
  • 并行开发 :当需要同时在多个分支上进行开发时,工作树可以让你在不同的工作区独立处理各个分支,避免频繁切换分支带来的不便。例如,开发人员可以在一个工作树中处理 master 分支的稳定版本,同时在另一个工作树中进行 feature 分支的新功能开发。
  • 快速验证 :在不影响主工作区的情况下,可以快速验证某个分支的更改。比如,需要验证一个实验性的功能分支,就可以创建一个新的工作树来进行测试。
9.2 子模块适用场景
  • 项目复用 :当多个项目需要共享同一个代码库时,使用子模块可以将共享代码作为一个独立的仓库进行管理,方便在不同项目中复用。例如,多个项目都依赖同一个工具库,就可以将该工具库作为子模块添加到各个项目中。
  • 独立开发 :子模块允许各个子项目独立开发和维护,每个子项目都有自己的版本控制。这样,不同的开发团队可以专注于自己负责的子项目,而不会影响其他部分。
9.3 子树适用场景
  • 集成外部项目 :当需要将一个外部项目集成到自己的项目中,并且希望将其历史记录也包含进来时,子树是一个不错的选择。例如,将一个开源的库集成到自己的项目中,使用子树可以保留该库的完整历史。
  • 代码合并 :在进行代码合并时,子树可以更好地处理目录结构的差异。当两个项目的目录结构不同,但需要将其中一个项目作为子项目合并到另一个项目中时,子树可以自动调整目录结构,使合并过程更加顺利。
10. 注意事项和最佳实践

在使用工作树、子模块和子树时,需要注意一些事项,并遵循一些最佳实践。

10.1 注意事项
  • 子模块初始化 :克隆包含子模块的项目时,需要手动初始化子模块,否则子模块可能不会正常工作。可以使用 --recursive 选项在克隆时自动初始化子模块。
  • 子树历史管理 :使用子树时,要注意历史记录的管理。如果不需要保留完整的历史记录,可以使用 squash 选项来简化历史。
  • 工作树清理 :不再使用工作树时,要及时清理,避免占用过多的磁盘空间。
10.2 最佳实践
  • 明确依赖关系 :在使用子模块和子树时,要明确各个项目之间的依赖关系,避免出现循环依赖等问题。
  • 定期更新 :定期更新子模块和子树,确保使用的是最新的代码。
  • 文档记录 :对于复杂的项目结构,要做好文档记录,方便其他开发人员理解和维护。

通过合理使用工作树、子模块和子树,可以提高开发效率,更好地管理项目结构和依赖关系。但在实际应用中,要根据具体的项目需求和场景选择合适的功能,并注意相关的注意事项和最佳实践。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值