Skip to content

Instantly share code, notes, and snippets.

@leemars
Last active December 28, 2015 15:49
Show Gist options
  • Save leemars/7524377 to your computer and use it in GitHub Desktop.
Save leemars/7524377 to your computer and use it in GitHub Desktop.

From: https://groups.google.com/forum/m/#!msg/codereview-discuss/ilUffSph68I/NCldEt2Ii-4J

Rietveld 项目的论坛里面, Robbie Iannucci 提出想把项目仓库从 hg 迁移到 git, 后面有人提问为什么, Robbie Iannucci 写了一封信给出了一些解释说明, 而 Martin Geisler 作为 hg 的一名开发者, 回复了一封信, 对 Robbie Iannucci 信中 hg 的一些理解进行了解释, 之后 Robbie Iannucci 又回复了一封信进行讨论. 下面是我节选的一些片段, 从一个 git 使用者的角度做的一些点评.

Robbie Iannucci 的信

Quote: Git's model of commits, content, and history are spot on though, IMHO.

Git 的模型的确相当直观, 同意.

Quote: In git, the workflow is such that there is never a penalty for committing your work, even if your work is incomplete (think of it like hitting 'save' on a document, but without intending to email it out right away). Git also affords very strong tools for taking your group of messy commits, and then presenting them as one or two polished (aka reviewable) commit(s) later when you actually want to send them. Once commits are pushed to the main repo, they're immutable (just like in hg), but until that time, git gives you tools to manipulate the commits themselves.

同意这段. Git 提供了充分的支持, 让你在本地可以先随意提交, 之后再整理成少量几个干净漂亮的 commit, 然后再提交到远端. 这个实在是 awesome.

Quote: The interest in git is best summarized by the simplicity of the underlying model, as well as the host of powerful built-in tools to manipulate it.

继续同意, Git 的模型是赞.

Quote: As another example of this (besides what I mentioned in the previous section), if I make a commit that the reviewer says is too complex, git gives me tools to split that commit in half, thirds, or whatever, even if the commit reflected changes all inside the same file, even changes on the same line. When I'm happy with the new arrangement, I can commit (save) all my work to disk as immutable objects, and then re-publish it as multiple reviews. If I decide that I made a mistake, I can easily rewind back to the original commit.

有追求的人都会需要这种整理 commit 的能力, 这也是我觉得 svn 不能胜任我的需求的原因.

Martin Geisler 的回复

Quote: This is somewhat ironic, since Mercurial has a simpler way to deal with remote and local branches than Git. That is to say, Mercurial has no concept of a remote tracking branch.

In Mercurial, commits live in a repository. The commits you have brought into the repository (using, say, 'hg pull') are present when you run 'hg log' and other commands. That is: all commits in the repository are local. Commands only operate on the local commits -- it's a DVCS after all. What 'hg incoming' does is simply a simulated 'hg pull' followed by 'hg log' on the data fetched.

Git works slightly differently here. After you add a remote and fetch the commits from the remote, the commits you fetched are really all in one big commit graph. However, it doesn't "feel" that way since you still need to checkout the one of the remote branches. After the checkout, your "foo" branch will point to the same commit as "origin/foo" and you can begin working, which will advance your "foo" branch. You will of course not see new commits made on the remote "foo" branch until they're published somewhere and you fetch them.

我不太同意他这一段话. 首先, hg 直接把 commit 拉进 local, 其实对 local 是一个很严重的污染, 会影响到你 local 上的工作, 像 git 那样区分好 local 和 remote 是很有好处的, 你可以在同步跟踪 remote 的修改, 但不会对 local 造成任何影响.

Quote: A commit in Mercurial is present in one or more repositories. You only operate on the commits present in your (local) repository. Commits in other repositories can be pulled into your local repository. The local revision numbers play no role here -- btw, they're just an (arbitrary) ordering of the commits in your local repository. No magic there.

Branches in Mercurial come in several flavors and I'll agree that the Git model is simpler here. In Mercurial you have no built-in garbage collection and therefore no "need" for branch pointers to keep commits alive. The changeset graph is in some sense more "robust" in that it's just there and doesn't change on its own initiative. This also means that you can checkout any commit (using its changeset hash or a tag name, for example) without being in a "detached head" state -- having a head checked out is no different from having a non-head checked out.

Now, people have of course wanted more structure on the DAG, and this is where "named branches" were invented. These are the branches you get when you use "hg branch". The "named" part comes from the fact that you now embed a branch name into the commits on that branch. Commits without such a name inside them are considered part of the "default" branch, other commits are part of whatever branch name they have inside them. Because a new commit by default includes the branch name of the working copy parent revision, the branches tend to be connected subsets of the DAG and thus behave like you would expect branches to behave.

Later, it became apparent that immutable branch names might be too rigid and (with inspiration from Git), bookmarks were born. Initially a standard extension (from 2008), they've been part of the core since 2011. Bookmarks are simply pointers to commits which live outside the commits. This makes them mutable. Like Git branches, an active bookmark will advance when you make commits. Bookmarks have been tuned and improved over the years, so make sure you use Mercurial 2.8 when trying them.

作者提到 detached head, 我认为这个对使用上没有造成什么影响, 没什么问题. 倒是这一段可以看到让人崩溃的 hg 的分支模型, heads/branches/named-branches/bookmarks… 我不太有兴趣去具体研究这些都到底是什么了. git 里面就一个 branch 的概念. named-branches 的想法是不错, 不过在 git 里面也可以通过另一些方法来达到类似的目的, 在 git 里面算是忍了吧, 避免在 git 模型上搞这么复杂.

Quote: Sometimes the functionality is useful for a lot of users, but a little advanced like in the case of rebase or MQ.

呃啊.. rebase 算个毛线的高级功能.. 不用 rebase 用毛的 git..

Quote: This was never really different in Mercurial -- you've been able to edit, squash, and reorder commits with since sometime in 2006 using the MQ extension. I will not claim that the MQ interface is the best, far from it, but the functionality has been there for a very long time. Modern Mercurial ships with a standard histedit extension which works very much like "git rebase -i" and you can use "hg commit --amend" without any extensions.

Unlike Git, Mercurial has a (still work in progress) framework for letting you manipulate published commits. The is called changeset evolution and allows you to do things like pushing a commit after you amend it. We're not just talking about forcing the push, we're talking about adding meta data (so-called "obsolete markers") which mark the new version of your commit as a successor to the old version. If others have pulled the old version already, this meta data will allow their Mercurial to hide the old version when they pull the new, amended version. If they have based work on the old version, this work can be automatically rebased on top of the new version. If you're interested, there is a video about the concept here: https://air.mozilla.org/changesets-evolution-with-mercurial/

git 从开始到现在, 概念应该几乎没变过. 相比之下 hg 里面的概念就一直各种变化的感觉... 你看同样的功能, 一直各种更换不同的实现, 而 git 就一直是 rebase, 所做的事情也没有太多变化.

不过后面提到的 hg 的这个 changeset evolution 的东西倒是有点..新奇的感觉. 但如果按照 git 的理念, 这种事情是不允许的: commit 是不可变的. 这带来的是完整性和安全性的保证. 在 git 里面能做的事情应该就是靠 notes 给 commit 附加额外的数据. 哪个更好? 我个人倾向于 git 吧.

Quote: In Mercurial I would 1) mark the work-in-progress commit as "secret" (with "hg phase -fs") to prevent them from being pushed by default and 2) keep using "hg commit --amend" on each branch head to incorporate feedback from the review.

又有 secret 什么的新概念出现了... 好烦躁...

Quote: 后面剩下的我就不Quote了

作者介绍了一下 hg 里面的 histedit 命令, 看起来应该是基本和 git 的 rebase 差不多了. hg 在经过这么多年以后, 终于弄了一个 builtin 的和 rebase 差不多的东西...

Robbie Iannucci 的回复

Quote: This is actually one feature of hg that I really like and I wish that git had. There's no way to really record the history of branches (reflogs fill in for some very light-weight purposes here, but they're not nearly strong enough, and they don't track well in a central repo that multiple developers are pushing to). This is especially apparent when doing fast-forward merges, since the commits just become inlined into the history of the ref, and the fact that they were a group of commits on a branch is lost. Thus, to undo them, you need to remember where the feature branch started :/. Yes, you could do merge commits, but having a linear history is just so pretty :P.

恩, 你看, named-branches.. 大家其实都是有这样的需求的. 特别是 Fast-Forward 以后容易看不出来哪些原来是属于一个 branch 的. 我个人也偏爱 linear history, 不喜欢 merge commit.

Quote: So the main takeaway for me is: HG implements tons of the features I thought were missing by way of first-party extensions. I didn't realize that these extensions in hg-world were supported as well as they apparently are. You can imagine my trepidation about installing an extension to do something as essentially non-mess-up-able as rebase when I think that the extension is under the implied open-source "Yeah it totally worked at some point and then I published it in a wiki. Good luck, have fun!" support 'contract' :). Multiply that by the ~4-5 extensions I would have had to enable to get on-par with git's out of the box state (minus the crazy CLI :)), and I decided it would just be safer to stick to what hg 'comes' with.

同意. hg 出厂时默认打开的 extension 太少了, 而 extension 又多而混乱, 以至于你需要花时间精力去研究哪些能用, 哪些好用. git 出厂带的东西基本都是靠谱的东西(作者下面吐槽 git-svn 我就不说了, 那个是有点 buggy 的感觉), 不需要自己花太多精力在选择上. 好的 default 很重要啊..

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment