38

hg advent init

 6 years ago
source link: https://www.tuicool.com/articles/hit/yqie6bq
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

This is the start of a daily series in the run-up to Christmas where I learn mercurial. This first post will be about what’s making me want to do this in the first place.

I tried briefly a while back, but I didn’t get very far. It’s annoying to feel useless using a tool that does approximately the same thing as another tool you know well. I’m hoping that some daily and deliberate practice will get me further!

I’ve been interested in mercurial from a distance for a year or so now. This is partly because it has cool features, and partly because I use git in an idiosyncratic way that I think could fit better with mercurial. I’ll start off talking about the features that initially made me interested. And I’ll write a companion post about how I use git which will hopefully help this make more sense.

Bear in mind I’ve basically never used mercurial, so there is probably a bit of wrongness in what I’m writing here. :-)

changeset evolution

I’m a pretty heavy user of git-rebase. When you rebase in git, there’s no connection between the rewritten commits, and the ones they’re replacing. Usually this is fine, but sometimes I’d like to be able to diff between the now-version and the then-version of a commit. Short of some reflog magic and making assumptions like you never change commit subjects, there’s no easy way to do this in git.

With changeset evolution in mercurial, changesets can point to their earlier versions. If you think of rebase-like operations as changing history, then evolution can give you the history of the history.

phases

In git, there’s this convention / recommendation that you don’t modify visible history. The precise meaning of visible depends on the circumstances, but in the popular centralized workflow this would be the main branch of the central repo.

There’s nothing that enforces this, so it’s a really easy mistake to make. Pushing an unrelated history up to the main branch of a project with lots of contributors breaks all of those contributors current PRs. They’ll have to rebase, and if everyone is lucky there won’t be conflicts.

To prevent this kind of thing, people often install update hooks. Or if using a hosted service like github, gitlab, or bitbucket, you can set an option to prevent force pushing. This saves you from modifying public history, and depending on the exact setup may also save you from accidentally pushing work in progress. But all this depends on the remote preventing you from doing damage. In mercurial, your local hg commands can stop you from making these mistakes.

In mercurial, changesets (commits) can be in one of three different phases :

  • secret
  • draft
  • public

Secret changesets can be modified but not shared. Draft changesets can be both modified and shared. Public changesets can be shared but not modified. Commands in mercurial enforce these restrictions: for example, hg push will prevent you from pushing a secret changeset, and hg commit --amend will prevent you from rewriting a public changeset.

unnamed branches

I spend a lot of time in detached head states. This might make more sense after I’ve written the “how I use git” post. From what I read, mercurial makes it a lot easier and safer to work with unnamed branches. You can do this in git—and I do!—but inevitably something gets a bit lost in the reflog. I want to see how mercurial handles that workflow.

absorb

This is the feature that pushed me over the edge and made me want to really try learning mercurial. There’s a really great post about it by Gregory Szorc. Like I said earlier, I rebase a lot. I also frequently have a few stacked changes in the works, or even out for review at the same time.

Doing little fixups from code review can be kind of annoying in that setup: you have to rebaseand shuffle things around. Autosquash helps a lot: you can commit with --fixup to say which commit you’re fixing. But it’s still kind of a pain.

Mercurials absorb supposedly makes all that pain go away! Here’s its synopsis

incorporate corrections into the stack of draft changesets

It sounds like exactly what I want! You just make changes to your working directory, and then run hg absorb . No committing required. It tries to find which changesets your working directory changes belong to, and then amends those changesets. And if, like me, you’re worrying about ambiguous changes that could belong to more than one changeset, well don’t! It leave those changes uncommitted in the working directory. And because it only works on draft changesets, it won’t accidentally stick changes in the middle of public history.

it’s written in python

This one’s a bit of a plus and minus. The fact that it’s in python rather than something like C results in perceptible startup delays. As a quick example, in a tmpfs on my laptop, it takes under 3ms to git init , and slightly under 90ms to hg init . Even hg --version takes around 70ms. This is solidly in the range where you can see the lag.

The mercurial folks have a project called oxidation to rewrite parts of it in rust. Initially the hg command will continue to be in python, and some operations will call into rust. But I think eventually they want to invert that so that hg is a rust binary that calls into python. This should help bring down lag on common operations.

But putting performance aside, python is really approachable. Far more approachable than C, and I’d say somewhat more approachable than bash. This means you can customise it in ways more advanced than aliases without resorting without resorting to bash or libgit2. For example, the absorb extension is about 1000 lines of pretty readable python ! If it turns out it doesn’t do exactly what I want, I think I could prod at it until it does.

so here’s to three-and-a-bit weeks of mercurial

I’m excited to try this experiment out. And the external commitment in the blog series makes me epsilon more likely to actually follow through! And who knows, maybe I’ll switch. I started using vim somewhat arbitrarily on 2009-01-01 as part of a learn-more-unixy-things resolution", and here I am almost 9 years later typing a blog post in vim. :-)

Oh and I’d love to hear your experience if you use mercurial as a git client. If I do switch, that’s where I’ll end up.


Recommend

  • 40
    • studygolang.com 6 years ago
    • Cache

    Go 中的 init 函数

    main 标识符是随处可见的,每一个 Go 程序都是从一个叫 main 的包中的 main 函数开始的,当 main 函数返回时,程序执行结束。 init 函数也扮演着特殊的角色,接下来我们将描述下 init 函数的属性并介绍下怎么使用它们。 init 函...

  • 69

    本文为译文,原文链接: https://spyhce.com/blog/understanding-new-and-init 本文的目的是讨论Python中 __new__ 和 __ini___ 的用法。 __new__ 和 _...

  • 36
    • studygolang.com 6 years ago
    • Cache

    init()函数

    init()函数优先于main()函数执行 每个源文件都可以包含一个init函数,这个init函数自动被go运行框架调用,执行的优先级最高。 让我们做一个代码来验证一下: ·目录结构: |-example2 |-initInfo...

  • 51
    • www.tuicool.com 6 years ago
    • Cache

    On the vagaries of init systems

    When I started working on Dinit I had only a fairly vague idea of the particulars of various other init systems, being familiar mainly with Sys V init and t...

  • 58
    • www.tuicool.com 6 years ago
    • Cache

    Init Is Bad and You Should Feel Bad

    func init() in Go is a weird beast. It’s the only function you can have multiples of in the same package (yup, that’s right… give it a try). It gets run when the package is imported . And you shou...

  • 43
    • www.tuicool.com 5 years ago
    • Cache

    Go中的init函数

    我们知道Go程序的入口是main函数,当main函数退出了,程序也就退出了。init函数在Go程序中也扮演着重要的角色。这篇文章将会介绍init函数的特性以及如何使用它们。 init函数的作用: 变量初始化...

  • 39

    Debian votes on init systems LWN.net needs you!Without subscribers, LWN would simply not exist. Please consider signin...

  • 36

    Join GitHub today GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.

  • 13
    • xaeroxe.github.io 5 years ago
    • Cache

    Init Struct Pattern

    Introduction Let’s talk initialization of complex structures in Rust. There’s a few popular ways to go about this, some of which include the pub fn new() convention and the builder pattern. In this...

  • 16
    • www.v2ex.com 4 years ago
    • Cache

    init 你们读[in it]还是 initial

    程序员 - @hikarikun1991 - 发现很多人都是读 in it 两个单词,但我的理解是这是 initial(初始化)这个单词的简写,所以我都是读 initial 的。是不是我自己的问题- -

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK