> For the complete documentation index, see [llms.txt](https://b1f6c1c4.gitbook.io/learn-git-the-super-hard-way/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://b1f6c1c4.gitbook.io/learn-git-the-super-hard-way/chapter12.md).

# 第12章：单repo多分支工作流

考虑如下场景：开发一个**中型、小型或者微型**微服务系统，包含10个组件

方案1：（多repo）创建10个git repo，每个repo一个master一个dev

方案2：（单repo单分支）创建1个git repo，设一个master一个dev

方案3：（单repo多分支）创建1个git repo，弃用master，每个组件单设自己的master和dev等等

|                    | 方案1      | 方案2      | 方案3      |
| ------------------ | -------- | -------- | -------- |
| 环境配置简易程度           | -------- | ++++++++ | ---      |
| 空间独立（同时修改不同组件是否可行） | ++++++++ | -------- | ++++++++ |
| 时间对齐（组件版本是否能够统一）   | -------- | ++++++++ | +++++    |
| 组件之间可以互相参照         | -------- | ++++++   | ++++++++ |
| 添加删除组件是否方便         | ++++++++ | -------- | +++++++  |

由此可见，微型项目方案2最优，小型和中型项目方案3最优。 方案3可能存在的问题是：

* 时间对齐无法实现：不存在的，参考下文
* 需要频繁checkout：不存在的，参考下文
* 单个git库过大：不存在的，我已经假设了是**中小型**系统

## 单repo多分支工作流的分支设置

对于小型项目，推荐按以下方式配置分支：

* doc
  * 全部系统设计文档
* master
  * 通过merge componentX整合系统各个部分
* component1
  * 通过merge doc取得与之相关的部分设计文档
* component2
  * 通过merge doc取得与之相关的部分设计文档
* component3
  * 通过merge doc取得与之相关的部分设计文档
* component4
  * 通过merge doc取得与之相关的部分设计文档
* ...

对于中型项目，推荐按以下方式配置分支：

* doc
  * 系统总体设计文档，组件接口文档
* master
  * 在测试稳定后，merge dev
* release
  * 发布之前merge master
* dev
  * 通过merge componentX/master整合系统各个部分
  * 此处进行集成测试
* component1/doc
  * 通过merge doc取得系统总体文档，并添加组件内部设计文档
* component1/master
  * 在测试稳定后，merge component1/dev
* component1/dev
  * 在功能开发完毕后，merge component1/featY
* component1/feat1
  * 在此处开发具体功能
* component1/feat2
  * 在此处开发具体功能
* component1/feat3
  * 在此处开发具体功能
* component2/doc
  * 通过merge doc取得系统总体文档，并添加组件内部设计文档
* component2/master
  * 在测试稳定后，merge component2/dev
* component2/dev
  * 在功能开发完毕后，merge component2/featY
* component2/feat1
  * 在此处开发具体功能
* component2/feat2
  * 在此处开发具体功能
* component2/feat3
  * 在此处开发具体功能
* ...

## 单repo多分支工作流的具体操作

需要注意的是，不同component分支上的worktree完全不同，互相独立，绝对不能够互相merge，也不能共享同一个worktree； 而同一component里面各个小分支的worktree基本一致，只是有开发先后关系（类似于单体应用的不同分支），适合互相merge，也一般共享同一个worktree。

### 创建

首先创建repo，不连带创建worktree：

```bash
mkdir the-project
git init --bare the-project/the-project.git
# Initialized empty Git repository in /root/the-project/the-project.git/
# git -C the-project/the-project.git remote add origin https://github.com/...
```

然后给doc和每一个component分别创建一个（小repo和）worktree：

```bash
cd the-project/the-project.git
# 由于git worktree实在太蠢，这里搞一个workaround
git hash-object -t commit -w --stdin <<EOF
tree 0000000000000000000000000000000000000000

EOF
# 60bc2812cc97ab2d2f2c7168aa101f7bfabcbf88
git update-ref refs/workaround 60bc2812cc97ab2d2f2c7168aa101f7bfabcbf88
git worktree add --no-checkout --detach ../doc refs/workaround
# Preparing worktree (detached HEAD 60bc281)
git --git-dir=worktrees/doc symbolic-ref HEAD refs/heads/doc
git worktree add --no-checkout --detach ../component1 refs/workaround
# Preparing worktree (detached HEAD 60bc281)
git --git-dir=worktrees/component1 symbolic-ref HEAD refs/heads/component1
git worktree add --no-checkout --detach ../component2 refs/workaround
# Preparing worktree (detached HEAD 60bc281)
git --git-dir=worktrees/component2 symbolic-ref HEAD refs/heads/component2
git worktree add --no-checkout --detach ../component3 refs/workaround
# Preparing worktree (detached HEAD 60bc281)
git --git-dir=worktrees/component3 symbolic-ref HEAD refs/heads/component3
git worktree add --no-checkout --detach ../component4 refs/workaround
# Preparing worktree (detached HEAD 60bc281)
git --git-dir=worktrees/component4 symbolic-ref HEAD refs/heads/component4
git update-ref -d refs/workaround
rm -f objects/60/bc2812cc97ab2d2f2c7168aa101f7bfabcbf88
```

### 从其他地方clone

不建议使用`git clone --bare`，因为还需要手工修改fetch信息（参考第5章，这种方式创建的repo默认没有fetch的config项）

建议按上述方法创建，然后再remote add。

### doc分支

直接在根目录（`the-project/doc`）下写文档即可：

```bash
cd the-project/doc
echo 'Some documents' > documents.txt
git add documents.txt
git hash-object -t commit --stdin -w <<EOF
tree $(git write-tree)
author b1f6c1c4 <b1f6c1c4@gmail.com> 1514736000 +0800
committer b1f6c1c4 <b1f6c1c4@gmail.com> 1514736000 +0800

Write documents
EOF
# ffe7520ba83e48bb254b9eb0fd07390d98124ede
git reset --soft ffe7520b
```

### component分支

首先配置开发环境（这里以c++为例）：

```bash
cd the-project/component1
echo 'build/' > .gitignore
cat - >Makefile <<EOF
build/component1: main.cpp
    g++ -std=2a -o $@ $^
EOF
git add .gitignore Makefile
git hash-object -t commit --stdin -w <<EOF
tree $(git write-tree)
author b1f6c1c4 <b1f6c1c4@gmail.com> 1514736010 +0800
committer b1f6c1c4 <b1f6c1c4@gmail.com> 1514736010 +0800

Setup environment
EOF
# 4dbe0f181e875f7a96b3f6079038d353c239a6f4
git reset --soft 4dbe0f1
```

然后从doc分支读取文档：

```bash
cd the-project/component1
# git rm -rf doc/
git read-tree --prefix=doc/ doc
git checkout-index -fua
git hash-object -t commit --stdin -w <<EOF
tree $(git write-tree)
parent $(git rev-parse HEAD)
parent $(git rev-parse doc)
author b1f6c1c4 <b1f6c1c4@gmail.com> 1514736010 +0800
committer b1f6c1c4 <b1f6c1c4@gmail.com> 1514736010 +0800

Merge branch doc
EOF
# 9f1f329f771711c7160c0784d4cf8a7f157d5c27
git reset --soft 9f1f329
```

### 文档更新以后各component的处理

假设文档在doc更新了：

```bash
cd the-project/doc
git rm -f documents.txt
# rm 'documents.txt'
echo 'New documents' > new-documents.txt
git add new-documents.txt
git hash-object -t commit --stdin -w <<EOF
tree $(git write-tree)
parent $(git rev-parse HEAD)
author b1f6c1c4 <b1f6c1c4@gmail.com> 1514736000 +0800
committer b1f6c1c4 <b1f6c1c4@gmail.com> 1514736000 +0800

Move to new documents
EOF
# 9584d01267d5f16c581e521d3bb401f211508eee
git reset --soft 9584d012
```

那么需要在component1分支更新文档：

```bash
cd the-project/component1
git rm -rf doc/
# rm 'doc/documents.txt'
git read-tree --prefix=doc/ doc
git checkout-index -fua
git hash-object -t commit --stdin -w <<EOF
tree $(git write-tree)
parent $(git rev-parse HEAD)
parent $(git rev-parse doc)
author b1f6c1c4 <b1f6c1c4@gmail.com> 1514736010 +0800
committer b1f6c1c4 <b1f6c1c4@gmail.com> 1514736010 +0800

Merge branch doc
EOF
# 7ad8da3b9f5825c63247bf655ef6da68b637453b
git reset --soft 7ad8da3
git config alias.lg "log --graph --pretty=tformat:'%h -%d (%an/%cn) %s' --abbrev-commit"
git lg
# *   7ad8da3 - (HEAD -> component1) (b1f6c1c4/b1f6c1c4) Merge branch doc
# |\  
# | * 9584d01 - (doc) (b1f6c1c4/b1f6c1c4) Move to new documents
# * | 9f1f329 - (b1f6c1c4/b1f6c1c4) Merge branch doc
# |\| 
# | * ffe7520 - (b1f6c1c4/b1f6c1c4) Write documents
# * 4dbe0f1 - (b1f6c1c4/b1f6c1c4) Setup environment
```

### master

master之于component，就是component之于doc； 唯一区别是一个master会merge多个compoent，而且master上自身代码很少 （可能只有README.md、LICENSE、docker-compose.yml等等全局配置）。

和component完全一致，在master上依次read-tree、write-tree、commit-tree， 就可以将不同组件整合到master上。 别忘了多加几个parent。

## FAQ

### master要不要直接merge doc

如果在master上进行一些集成测试，那么应该有doc。 否则可以省略。

### 为什么不用简单方便的`git merge -s subtree doc`

一些诡异的情况下subtree无法完整地复制doc那边的整个tree的情况， 比如删掉的文件还在、新添加的文件没有出现等等。 参考第6章。

### 我应该在哪里build

一般来说应该在每个组件各自的worktree里面build， 比如`node_modules`、`*.o`、`*.pyc`等等。 如果采用docker，那么每个组件分支上应该能生成一个image， 而到了master里面就直接`docker-compose up`了。

如果出于ci的需要，也可以选择在master里面再次build。但这就需要双倍的磁盘空间。

### 如何简化操作

参见第8章。另外别忘了`git push --all`，`git log --all`等等。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://b1f6c1c4.gitbook.io/learn-git-the-super-hard-way/chapter12.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
