What to do instead of squashing commits in Mercurial
Pregunta
I've got my IDE set to commit locally every time I save anything. I'd ideally like to keep an uncensored record of my idiot fumblings for the rare occasions they may be useful. But most of the time it makes my history way to detailed.
I'd like to know a good strategy to keep that history but be able to ignore it most of the time. My IDE is running my own script every time I save, so I have control over that.
I'm pretty new to Mercurial, so a basic answer might be all I need here. But what are all the steps I should do when committing, merging, and reporting to be able to mostly ignore these automatic commits, but without actually squashing them? Or am I better off giving up and just squashing?
Edit - My point here is that if Mercurial wants to keep all your history (which I agree with), it should let you filter that history to avoid seeing the stuff you might be tempted to squash. I would prefer not to squash, I'm just asking for help in a strategy to (in regular usage, though not quite always) make it look as much as possible like I did squash my history.
Solución
You want to keep a detailed history in your repo, but you want to have (and be able to export) an idealized history that only contains "reasonable" revsets, right? I can sympathize.
Solution 1: Use tags to mark interesting points in the history, and learn to ignore all the messy bits between them.
Solution 2: Use two branches and merge. Do your development in branch default
, and keep a parallel branch release
. (You could call it clean
, but in effect you are managing releases). Whenever default
is in a stable state that you want to checkpoint, switch to branch release
and merge into it the current state of default
-- in batches, if you wish. If you never commit anything directly to release
, there will never be a merge conflict.
(original branch) --o--o--o--o--o--o--o (default)
\ \ \
r ... ... --r--------r (release)
Result: You can update to any revision of release
and expect a functioning state. You can run hg log -r release
and you will only see the chosen checkpoints. You can examine the full log to see how everything happened. Drawbacks: Because the release
branch depends on default
, you can't push it to another repo without bringing default
with it. Also hg glog -r release
will look weird because of the repeated merges.
Solution 3: Use named branches as above, but use the rebase
extension instead of merging. It has an option to copy, rather than move outright, the rebased changesets; and it has an option --collapse
that will convert a set of revisions into a single one. Whenever you have a set of revisions r1:tip
you want to finalize, copy them from default
to release
as follows:
hg rebase --source r1 --dest release --keep --collapse
This pushes ONE revision at the head of release
that is equivalent to the entire changeset from r1 to the head of default
. The --keep
option makes it a copy, not a destructive rewrite. The advantage is that the release
branch looks just as you wanted: nice and clean, and you can push it without dragging the default branch with it. The disadvantage is that you cannot relate its stages to the revisions in default
, so I'd recommend method 2 unless you really have to hide the intermediate revisions. (Also: it's not as easy to squash your history in multiple batches, since rebase will move/copy all descendants of the "source" revision.)
All of these require you to do some extra work. This is inevitable, since mercurial has no way of knowing which revsets you'd like to squash.
Otros consejos
it should let you filter that history to avoid seeing the stuff you might be tempted to squash
Mercurial has the tools for this. If you just don't want see (in hg log
, I suppose) - filter these changesets with revsets:
hg log -r "not desc('autosave')"
Or if you use TortoiseHg, just go View -> Filter Toolbar, and type in "not desc('autosave')" in the toolbar. Voila, your autosave entries are hidden from the main list.
If you actually do want to keep all the tiny changes from every Ctrl-S
in the repo history and only have log
show the subset of the important ones, you could always tag
the "important" changesets and then alias log
to log -r tagged()
. Or you could use the same principle with some other revset descriptor, such as including the text 'autosave' in the auto-committed messages and using log -r keyword(autosave)
, which would show you all non-autosaved commits.
To accomplish your goal, at least as I'd approach it, I'd use the mq
extension and auto-commit the patch queue repository on every save. Then when you've finished your "idiot fumblings" you can hg qfinish
the patch as a single changeset that can be pushed. You should (as always!) keep the changes centered around a single concept or step (e.g. "fixing the save button"), but this will capture all the little steps it took to get you there.
You'd need to
hg qinit --mq
once to initialze the patch queue repo (fyi: stored at\.hg\patches\
)hg qnew fixing-the-save-btn
creates a patch
then every time you save in your IDE
hg qrefresh
to update the patchhg commit --mq
to make the small changeset in the patch queue repo
and when you are done
hg qfinish fixing-the-save-btn
converts the patch into a changeset to be pushed
This keeps your fumblings local to your repo complete with what was changed every time you saved, but only pushes a changeset when it is complete. You could also qpop
or qpush
to change which item you were working on.
If you were to try the squash method, you'd lose the fumbling history when you squashed the changesets down. Either that or you'd be stuck trying to migrate work to/from the 'real' repository, which, I can tell you from experience, you don't want to do. :)
I would suggest you to use branches. When you start a new feature, you create a new branch. You can commit as many and often as you like within that branch. When you are done, you merge the feature branch into your trunk. In this way, you basically separate the history into two categories: one in fine-grain (history in feature branches), and the other in coarse-grain (history in the trunk). You can easily look at either one of them using the command: hg log --branch <branch-name>
.