这几年入坑输入法,自己根据一堆开源项目拼凑出了基于小鹤双拼,使用小鹤音形作为辅助码的方案,使用了也快2年了,效率非常高。项目是基于雾凇拼音二次开发的,我自己的fork在小鹤流,最近同步了上游的更新之后发现我的方案无法使用了,因为几个月才同步一次,中间上游的提交非常多,所以比较难以定位是什么原因。本文记录一下排查的过程以及面对这种问题的处理方式。
为什么要同步?
从我用输入法多年的经验来看,一个大词库是必要的。也曾经尝试过小鹤音形方案,这是一个可以支持二码上屏的方案,也就是打有些字是不需要按空格的,这样有的需要按空格而有的不需要就会很大程度上打乱打字的节奏,所以尝试了一段时间就放弃了,但学到了这个音形方案之后我在想为什么不能把它作为辅助码呢?如果有一个大词库,那么90%的情况下是不需要使用辅助码的,和直接用双拼是一样的感受,但当真的需要快速定目标字词时可以直接输入形码定字,省去了翻页的烦恼。
所以我拼凑出来的方案就是这个思路,而雾凇拼音最厉害的就是它的庞大的、优质的、定期维护的词库,所以就需要定期同步,前段时间比较忙,就没有怎么同步,已经5个月了。等最近同步了之后就发现已经无法使用了。
git bisect
git bisect
这个命令还真是没用过,可以简单理解是用二分查找的方式来快速定位有问题的提交。
git bisect good sha1
用来指定功能正常的提交,git bisect bad sha2
来指定功能不正常的提交,举例来说如果中间有128次提交,那么最多7次就能定位到有问题的那次提交了,但是这个方案并不适用于我这个情况。
看一下我的项目的提交情况。
这是用github上的Sync Fork功能实现的。那么使用git bisect会有什么问题呢?
它会在good到bad之间的所有commit之间跳跃,但是一旦跳到了上游作者提交的commit上,就会不包含我的改动,我也就没办法测试自己的功能了。所以为了定位到具体的原因,我就先切到了最近一次合并之前的合并,然后逐个merge在那之后的上游更新(当然也是使用二分法),终于找到了引发错误的提交。
来看下原因
可以看到,作者把原来放在rime.lua
里的内容给拆了,拆到了lua/*.lua
里,拆成了很多个文件,我没有细研究这个做法,但带来的问题就是原来在xxx.scheme.yaml
里的类似lua_translator@xxx
或lua_filter@xxx
的写法都要改成lua_translator@*xxx
和lua_filter@*xxx
,也许*
是个通配符,是为了解决这一层多出来的目录,而我自己的方案是没有同步这个改动的,事实证明确实是这个问题,我把*
加上去之后就解决了。
git merge 和 git rebase的区别
从这个例子中就可以深刻理解git merge和git rebase的区别了。git merge是把你的提交attache到上游的提交上,然后生成一个新的commit,只有在这个commit内才有你自己的提交,就像这样
只有我框出来的这部分是包含了我自己的提交的,所以用git bisect
跳转到其他任意的commit都无法测试我自己的改动。但这样的好处就是非常清晰(或许吧),至少可以明确看到每次合并的动作。但如果用git rebase
,来看下效果
可以看到,提交记录是一条直线,但这是有非常大的代价的,要一直解决冲突,因为这里有一个最重要的问题:到底是谁要同步谁的更新?
git merge做的是,把对方的更新同步到我这里,由于项目基本上都是对方的改动,而我改的东西并不影响上游,所以合并是不会有冲突的,同一个文件我这上次也是他改的,现在合并的新提交也是他改的。
但git rebase不同,是要先把我的base切换到他的主干,然后把我的改动merge到上游的顶部,原则上不会产生一次commit,但问题在于,上游的文件是在更新,而我的分支里比如是他4月份的提交,我git rebase就是要把他去年4月份提交的文件合并到他今年1月份提交的新文件上,这是什么道理呢?所以就需要一直解决冲突,一直选择git checkout --theirs .
git add .
git commit -m 'use theirs'
类似这种,然后git rebase --continue
。
总结
回到这个问题,git merge 和 git rebase并没有谁比谁更好,起码在这个场景,虽然花了我更多的时间去debug,但我还是认为用merge是好于rebase的。而rebase适合什么场景呢?适合的是多人协作的情况下,每个人都有自己的开发分支,但有一条主线分支,每个人提交代码时都是git rebase origin/main
这样,因为每个人的分支都可以认为是比origin/master
分支要更新的,所以除非两个人同时修改了同一个文件的情况,否则是不需要处理冲突的。