Techyou labs
真正的爱应该超越生命的长度,心灵的宽度,灵魂的深度
常用标签
您正在查看:标签 git 下的文章

日常开发时经常会遇到在稳定分支发hotfix的情况,通常的解决办法是git stash暂存当前工作区内容,再checkout到需要发补丁的分支下工作,完了再切回来。大多数时候这么干没啥问题。不过如果类似node js项目或者其他有大量外部库依赖项目时就麻烦了。比如php或者golang,通常vendor目录都会git ignore。如果刚好工作分支升级了vendor下的依赖库,与即将发补丁的分支依赖库冲突,那就麻烦了,只能clone到别的目录下来做。好在git 2.5已经把这个需求内置实现。这就要介绍今天的主角:git worktree 命令:


git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<commit-ish>]
git worktree list [--porcelain]
git worktree lock [--reason <string>] <worktree>
git worktree move <worktree> <new-path>
git worktree prune [-n] [-v] [--expire <expire>]
git worktree remove [-f] <worktree>
git worktree unlock <worktree>

项目开发的一般流程是先fork项目,然后基于自己fork的项目开发,最后通过提交PR将自己的代码merge到上游。 这里会涉及到两个:上游仓库和自己fork的仓库,它们之间最终通过PR关联起来。 但是,由于Golang特定的GOPATH依赖,导致这种方式针对于Golang项目会有些问题。

常见开发流程:

  1. 将项目fork自己的账户下;
  2. Clone自己fork的项目到本地,并进行开发;
  3. 将修改的代码push到自己fork的项目中;
  4. 从fork的项目中为自己的代码创建PR;
  5. 等这个PR被merge后,自己的代码才能进入上游项目。

举个例子,来演示整个过程。为描述清晰、方便,有如下约定:

上游项目为org/repo
fork项目为user/repo

按照常规的Git开发流程,在本地clone的fork项目中直接go build,会存在依赖问题:

Fork出来的项目在GOPATH中的路径发生了改变,但是代码中import的路径没有改变,所以没有使用fork项目中的package。

即使构建成功,但是真正使用也不是fork项目中修改的代码 有可能GOPATH中已经存在org/repo,所以按照上面的方式构建,不会提示错误。但是如果在user/repo中修改的代码,是无法生效的。 因为代码中引用的是org/repo的package,不是user/repo中修改过的package。

问题的产生

subtree 处理多层包含是没有问题的,因为包含进项目之后, 别人根本看不出这是一个 subtree, 所以它本质上还只是管理本地 repo 的一种方法。

使用 Git subtree 新建或更新子项目的时候,可以选用 --squash 参数, 它的作用就是把 subtree 子项目的更新记录进行合并,再合并到主项目中。

所以,在使用 --squash 参数的情况下, subtree add 或者 pull 操作的结果对应两个 commit, 一个是 Squash 了子项目的历史记录, 一个是 Merge 到主项目中。

这种做法下,主项目的历史记录看起来还是比较整齐的。 但在子项目有更新,需要 subtree pull 的时候,却经常需要处理冲突。 严重的,在每次 subtree pull 的时候都需要重复处理同样的冲突,非常烦人。

如果不使用 --squash 参数,子项目更新的时候,subtree pull 很顺利, 能够自动处理已解决过的冲突,缺点就是子项目的更新记录“污染”了主项目的。

September 21, 2017

假设你有一个在线商城项目 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 &lt;子树名&gt; &lt;子仓库名&gt; &lt;分支&gt;

以上命令可以多加一个 --squash 参数:


git subtree add -P &lt;子树名&gt; &lt;子仓库名&gt; &lt;分支&gt; --squash

--squash 参数含义是:把 subtree 的改动合并成一次commit,这样就不用写入子项目完整的历史记录。

git subtree pull 从子仓库拉取子树更新

先 fetch:


git fetch &lt;子仓库名&gt; &lt;分支&gt;

后 pull:


git subtree pull -P &lt;子树名&gt; &lt;子仓库名&gt; &lt;分支&gt;

以上命令也可以多加一个 --squash 参数:


git subtree pull -P &lt;子树名&gt; &lt;子仓库名&gt; &lt;分支&gt; --squash

git subtree push 推送子树更新到子仓库


git subtree push -P &lt;子树名&gt; &lt;子仓库名&gt; &lt;分支&gt;