hg-git is confusing with implicit/anonymous branching

693 views
Skip to first unread message

mcc

unread,
Nov 6, 2011, 2:26:43 PM11/6/11
to hg-git
I keep running into situations like this:

I create a brand new git repository named "test" on github. I then do
the following:

hg clone git+ssh://g...@github.com:mcclure/test.git
echo "Rev one" > onefile.txt
hg add onefile.txt
hg commit -m "Rev one"
echo "Rev two" > onefile.txt
hg commit -m "Rev two"
hg push
hg up 0
echo "Rev three" > onefile.txt
hg commit -m "Rev three"
hg push

At this point an hg glog looks like:

@ 2[tip]:0 dc7ac7f5f29d 2011-11-06 10:06 -0800 mcc
<andrew....@gmail.com>
| Rev three
|
| o 1[default/master] ebc440693fae 2011-11-06 10:05 -0800 mcc
<andrew....@gmail.com>
|/ Rev two
|
o 0 d0b891f73085 2011-11-06 10:03 -0800 mcc
<andrew....@gmail.com>
Rev one

But! If I go and look at my github page-- I have only the two
revisions, 0 and 1. Only the magic branch holding the "master"
bookmark gets pushed to github when I hg push, and revision "2" is
ignored. This is as I understand because within git the contents of a
single branch can only contain an unbroken line of revisions, so hg-
git is pushing only the named branch flowing back in a straight line
from the "master" bookmark.

I use the mercurial anonymous-branch feature a lot in my workflow, so
I run into this problem a lot.

My intuition on ways to fix this all usually wind up creating huge
messes. I think: Okay, the master bookmark is what makes things
"visible" to github, and I want that on revision 2. I know, I will
just move the bookmark. I type (continuing from the commands above):

andis-macbook:test mcc$ hg book master
abort: bookmark 'master' already exists (use -f to force)

Okay, so I'll use -f ? Then this happens:

andis-macbook:test mcc$ hg book -f master
andis-macbook:test mcc$ hg push
pushing to git+ssh://g...@github.com:mcclure/test.git
creating and sending data
["git-receive-pack 'mcclure/test.git'"]
abort: pushing refs/heads/master overwrites dc7ac7f5f29d
andis-macbook:test mcc$ fatal: The remote end hung up unexpectedly

So now it seems my repository can't push at all... and usually by this
point I've gotten confused and done something stupid while trying to
trick hg-git into doing what I want (like creating an unnecessary
bookmark named "default/master"... my github repos are littered with
strange tags like "default/default/master").

Now, I've actually just now, while writing this email, after butting
my head against this problem for several months (and doing stupid
things like repeatedly deleting then re-pushing my github repositories
to get around "refs/heads/master overwrites"), figured out what I was
supposed to have been doing all along. Apparently if I say "hg push -
f" instead of just hg push, then hg-git will history rewrite at the
git end and throw away the dc7ac7f5f29d that was to be overwritten.
And experimenting more, I realize that if I give dc7ac7f5f29d a new
bookmark, named just anything, then this becomes a new git "named
branch" and gets pushed to github with everything else. Hm, okay, that
is a workflow I would be totally happy using. But wow, this is not
very discoverable! None of this (the push -f trick or the "must create
bookmarks" rule) seems obvious from the scanty hg-git docs and someone
experimenting to find them is more likely to create messes than find
the correct workflow. I feel like if I had this much trouble with this
there is a usability problem and others somewhere will probably
struggle with it as well.

Here are some things that it occurs to me any combination of which
would help with the usability problem here:

1. Detect the "refs/heads/master overwrites" thing and print a line
like "Use push -f to history rewrite".

2. My expectation as an hg user is that anything which shows up in "hg
heads" should be going out when I push. If there are anonymous
branches I do not wish to push, hg has existing interface idiomry for
me to communicate this. Maybe when one has open implicit branches that
do not have bookmarks, rather than ignoring them hggit should create
git-side named branches for them anyway and name the branch something
arbitrary but predictable (like perhaps the rev hash of the first rev
after the fork). When the user comes back later and ACTUALLY names
their branches (with hg book), the hash-named branch could be silently
discarded and replaced with the correctly named branch.

3. If this is not to be automatic, I think it would make sense to have
some sort of explicit command for managing git named branches. For
example add an "hg git-name" command, or something. Even if git-name
were literally just an alias to hg book, this would still be an
advantage because it would (1) leave no ambiguity as to what you are
doing-- stupid people like me would not wind up accidentally creating
default/default/master sort of tags because it wasn't obvious that say
bookmarks map to git names instead of tags mapping to git names and
(2) show up in the "hg help hg-git" menu, and thus be discoverable.

3a. Something I would really like is a "hg git-fix", which just goes
through and makes sure everything is correct. hg-git creates all these
bookmarks and tags and it's really not obvious (especially not to a
non-git user) what any of them do… like, my default/default/master tag
problem. I'd like to just go through and delete all these weird tags
that I think aren't supposed to be there from my repo, but if I do
this, will I accidentally delete a tag that was important and make hg-
git stop working? I don't know. And on at least one occasion I've had
hg-git act weird because for reasons I don't understand the master
bookmark failed to move forward on a commit like it's supposed to. A
git-fix could for example recreate all the tags that need to be
recreated, assign the "master" bookmark to tip, and either create new
bookmarks for open implicit branches that don't already have them (if
my suggestion (2) is not followed) or in any case print helpful
warnings saying something like, "Revision 32 doesn't have a bookmark,
it will not be pushed!"

Any thoughts on these issues? I would be willing to implement the
fixes described above if there were agreement ahead of time that the
approach is good and that the fixes would be accepted back into trunk…

Augie Fackler

unread,
Nov 7, 2011, 9:12:53 PM11/7/11
to mcc, hg-...@googlegroups.com
Random thoughts inline. I like proposal #3 best, see my comments next to that for my favorite idea.

No, it's because you don't have a bookmark pointing at 2. Revisions in git must have a bookmark or tag pointing to either that revision or one of its descendants to exist.

> I use the mercurial anonymous-branch feature a lot in my workflow, so
> I run into this problem a lot.
>
> My intuition on ways to fix this all usually wind up creating huge
> messes. I think: Okay, the master bookmark is what makes things
> "visible" to github, and I want that on revision 2. I know, I will
> just move the bookmark. I type (continuing from the commands above):
>
> andis-macbook:test mcc$ hg book master
> abort: bookmark 'master' already exists (use -f to force)
>
> Okay, so I'll use -f ? Then this happens:
>
> andis-macbook:test mcc$ hg book -f master
> andis-macbook:test mcc$ hg push
> pushing to git+ssh://g...@github.com:mcclure/test.git
> creating and sending data
> ["git-receive-pack 'mcclure/test.git'"]
> abort: pushing refs/heads/master overwrites dc7ac7f5f29d
> andis-macbook:test mcc$ fatal: The remote end hung up unexpectedly
>
> So now it seems my repository can't push at all... and usually by this
> point I've gotten confused and done something stupid while trying to
> trick hg-git into doing what I want (like creating an unnecessary
> bookmark named "default/master"... my github repos are littered with
> strange tags like "default/default/master").

My takeaway here is that you're trying to use hg-git to avoid understanding /anything/ about git. While I wish that were possible, I'm not sure it's actually a doable goal.

> Now, I've actually just now, while writing this email, after butting
> my head against this problem for several months (and doing stupid
> things like repeatedly deleting then re-pushing my github repositories
> to get around "refs/heads/master overwrites"), figured out what I was
> supposed to have been doing all along. Apparently if I say "hg push -
> f" instead of just hg push, then hg-git will history rewrite at the
> git end and throw away the dc7ac7f5f29d that was to be overwritten.
> And experimenting more, I realize that if I give dc7ac7f5f29d a new
> bookmark, named just anything, then this becomes a new git "named
> branch" and gets pushed to github with everything else. Hm, okay, that
> is a workflow I would be totally happy using. But wow, this is not
> very discoverable! None of this (the push -f trick or the "must create
> bookmarks" rule) seems obvious from the scanty hg-git docs and someone
> experimenting to find them is more likely to create messes than find
> the correct workflow. I feel like if I had this much trouble with this
> there is a usability problem and others somewhere will probably
> struggle with it as well.
>
> Here are some things that it occurs to me any combination of which
> would help with the usability problem here:

I'm open to a concrete (briefly summarized) proposal to make things better. I don't actually use hg-git on a daily basis anymore, so it's hard for me to come up with ideas of how things could be better.

>
> 1. Detect the "refs/heads/master overwrites" thing and print a line
> like "Use push -f to history rewrite".

I'm not sure how to communicate this to the user. One of the biggest problems with hg-git is the dual universe you live in, where things that make sense in Mercurial are illegal in git.

> 2. My expectation as an hg user is that anything which shows up in "hg
> heads" should be going out when I push. If there are anonymous
> branches I do not wish to push, hg has existing interface idiomry for
> me to communicate this. Maybe when one has open implicit branches that
> do not have bookmarks, rather than ignoring them hggit should create
> git-side named branches for them anyway and name the branch something
> arbitrary but predictable (like perhaps the rev hash of the first rev
> after the fork). When the user comes back later and ACTUALLY names
> their branches (with hg book), the hash-named branch could be silently
> discarded and replaced with the correctly named branch.

I've thought about this too, but I can't figure out how to make it work right.

> 3. If this is not to be automatic, I think it would make sense to have
> some sort of explicit command for managing git named branches. For
> example add an "hg git-name" command, or something. Even if git-name
> were literally just an alias to hg book, this would still be an
> advantage because it would (1) leave no ambiguity as to what you are
> doing-- stupid people like me would not wind up accidentally creating
> default/default/master sort of tags because it wasn't obvious that say
> bookmarks map to git names instead of tags mapping to git names and
> (2) show up in the "hg help hg-git" menu, and thus be discoverable.

This already exists. It's called bookmarks.

In hindsight, it was a mistake to ever try and be sensible in the absence of bookmarks. As I try and design a new extension for this problem, I'm not allowing working without bookmarks. Perhaps we should alter hg-git to do the same, and print a warning about revisions not being pushable since they're not the ancestor of any bookmarks.

>
> 3a. Something I would really like is a "hg git-fix", which just goes
> through and makes sure everything is correct. hg-git creates all these
> bookmarks and tags and it's really not obvious (especially not to a
> non-git user) what any of them do… like, my default/default/master tag
> problem. I'd like to just go through and delete all these weird tags
> that I think aren't supposed to be there from my repo, but if I do
> this, will I accidentally delete a tag that was important and make hg-
> git stop working? I don't know. And on at least one occasion I've had
> hg-git act weird because for reasons I don't understand the master
> bookmark failed to move forward on a commit like it's supposed to. A
> git-fix could for example recreate all the tags that need to be
> recreated, assign the "master" bookmark to tip, and either create new
> bookmarks for open implicit branches that don't already have them (if
> my suggestion (2) is not followed) or in any case print helpful
> warnings saying something like, "Revision 32 doesn't have a bookmark,
> it will not be pushed!"

I have no idea what you did to make a default/default/master tag. That sounds like absolute nonsense, but if I had to guess you did something with 'hg tag' that is strange.

Reply all
Reply to author
Forward
0 new messages