从一个 GitHub 问题中学到的

最近在使用 github 时碰到一些小问题,在这时记录一下。

一直对函数式编程比较好奇,以前也看过The Little Schemer,但是书中的代码并不适合
尝试,因此虽然看了一遍,对代码却并没有什么感觉。后来了解到了 clojure 这门语言,
就在图书馆借了一本Living Clojure来学习,书最后有一些稍微完整一些的练习题目(就
是每个题目是一个单独的工程),就拿来练习。在 fork 了作者提到的 repo 后就先做了一
个 alphabet-cipher,因为使用 git 经验太少,所以直接就在 master 分支上进行修改,
后来想要在作者的原 repo 里加入自己的解法链接的时候遇到了一些麻烦。

作者原 repo: wonderland-clojure-katas
我的 solution: wonderland-clojure-katas

遇到的问题

我首先想到的是提一个 pull request(PR),但细想不能把答案加到原 repo 啊(作者
肯定不想让别人直接看到答案的),应该是只修改 README 文件,把我的解法的链接加
进去。于是就新建一个 branch,在新 branch 中撤消修改,只把 master 分支的链接
加到 README 中。

1
2
3
4
5
$ git branch pr
$ git checkout pr
$ git reset HEAD~5
$ git reset --hard
$ git push

这时只要在 README 文件中加入链接,然后再提 PR 就可以了。可是因为马虎把链接加
到了master 分支的 README 文件里去了。当然这时只要切换到 pr 分支,再把链接加
一遍就行了。但为了多学习一下 git 的用法,就想知道能不能把特定的 commit 合并
到分支。

git 合并特定 commit

搜索了一下貌似 git cherry-pick 可以满足需要,于是根据一篇博客[1]中的描述进行了
操作。首先找到所需 commit 的 hash 值(可通过 git log 查看),然后切换到要合并
到的分支,最后使用 cherry-pick 合并进去就好了。

1
2
3
4
$ git checkout master
$ git log
$ git checkout pr
$ git cherry-pick dcc5f339ddd8467946cea5fd3eed4d2001290b85

此时查看文件,对应修改应该已经有了。

随后到网页端准备提 PR,但是发现原作者的 repo 已经有了新的提交,这时提不知道
会不会产生冲突,安全起见还是先把 repo 与原作者的进行同步。然后发现我不会同
步。。。

与原作者的 repo 同步

原来以为 github 上的 repo 同步也和本地与远程同步一样是用 pushpull,但是没
有在网页上找到相关的操作,搜索之后发现了这篇博客[2],好像这个同步也是通过 PR 来实现的。

我现在的理解是,我自己根据这个变更提一个 pull request,然后再由 repo 的所有者
(恰好也是我)接受进而合并。但是这个变更不是我做的,难道我可以随意帮别人提 PR?

还有另一篇博客[3]提到了另一种方法,即通过将本地的 upstream 设置为原作者的 repo
地址,在本地进行合并操作,之后再 push 到自己的 github repo 上。感觉这种方法思路
明确,比博客[2]中提到的方法好。不过我在已经用博客[2]中的方法同步后才看到博客[3]
的,就没有再试了。

不管怎么说,现在已经同步好了。然后也可以提 PR 了,就等着原作者接受了。

upstream 和 origin 的区别

那么 upstream 和 origin 究竟有什么区别呢?这篇文章[4]解释得比较清楚,这里有篇译
文[5]。

正如文章[5]中开篇提到的

Fork,本身并不是git工具中的一个命令,也不是对git的扩展,它是在GitHub上的
概念,是另一种clone方式——在服务器端的clone。而我们通常意义上的clone,是
将远程repo 复制一份到本地。

这对理解这两个概念的区别十分重要。这里有三个角色,对应到我的例子中就是
“原作者的 repo”、“我 fork 原作者的 repo”和“我本地的 repo”,对“我本地的 repo”来说

  • upstream -> 原作者的 repo (原作者在 github 上的 repo)
  • origin -> 我 fork 原作者的 repo (也就是我在 github 上的 repo)

upstream 和 origin 之间的关系应该是 origin 是 clone upstream 产生的。它们之间的
同步应该是用 PR,而它们与本地 repo 的同步是通过 push 和 pull 完成的。

学到的其他 git 命令

git clean 删除 untracked 的文件。

References

  1. Git合并特定commits 到另一个分支
  2. 如何在github上fork一个项目来贡献代码以及同步原作者的修改
  3. Github上fork之后如何保持同步(Windows环境)
  4. github中origin和upstream的区别
  5. github中origin和upstream的区别(转)
  6. [译]git clean
0%