某些场景下,我们需要基于某个仓库的 tag
、branch
或者 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 仓库?
主要思路如下:
- Clone source 仓库,
git clone https://github.com/whoever/whatever.git
- 选择基点(
tag
,branch
orcommit
)并以此作为 master,git checkout tag
,git branch -d master
,git checkout -b master
- 修改 remote URLs 为新仓库的 URLs,
git remote set-url origin https://github.com/whoever/new-whatever.git
或者
- 初始化本地仓库,
git init
- 添加 source 仓库,
git remote add source https://github.com/whoever/whatever.git
- Fetch source 仓库的代码,
git fetch source
或git fetch source branch_name
- 合并特定的 commit,
git merge commit
Syncing
如果需要同步 source 仓库的更新,有两种方法:
添加 Remote
- 添加 source remote,命名为 “source”:
git remote add source https://github.com/whoever/whatever.git
- Fetch source remote:
git fetch source
- 确保你的仓库在 master 分支:
git checkout master
- 合并 source/master 上的 commit:
git merge source/master
,或者git merge commit
其中 commit 是 source 上的某一个 commit hash
不添加 Remote
git fetch https://github.com/whoever/whatever.git
或者git fetch https://github.com/whoever/whatever.git branch_name
git merge commit
,如果是最后一个 commit,可以直接git merge FETCH_HEAD
相对来说,更建议添加一个 Remote,避免每次都要输入 URLs 以及 git Counting objects
过多。
总结
无论「Fork」或者「Branch」,本质上是为了提高我们的效率,不至于被各种信息分离关注点。如果其中一种方式导致工作量增加,不就是本末倒置?所以在使用「分叉」之前,请先考虑自己的场景该使用哪种方式。