Git submodule & pull request ! 让我们啃下这块骨头!

实际上submodule 与 pull request,并没有什么直接关系。
比如一些团队采用code-review的方式进行协作,那么他们可能只用到pull request。
这里之所以放在一起,是因为很多场景用到了其中一个,就少不了另一个。
那么,什么情况下我们需要用到 submodule 和 pull request呢?!


假设我们的项目是用git来管理,这时我们需要添加一个第三方库的源码(以下简称lib),而这个lib,也是通过git来管理。
这种情况,一般有两种选择:

  1. 把lib的源码复制到我们的项目中,把它作为当前项目的源码进行管理。
  2. 通过git clone的方式,把lib整个放到我们的项目中(保留它自身的git信息),作为git的submodule。

这两种方式各有利弊,接下去就来好好分析一下。


方式1—只复制源码

第一种方式对于项目自身的管理来说,非常简单,没有引入额外的概念。
如果我们需要修改第三方库的功能,直接改就可以了。
但这种方式在以下几种情况,会显得非常糟糕:

  1. 更新lib的源码到最新版本
    如果我们已经在自己的项目仓库对这lib进行了很多修改。
    然后某一天,lib的官方github发布了许多非常有用的更新,那这时候的合并工作,就必须得借助第三方工具。

  2. 为lib贡献自己的代码
    当我们在使用lib时,发现了一个bug,并解决了。我们很难把修复这个bug的commit,告诉官方的github。
    有悖开源的目的。

所以接下去,本文将着重介绍方法2的操作方式!!!


方式2—submodule & pull request

方式1中遇到的问题,通过采用 submodule 就可以很好的解决,但是submodule本身特别绕。
所以本文将以我的博客仓库作为案例来分析。

背景介绍

博客主题是一个独立的模块,所以我有以下两个仓库,theme作为blog的submodule来管理。

  1. 我的博客仓库为 xtutu/blog.git
  2. 博客的主题仓库 xtutu/theme.git ===> fork from official/theme.git

这里可以看到xtutu/theme.git 是一个从官方仓库 fork出来的分支!
xtutu/blog.git储存在我们自己的git仓库里.(所以仓库的前缀是xtutu/)。

如果你对submodule、pull request 都有一定了解,可以直接看2.4的小结内容!!!
那里有我目前的操作流程!


submodule 部分

添加 submodule

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# clone blog仓库到本地
$ git clone xxxxxxx/blog.git
$ cd blog
# 在blog仓库下,执行下面这个语句,就会创建一个themes文件夹下,创建一个名为theme的submodule
$ git submodule add git@github.com:xtutu/theme.git themes/theme
# 看看blog当前的状态
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitmodules
new file: themes/theme
# 提交所有记录
$ git commit -am "add submodule theme"
$ git push # 执行完这句命令之后 submodule 就添加完成了

注意!!!
blog仓库中的 theme 文件夹下面的内容并没有提交到blog仓库,而是只提交了一个空的theme目录。
可以理解为:在blog.git中,只记录了submodule的状态,而不是实际内容!


从图中可以看到,blog仓库会记录submodule,对应在theme仓库中的commit id


对submodule进行git操作

1
2
# Administrator at USER-20160613SI in /f/blog on git:master o [14:18:52]
$ cd themes/theme

切换到theme下面之后,执行的各种git操作(包括cmmit、push等),都是只针对 xtutu/theme.git进行的。
这与普通的git操作并没有什么不同。

但需要这注意的是:对theme.git执行了修改操作之后,blog.git会察觉到theme文件夹的commit id发生了变化!
所以需要在blog仓库中,进行一次常规的commit,用于提交 submodule 状态修改!


同步一个包含submodule的仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git clone xxxxxxx/blog.git blog2
$ cd blog2
# 执行完上面这部分之后,虽然创建了themes/theme文件夹,但文件夹里并没有内容...
# 所以我们还需要以下操作:
# 第一次clone需要进行注册。
$ git submodule init
# 进行更新
$ git submodule update
### 到这为止,theme下面已经有了具体的内容!
---------------------------------------------
# 以上3条 git 命令,也可以用这句话代替
$ git clone xtutu/blog.git blog2 --recursive

注意!!!
默认clone下来的submodule不属于任何branch,处于游离状态,所以一定要记得执行下面的操作!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Administrator at USER-20160613SI in /f/blog2 on git:master o [14:45:37]
$ cd themes/theme
# Administrator at USER-20160613SI in /f/blog2/themes/theme on git:966eb5d o [14:52:23]
$ git branch
* (detached from 966eb5d)
master
# Administrator at USER-20160613SI in /f/blog2/themes/theme on git:966eb5d o [14:52:52]
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
# Administrator at USER-20160613SI in /f/blog2/themes/theme on git:master o [14:54:42]
$

当最后一条命令执行完毕之后,可以在命令行上看到。我们已经进入到了master分支!


小结

了解了上面这些内容之后,使用submodule应没什么太大问题了。
但是方式1提到的两个问题,还是没有涉及到!
接下去的内容就是来解决这两个问题。


pull request 部分

pull request 大致浏览

下面的内容,实际上和submodule已经没有任何关系。
pull request & update 等操作,都是针对于 xutut/theme.git 以及 official/theme.git (原始的官方仓库)。

  1. 进入xtutu/theme.git主页,如图所示
  2. 点击pull request

    从图中可以看到,当我们点击Create pull request之后,就会把自己仓库的修改提交到official/theme.git。
    准确的说:应该是发起一个提交的请求。最后由官方仓库的拥有者,觉得是否接受提交。

但是在这个页面会列出所有的修改,包括不是用于修复这个bug的commit!
所以如果我们只是想pull request一个(或者几个)修复bug的commit,而不是所有的commit。那该怎么做呢?!
我在搜索资料的时候大致看了下,用cherry-pick命令应该可以完成这一效果。不过这里不采用这种方式

pull request的正确姿势

在自己的仓库中,新建一个专门用于修复这个bug的branch。
当这个bug修复了之后,我们再通过网页,在这个branch上 pull request!
这样pull request里面的commit全都是为了修复这个bug而提交的。

当然了,不管最后官方仓库,要不要接受这个pull request。
我们自己使用的xtutu/theme.git master分支,都可以把这个修复bug的分支合并过来!(就是普通的merge操作,相信大家都已经很熟悉了)

注意事项
我们用于pull request的的分支,最好更新到official/theme.git的最新状态,并保持HEAD一致(git rebase & reset命令)!再进行提交!
下面就讲一讲如何从official/theme.git 拉取最新的状态。

同步官方仓库的最新修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 添加一个新的远程仓库
$ git remote add upstream https://github.com/...../official/theme.git
# Administrator at USER-20160613SI in /f/mygit/blog/themes/next on git:master o [16:07:26]
$ git remote -v
origin git@github.com:xtutu/theme.git (fetch)
origin git@github.com:xtutu/theme.git (push)
upstream https://github.com/xxxxxxx/theme.git (fetch)
upstream https://github.com/xxxxxxx/theme.git (push)
# 设置从upstream上拉取数据
$ git pull upstream master
# 推送到自己的 xutut/theme.git 仓库中
$ git push

这里做的就是普通的merge操作,不同之处在于是从一个新的remote上进行merge。

小结

我目前的做法是在xtutu/theme.git中,一直保存着2个分支。

  1. master:针对自己的需求,进行的所有修改,都会提交到这里
    比如在现有主题上,更改显示效果,添加自己的信息等。

  2. latest-from-upstream: 永远与官方的master保持一致!
    该分支主要用于创建 执行pull request操作的 分支,该分支要保持干净。
    注意:该分支不是为了创建pull reqeust,而是为了创建(执行pull request操作的)分支
    保持干净是为了:在pull request时,避免出现不相干的commit id。

当需要提交一个新的pull request时,可以执行以下操作。

1
2
3
4
5
6
7
8
9
10
$ git checkout latest-from-upstream
$ git pull upstream master # 同步官方仓库的更新
$ git checkout -b new-feature # 创建用于pull request的分支
# 然后进行各种修改。
$ git ......
# 推到xtutu/theme.git上
$ git push origin new-feature
# 最后通过网页,进行pull request操作。
# 如果我们自己也需要用这个修改,只需要从本地的master分支,merge这个new-feature分支。

最后的最后

写了这么多,大家在用的时候肯定还会遇到不少问题。不过了解了上面这些知识点,再去搜索下,应该可以比较容易的解决。
上面的操作流程,也只是我自己摸索出来的方式,如果有更好的建议,可以给我留言,大家一起讨论。

最后,附上一张截图:这是我采用这种方式,给hexo-theme-next提的一个pull request。
哈哈哈,成功merge!


转载本站文章请注明作者(xtutu)和出处 xtutu.me