git拆分工程

随着工程的代码量的增加, 大家可能需要对工程进行拆分, 并且拆分的同时还需要保留子工程的历史提交版本. 这时就需要用到git filter-branch命令了.

filter-branch是一个非常强大的git命令, 用来重写版本历史. 通过他, 我们可以实现一些高级功能.

例如:

  • 删除所有历史版本中的某个文件
  • 工程拆分
  • 更具预先定义的规则修改历史提交信息

等等

filter-branch执行成功后, 会产生与原来不同的提交, 所以对分支修改后, 不可再push到正在使用的已发布分支.

1. 场景

原工程

1
2
3
4
5
saas-app
├─core
│ ├─user-api
│ └─user-svc
└─common

拆分成两个工程usercommon:

1
2
3
4
user
├─user-api
└─user-svc
common

2. 拆分方法

以user为例:

编写处理脚本

新建index-filter.sh, 每个index均会执行该该脚本

1
2
3
4
#!/bin/bash
git read-tree --empty
git read-tree --prefix=user-api/ ${GIT_COMMIT}:core/user-api/
git read-tree --prefix=user-svc/ ${GIT_COMMIT}:core/user-svc/

说明: 对于每一个index, 首先清空index, 并将core/user-api/路径改为user-api/
index-filter.sh需要有+x权限
路径不能以/开头, 必须以/结尾

调用filter-branch命令

1
git filter-branch -f --index-filter /s/index-filter.sh -- --all
  • -f 会强制执行, 忽略上一次filter-branch操作的备份
  • /filter.sh 是处理脚本, 会对每次index执行该脚本, 脚本路径为绝对路径
  • -- 用于分割
  • --all 针对所有的分之, tags

此过程执行时间较长, 且IO很高, 建议在内存盘中执行(tmpfs).

清理空提交

如果有多个提交

1
git filter-branch -f --commit-filter 'git_commit_non_empty_tree "$@"' -- --all

分出子版本库

可清除git版本库中无法触及的提交

1
2
3
4
5
git init --bare ../user.git
git push ../user.git refs/*

cd ..
git clone user.git user

简单拆分

对于简单的拆分(某一个子目录直接作为子工程, 例如本利中的common)
可用使用下述命令

1
git filter-branch --subdirectory-filter common -- --all

该命令执行的同时会自动清除无用提交

3. 预备知识

git read-tree

git read-tree用于将制定提交读入用户的index(暂存区). 也就是.git/index文件. 可通过git ls-files –stage查看暂存区中存储的内容.

  • git read-tree <commit-id>
    可读取指定提交到当前index.
    (我的理解git的快照就是index, 历史版本就是一个个的index想连, commit操作相当于将当前index压入版本库, 新建一个新的index)

  • git read-tree --empty
    清除当前的index.
    (这样我们在暂存区会看到文件被删除的信息, 因为git拿当前index与最后一次提交的快照进行对比, 由于当前index全清空了, 故会提示文件被删除)

  • git read-tree --prefix=user-svc/ <commit-id>:core/user-svc/
    将制定提交的指定文件读入当前index, 并且文件路径添加user-svc前缀.
    (–prefix中的user-svc/的/不可省略)

引用git帮助文档中的一张图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
                 commit-tree
commit obj
+----+
| |
| |
V V
+-----------+
| Object DB |
| Backing |
| Store |
+-----------+
^
write-tree | |
tree obj | |
| | read-tree
| | tree obj
V
+-----------+
| Index |
| "cache" |
+-----------+
update-index ^
blob obj | |
| |
checkout-index -u | | checkout-index
stat | | blob obj
V
+-----------+
| Working |
| Directory |
+-----------+

git filter-branch

用于重写分支的历史

  • –index-filter

每次回调, git会设置这些环境变量
GIT_AUTHOR_NAME
GIT_AUTHOR_EMAIL
GIT_AUTHOR_DATE
GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL
GIT_COMMITTER_DATE

对于–index-filter, 如果在修改过程中修改了当前的index, 则回调结束后, git会将当前的index作为新的提交.

4. 其他

可用该脚本生成一个demo的仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/bin/bash

gitCommit(){
git add -A
git commit -m $1
}

mkdir saas-app
cd saas-app
git init

mkdir -p core/user-api
mkdir -p core/user-svc
mkdir -p common

echo "commit1" > core/user-api/code
echo "commit1" > core/user-svc/code
echo "commit1" > common/code
gitCommit "commit1"

echo "commitApi" >> core/user-api/code
gitCommit "commitApi"

echo "commitSvc" >> core/user-svc/code
gitCommit "commit1"

echo "commitCommon" >> common/code
gitCommit "commitCommon"

git checkout -b user
echo "user" >> core/user-api/code
echo "user" >> core/user-svc/code
gitCommit "user"

git checkout master
echo "commitCommon" >> common/code
gitCommit "common"

git merge user --no-edit

git branch -D user

echo "commit2" >> core/user-api/code
echo "commit2" >> core/user-svc/code
echo "commit2" >> common/code
gitCommit "commit2"


参考
官网filter-branch命令讲解

0%