这篇文章上次修改于 2311 天前,可能其部分内容已经发生变化,如有疑问可询问作者。 假设你有一个在线商城项目 Shop,这个项目下有个订单模块 order,这个 order 模块很通用,你在下一个团购系统 Groupon,也要用到这个 order 模块,最简单的方式就是把整个 order 模块 copy 过来。 然而,这种 copy 方式,会很难维护:order 模块修改了一个Bug,然后又要复制更新到所有用了 order 模块的项目里。 这时就需要 git subtree 来管理这个 order 模块了:把 order 模块单独作为一个 git 仓库,在需要用到 order 模块的项目,通过使用 git subtree 把 order 模块当作一个 git 项目来引入,这就解决了 order 模块的共享和维护问题。 git subtree --help 可以查看 git subtree 命令的帮助文档。 git subtree 命令中,都会用到一个参数 --prefix=,可以简写成 -P ,本文的命令都是这样使用的。 ##git subtree add 添加项目作为子树 ``` git subtree add -P <子树名> <子仓库地址> <分支> ``` 执行以上命令后,项目下就会新创建一个名为 <子树名> 的目录。 如果提前使用 git remote add <子仓库名> <子仓库地址> 添加了子项目的远程仓库地址(建议按此方式,下文都基于此),那么也可以这样: ``` git subtree add -P <子树名> <子仓库名> <分支> ``` 以上命令可以多加一个 --squash 参数: ``` git subtree add -P <子树名> <子仓库名> <分支> --squash ``` --squash 参数含义是:把 subtree 的改动合并成一次commit,这样就不用写入子项目完整的历史记录。 ##git subtree pull 从子仓库拉取子树更新 先 fetch: ``` git fetch <子仓库名> <分支> ``` 后 pull: ``` git subtree pull -P <子树名> <子仓库名> <分支> ``` 以上命令也可以多加一个 --squash 参数: ``` git subtree pull -P <子树名> <子仓库名> <分支> --squash ``` ##git subtree push 推送子树更新到子仓库 ``` git subtree push -P <子树名> <子仓库名> <分支> ``` ##场景一:把其他已有项目,添加为子树 场景假设:新建了一个团购系统项目 Groupon,也需要用到 order 模块,那么按以下步骤即可: ###添加子项目的远程仓库地址: ``` git remote add order-repo https://github.com/xxx/order.git ``` ###在 Groupon 项目里进行添加项目作为子树: ``` cd Groupon git subtree add -P order order-repo master --squash git subtree pull -P order order-repo master ``` ##场景二:把项目里的某个目录,分割出去作为子树维护 场景假设:现在在开发 Shop 项目有一段时间了,发现里面的 order 模块非常通用,着手使用 git subtree 把 order 作为一个子项目单独维护。 ###方式一:分割出去,并清理 commit 记录 ``` cd Shop git subtree split -P shop -b order-branch //1 cd ../ //2 mkdir tempDir //2 cd tempDir //2 git init //2 git pull ../Shop order-branch //2 git remote add order-repo https://github.com/xxx/order.git //3 git push -u order-repo master //3 cd ../ //4 rm -rf tempDir //4 cd Shop //5 git filter-branch -f --index-filter "git rm -r -f -q --cached --ignore-unmatch order" --prune-empty HEAD //5 注意:这里是 order,那个子目录(-P 对应的值) git branch -D order-branch //6 ``` 流程说明: 1. 在 Shop 项目里把 order 模块分割成一个临时分支 order-branch 2. 项目外新建一个临时目录 tempDir 并创建 git 仓库,拉取这个 order 模块的临时分支 order-branch 3. 在临时目录里,添加 order 子项目的远程仓库地址,并 push 到该远程仓库 4. 删除临时目录 tempDir 5. 清理掉原来的 order 模块的 commit 记录 6. 按需删除临时分支 order-branch 当然,把 order 模块抽离出去之后,这个 Shop 项目还是需要用到 order 模块的,重新走一遍 《场景一(把其他已有项目,添加为子树)》 的流程就可以了: ``` cd Shop git remote add order-repo https://github.com/xxx/order.git git subtree add -P order order-repo master --squash git subtree pull -P order order-repo master ``` ##方式二:直接作为子树提交到子仓库,不清理 commit 记录 git subtree --help 查看文档,git subtree push 命令本身就会进行 git subtree split 操作: ``` Does a split (see below) using the supplied and then does a git push to push the result to the repository and ref. This can be used to push your subtree to different branches of the remote repository. ``` 所以简单点就 git subtree push 完事。 ``` cd Shop git remote add order-repo https://github.com/xxx/order.git //1 git subtree push -P order order-repo master //2 ``` 流程说明: 1. 添加 order 子项目的远程仓库地址 2. order 模块作为 order 子树 push 到远程仓库 ##场景三:在现有项目里,新建了一个子模块(子目录),准备当作子树维护 场景假设:现在在开发 Shop 项目,刚新建了个 order 模块(还未提交到git),准备把这个 order 模块当作一个子项目维护。 ###方式一:子模块(子目录)单独提交到远程仓库,删除子模块(子目录)后,重新引入 ``` cd Shop/order //1 git init //1 git add . //1 git commit -m "add order ..." //1 git remote add order-repo https://github.com/xxx/order.git //1 git push -u order-repo master cd ../ //2 rm -rf order //2 git remote add order-repo https://github.com/xxx/order.git //3 git subtree add -P order order-repo master --squash //3 ``` 流程说明: 1. 进入 order 模块创建仓库、提交、添加远程仓库地址、push 2. 删除 order 模块 3. 添加 order 子项目远程仓库地址,git subtree add 添加 order 子项目作为子树 ###方式二:先把子模块(子目录)提交,然后 git subtree push 场景二 里的 《 方式二:直接作为子树提交到子仓库,不清理 commit 记录》 一样了。 ##四:删除子树 场景假设:突然有一天,Shop 项目不再开放下单功能,以后也仅仅做静态展示,很确定 order 子模块可以删除了,那么有以下两种删除途径。 ###删除子模块目录,并commit ``` git rm -r <子模块目录名> git commit -m "remove <子模块目录名> ..." ``` 此方式的缺点就是 git log 里还有留着 order 子模块的 commit 历史。 ###使用 filter-branch 彻底删除 ``` git filter-branch -f --index-filter "git rm -r -f -q --cached --ignore-unmatch order" --prune-empty HEAD //注意:这里是 order,那个子模块(-P 对应的值) ``` 此方式可以把order 子模块的 commit 历史一并删除。完成后push需要--force参数 ##五:重命名子树 直接重命名子树的目录名,并提交: ``` cd shop mv order xiadan git add . git commit -m "reanme order -> xiadan" ``` 只是以后 pull 和 psuh 子树的命令要改变少少: ``` git subtree pull -P xiadan order-repo master //原来的 -P 参数值也由 order 改成 xiadan git subtree push -P xiadan order-repo master //原来的 -P 参数值也由 order 改成 xiadan ```
没有评论