Mercurial workflow, Bitbucket, draft commits, and pytave
Colin Macdonald wrote:
> 10 min change, 15 mins histedit (read about phase), 20 mins to force
> changes (strip through webgui settings). Still not a huge fan of this
> workflow (hg part seems fine, but bitbucket not designed to deal with
> bookmarks IMHO).
I agree that the Bitbucket UI is kind of clunky when it comes to the
workflow that we are trying to use. For others on the sidelines, we have
a central repository and fork repositories, and using pull requests and
a combination of merges and rebases to commit new changes to the main
Colin, I hope you'll agree that, once you know the name, the `hg
histedit -i` command is feature-compatible with `git rebase -i`.
My local workflow consists entirely of `hg commit -i` and `hg histedit
-i` to squash, split, reorder, or reword commits before pushing.
I would suggest that you go to the Settings tab of your Bitbucket
repository and tick the box that says "This is a non-publishing
repository". I would recommend that anyone following the fork-and-PR
development model do this by default actually.
What this means is when you push changes to your fork, they will remain
in the draft phase. Bitbucket will annotate draft commits with a little
"D" icon. This gives you the freedom to rebase and rewrite as much as
you want without the default protection that Mercurial provides by
moving commits into the public phase when they are pushed. This *should*
signal to others not to base their work on your draft commits, but there
is nothing else in the toolbox to help with that.
Stripping changesets on Bitbucket:
Yes, in git when you rebase and force push a branch, the remote
repository automatically deletes all commits that were in the branch but
are not in the new history you created. In Mercurial, a branch can have
multiple heads so there is no automatic deletion of "obsolete" commits.
(cf. Changeset Evolution below).
If you invoke the strip command or Bitbucket strip tool on the oldest
revision in a series that you want to strip, it will strip all
descendants as well, and the Bitbucket UI should show the list of all
You can also use revsets, which is a very featureful query language, to
pick which revisions to strip. For example to strip all draft commits,
you could simply put "draft()" into the Bitbucket strip tool and it
If you want to strip all commits that are ancestors of a certain
bookmark but not another bookmark, you could plug in something like
"my_fix % @".
See https://www.mercurial-scm.org/wiki/ChangesetEvolution or ask Jordi
for more details. This is a plugin that enables new commands and new
features to make it easier to deal with rewriting or reordering history
with a team of developers, marking commits as obsolete and hiding them
from history automatically, etc.
My biggest problem with the Bitbucket UI is that it doesn't treat
bookmarks the same as branches. For example, as you found, if you open a
PR based on a bookmark, and then push subsequent commits on the same
head but without advancing the bookmark, they are still included in the
PR. It is impossible to file a PR within the same branch within a single
repo, even if it is to merge one head or bookmark into another.
And as you and Abhinav have found, when you do rewrite the commits that
a bookmark is pointing to, some of the PR content is lost or sometimes
it's easier to open a new PR. I haven't followed all the troubles you've
had with that.
It would be nicer to have a more user-friendly way to choose series of
commits to strip, since multiple heads is something anyone will have to
deal with if they rebase changes before a merge. Jordi tells me there is
a way to request Bitbucket to handle obsolescence markers so you could
use the evolve plugin to more effectively rewrite branches, but I have
not tried this.