git subtree 的使用方法

场景1 项目拆分

例如,某个git项目有三个平行的目录p1/,p2/,p3/。随着开发进程的推移,发现这三个目录的代码或文件的关联度很低,实际上是各自独立的,例如最好用分支p1proj,p2proj,p3proj来进行管理。原来的master分支,可以废弃不用。用git subtree来实现这种拆分。

创建一个示例项目。

#!/bin/bash
mkdir proj && cd proj && git init . && mkdir p1 p2 p3 
i=1;while (($i < 4));do
 j=1
 while (($j < 3));do
   echo "$i.$j" > p$i/p$i.$j && git add p$i && git commit -m "p$i add p$i.$j" 
   ((j++))
 done
((i++))
done

# ls -R
.:
p1  p2  p3
./p1:
p1.1  p1.2
./p2:
p2.1  p2.2
./p3:
p3.1  p3.2

# gitk master

实现拆分

# git subtree split --prefix=p1 -b p1proj
# git subtree split --prefix=p2 -b p2proj
# git subtree split --prefix=p3 -b p3proj
# git branch
* master
  p1proj
  p2proj
  p3proj
# gitk p1proj p2proj p3proj



以上实现了按目录对项目进行逻辑拆分。提交信息保持一致,但提交HASH值并不一致。例如在分支p1proj,有一个commit “p1 add p1.1”,来自于master分支同样信息的commit,但是这两个commit的hash值是不同的。

# git checkout p1proj
Switched to branch 'p1proj'
# ls
p1.1  p1.2

p1proj分支只包含master分支p1目录下的内容。这个例子中,subtree之后master分支不再使用。例如,之后针对p1的开发,在p1proj分支上进行,不必再与master分支保持同步。


场景2 与三方库集成

利用subtree引入第三方库,作为项目中的一个目录。这种方法与submodule不同。前者是将自己的代码与三方库融合在一起管理,不需要git submodule那样对子目录进行特殊配置。当第三方库有更新时,subtree pull命令可以拉取上游更新,与自己的代码merge在一起。

# 创建一个testlib用于测试
# mkdir testlib && cd testlib && git init . &&  \
 echo "libfile1" > libfile1 && git add . && git commit -m "libfile1"
# 切换到proj项目master分支
# cd ../proj
# git branch
* master
# ls
p1  p2  p3
# 添加这个testlib库
# git subtree add --prefix=lib1 ../testlib master
git fetch ../testlib master
warning: no common commits
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
来自 ../testlib
 * branch            master     -> FETCH_HEAD
Added dir 'lib1'
# ls
lib1  p1  p2  p3
# ls lib1/
libfile1
# git log
commit 52fb9c2e2e850715d91285c6c828b664341b8025
Merge: 8f03cd4 a4a0852
Author: 
Date:  

    Add 'lib1/' from commit 'a4a08523d9f0a69a6481c9977606c9726cecd36b'
    
    git-subtree-dir: lib1
    git-subtree-mainline: 8f03cd40bdc5bcd371a30d53e73f7274aa64122a
    git-subtree-split: a4a08523d9f0a69a6481c9977606c9726cecd36b

commit a4a08523d9f0a69a6481c9977606c9726cecd36b
Author: 
Date: 

    libfile1
git subtree 的使用方法_第1张图片
当上游的lib有更新的时候,使用subtree pull拉取并合并更新。

# 在testlib上发生了版本更新
# echo "libfile2" > libfile2 && git add libfile2 && git commit -m "libfile2"

# 在proj拉取testlib的更新
# git subtree pull --prefix=lib1 ../testlib master
# git log
commit dbf7b90f1dc9185eba0ee3c30ce79bad457365a8
Merge: 52fb9c2 6b9d5a8
Author: 
Date: 

    Merge commit '6b9d5a80386cbd90c0314e9fb058cbecc5bdbe76'

commit 6b9d5a80386cbd90c0314e9fb058cbecc5bdbe76
Author: 
Date: 

    libfile2
git subtree 的使用方法_第2张图片

参数--rejoin

在split的时候,如果指定了rejoin参数,将会把新产生的提交合并到主项目。例如前面的例子,如果执行

git subtree split --prefix=p1 -b p1proj --rejoin
主分支将产生如下的提交序列

git subtree 的使用方法_第3张图片
git log将显示重复的提交,一个是original,一个是synthetic。以上命令可以反复多次运行。例如,在master分支增加一个文件p1/p1.3,提交信息为“p1 add p1.3”。然后再次运行上面带有rejoin参数的subtree split命令。主分支的提交序列变为下图。

git subtree 的使用方法_第4张图片

p1proj分支此时有三次提交。


参考文献

  • https://github.com/git/git/blob/master/contrib/subtree/git-subtree.txt

你可能感兴趣的:(scm)