This is an adapted mail for Mercurial mailing list, which is good to have as a blog post for reference.
For those of you who was born in Github era, Mercurial is an alternative version control system with transparent, pythonic internals. Because all my projects are escaping to Github I took a chance to reiterate over my knowledge of HG and see what I missed over the years of using it. This is just one idea.
This year, Mercurial introduced new ChangesetEvolution concept, which allows to safely mess with repository history. I decided to take a look and started with 'hg fold' command. Quite soon I got into a usual state of missed RTFM evening (you know the evening when you have a plenty of time to read a book with a cup of coffee). I couldn't understand what happened, but I clearly knew it is not something I want, so I wanted to get my repo back into the initial state. There are a lot of commands like 'rollback', 'revert', 'update -C', 'backout', 'strip' to revert the state after some command, but the real problem is to choose the right one. So I thought that it is something that is missing.
In Mercurial (and in other version control systems as well) - there is no concept of "operational transaction". In databases no matter what you do, if transaction is not committed, the state is reverted. These are called atomic transactions. Before Subversion there was CVS with non-atomic commits - if there was an error with some file (merge or something) - you got half of files committed and half not. Awful, right? After SVN all commits are atomic - if something is wrong, nothing is committed. Atomicity is important for user operations too. If something goes wrong - I want to get back from where I started. In Mercurial it works by making a backup copy of your repo. I guess for Git it's the same.
So, no obvious command to revert the last operation, no atomicity on operation level. This makes me feel unsafe and unsure about what can I do in my clone if I am too lazy to make a copy. And I thought that the next step in Mercurial evolution would be going from "user command" to "user operation" concept.
"user command" is a command like `hg inc` that users type in command line. It can affect the state of repository or not.
"user operation" - is a command or commands that change the state of repository. The "user operation" has a property of being "revertible" or "not". Granularity of changes to repository (how many commands is one operation) is decided using the high level user level goal to undo and redo these operations. For example 'hg fold' is an command that can be undone. It is a separate "user operation" and an entry in "undo history".
"user command" that modifies state may have "reverse command" that brings the changes back to the initial state. But maintaining this on command level is too fragile and hard to remember "commit/rollback". "user operation" may not have a "reverse command" - it may just be reverted without dedicated reverse command (like when you replace clone with your backup copy). And for that you need "undo history".
"undo history" is a stack of "user operations". These can be revertible or not - it depends on the logic. And it is not a commit log - it is operations log. The direct analogy is GIMP undo history dialog.
Now that the concept of the feature wanted is clear, some blueprints for the starter.
From the usability POV, a mercurial operations history dialog is a list, where each entry contains:
- operation name
- if it can be undone
- if not, state the reason
the reason is necessary to understand either:
1. current condition of repository
- what should be adjusted to enable undo
- why adjustment can not be automated
2. what should be written in hg itself to make it possible
- pointer to dev docs and status page
Summary:
* user command ('hg inc', 'hg ci', ...)
* user operation (hg command that changes state)
* undo history (stack of latest user operations)
* undo history items are frozen if reverting is impossible
* undo history is local
* state explanation between operations
Links:
https://www.google.by/search?q=undo+pattern - command and memento patterns can help
https://bitbucket.org/hstuart/hg-multiundo - some work on the topic was done by Henrik Stuart
Summary:
* user command ('hg inc', 'hg ci', ...)
* user operation (hg command that changes state)
* undo history (stack of latest user operations)
* undo history items are frozen if reverting is impossible
* undo history is local
* state explanation between operations
Links:
https://www.google.by/search?q=undo+pattern - command and memento patterns can help
https://bitbucket.org/hstuart/hg-multiundo - some work on the topic was done by Henrik Stuart
The final test:
hg undo
hg redo
hg undo --list
If you have what to say, but are not subscribed to continue thread in official mercurial@selenic.com mailing list, then I guess it's safe to leave comments here.
No comments:
Post a Comment