Forking a Repository

某些场景下,我们需要基于某个仓库的 tagbranch 或者 commit 来进行二次开发。

例如,我们打算基于 CyanogenMod/android_packages_apps_Launcher3 进行二次功能开发,一般情况下我们需要 fork 一份源码到自己的 Github 上进行开发(当然也有其他途径),并且在 CM 更新 Launcher3 后我们还能通过 rebase 或 merge 更新 CM 的代码。

又如,直接在「Source」上 checkout 一条新的分支进行开发,并且如果 master 上有做什么更新,也可以通过 rebase 或 merge 同步更新。

比较

熟悉 Git 的朋友们,日常中对于 Fork 和 Branch 也不会陌生。稍有留意的,就能明白这两者之间不同方面的优劣。

Fork

使用 fork 方式进行「分叉」,优点有:

  • 能独立于「Source」进行开发
  • 「Source」仓库的更新、优化我们能够比较方便地同步更新

缺点在于:

  • 同步「Source」的代码时相对 branch 会比较麻烦,需要 fetch「Source」或者 add 一个 new remote 后再 merge
  • 更多的仓库

Branch

而使用 branch 进行「分叉」,优点在于:

  • 能直接对 source branch 进行 rebase 或 merge

而缺点在于:

  • (可能)会导致仓库存在过多的 branches 或 tags
  • 更大的仓库

选择

「Fork」和「Branch」的选择,个人更倾向于「Fork」。虽然「Branch」在更新的时候会更快捷,但是「Fork」避免了一堆 Branches 和 Tags,相比之下看着更舒服点(略鸡肋的理由)。

另一个更重要的理由是,考虑以下场景:

我们需要基于 AOSP 6.0 进行不同的产品开发,而不同产品间的 APPs 以及系统 UI 不同。另有一个团队根据 Google 的修改维护这个 AOSP 6.0 的镜像,该镜像只会更新 framework 或者 firmware 等同享的内容(也可以当作该镜像只有除 APPs 及 UI 相关外的代码)。

当我们有新的产品需要开发时,只需要 frok 一份最新的 master 镜像进行开发即可,而且最新的镜像也不会有 UI 相关的 commit。这样做则避免了在 AOSP 的 commit log 上看到负责 APPs 开发的人员的 commit,而作为 APPs 开发的人员也可以忽略底层开发人员的 commit(题外话:偶尔也需要找底层的锅_(:зゝ∠)_)。

这样的情况下当然是用「Fork」更好,不是吗?

Forking a Repository

用 Github 的同学,请注意 Github 不支持 fork 自己的仓库(所有权属于自己)且需自己手动同步。而 Bitbucket 则既可以 fork 别人的仓库,又可以 fork 自己的仓库,并且在无冲突的情况下能直接同步 source 的代码。

Forking

如果不借助 Github、Bitbucket 等平台的 fork 功能,如何 fork 仓库?

主要思路如下:

  1. Clone source 仓库,git clone https://github.com/whoever/whatever.git
  2. 选择基点(tagbranch or commit)并以此作为 master,git checkout taggit branch -d mastergit checkout -b master
  3. 修改 remote URLs 为新仓库的 URLs,git remote set-url origin https://github.com/whoever/new-whatever.git

或者

  1. 初始化本地仓库,git init
  2. 添加 source 仓库,git remote add source https://github.com/whoever/whatever.git
  3. Fetch source 仓库的代码,git fetch sourcegit fetch source branch_name
  4. 合并特定的 commit,git merge commit

Syncing

如果需要同步 source 仓库的更新,有两种方法:

添加 Remote

  1. 添加 source remote,命名为 “source”: git remote add source https://github.com/whoever/whatever.git
  2. Fetch source remote: git fetch source
  3. 确保你的仓库在 master 分支: git checkout master
  4. 合并 source/master 上的 commit: git merge source/master,或者 git merge commit 其中 commit 是 source 上的某一个 commit hash

不添加 Remote

  1. git fetch https://github.com/whoever/whatever.git 或者 git fetch https://github.com/whoever/whatever.git branch_name
  2. git merge commit,如果是最后一个 commit,可以直接 git merge FETCH_HEAD

相对来说,更建议添加一个 Remote,避免每次都要输入 URLs 以及 git Counting objects 过多。

总结

无论「Fork」或者「Branch」,本质上是为了提高我们的效率,不至于被各种信息分离关注点。如果其中一种方式导致工作量增加,不就是本末倒置?所以在使用「分叉」之前,请先考虑自己的场景该使用哪种方式。

References