Incorrect "Can't rebase immutable changeset" error - mercurial

Occasionally, when I do a pull with rebase, I get the following error:
abort: can't rebase immutable changeset fa044e766d1f
hint: see hg help phases for details
Funny thing is, that changeset that errors (and it is the same one every time) is very old, and is already public.
Here's a screenshot:https://db.tt/xHiOxm6R
It appears that I can just ignore the message (at least, so far, I've not gotten into any trouble by doing so), but obviously, I'd like to help Hg resolve this issue so that its not going to continue to be a hassle in the future.
Thanks.
Update
hg out does not list it. The clone is probably about 6 months old. Also, as far as I know, none of the other developers are experiencing this problem.

(note: I can't see the screenshot due to a strict firewall).
You mention that the error occurs every time on a changeset that "is very old, and is already public." This should give you a hint as to why you're getting the error.
Changesets in Mercurial have 3 possible phases:
- public : changeset is visible on a public server
- draft : changeset is not yet published
- secret : changeset should not be pushed, pulled, or cloned
Changesets that are at draft and secret are considered to be mutable, that is you can change their history, modify them (perhaps change the author, for example), and so on.
Changesets that are at public are considered immutable, you "cannot" change anything about them when they are in this phase. The reason being, they have already been seen by the outside world, and so if you make any modifications, it could cause issues. The theory is that you can't remove a changeset that's public - if you tried to change it, you would simply create another changeset.
That said, you can force the phase of a changeset back to draft like so:
hg phase -d -f <changeset_id>
This would allow you to rebase, or modify the changeset in some other way.
However, this would not remove the original changeset in the "outside world"... that already exists, and will until the end of time itself. It would simply create a new one that does mostly the same thing.
So, if you did try to resolve the issue, you would likely just create a new issue in its place.
Caveat: as I can't see the screenshot from here, I may revise my answer later.
Update: A quick note - having seen the screenshot, that looks like a very old changeset, which makes me wonder just what you're trying to rebase. As Kindread mentions above, merging would be your best bet here.

Related

Edit commits in HG repo & mark as "closed"

I'm currently dealing with the fallout from BitBucket dropping HG support. We're going to be giving hg-git a try because, while my preference is self-hosting, my boss isn't quite mad enough at Atlassian to move away from BB yet. Taking this opportunity to clean up our existing HG repo before the conversion to GIT. Have used hg convert to remove some accidentally committed binaries to reduce size, etc.
One thing I've noticed is that we've got about two dozen old branches that are technically "open", but have been merged into default (no closing commit, but they're months to years old). Is there any way I can use a tool like hg histedit or during the hg convert to go back and specifically mark old branch heads with --close-branch?
Looking through docs I can find things about editing files, editing the contents of commits, or modifying commit messages, but nothing I can find mentions meta-data around whether a commit is "closed". I know this is just a flag on a given commit, but I don't know how to retroactively add it via any HG extension.
Edit: Just to add a bit more clarity, I recognise I can just update to each of these old branches & add a new commit that just closes the branch. There'll be a lot of dangling-looking, closed heads, but that'd work fine enough. However, I also then have to give each of them a bookmark in HG as well, or these additional "closing" commits are lost in the hg-git conversion. I'd rather avoid having to add ~30 additional branches to the git branch-list, just to have them show up as closed properly in HG without having to use revsets.
What I want to do isn't "essential" in the grand-scheme of the repo, but I'd be surprised if editing a commit's metadata to say --close-branch were impossible.
I tested out the rebase idea with a mock repository and it seemed to work.
Here was the starting repo:
And here was the state after rebase:
I think this example matches what the question was asking about. The original dangling close-branch changeset was moved to precede the merge.
I updated to default and ran the following command:
hg rebase --dest=4 --source=3 --keepbranches --config=ui.merge=internal:merge
I actually used Tortoise Workbench to execute the rebase and that is the command it used. So the final argument for ui.merge is probably not strictly necessary.
As you may have already noticed using hg convert its a really good idea to make new clones when you go to modify the repository. Thus if it gets messed up you have an easy undo option. I'd certainly recommend that approach for this operation as well.

Mercurial: Pull changes from remote repository without public phase (non-publishing server, "abort: can't rebase immutable changeset")

Background
Mercurial now has Phases which are a great mechanism to keep people from altering history that should not be altered. When a changeset is pushed to remote repository it is made public and can no longer be rebased. This is normally a good thing, if the repository is public and you don't want others altering your changesets.
However, if you have your own temporary repository for sharing draft changesets only with yourself, it can get very annoying. Mercurial has a option to turn this off. Adding this to the .hg/hgrc file of the remote repository.
[phases]
publish = False
This will prevent changesets pushed to the remote repository from moving from draft phase to public phase (both the local changeset and the one just pushed to the remote repository. However, pulling this changeset down still will always pulls it down in the public phase.
Question
I am hoping to pull down changesets in the draft phase. I simply want to push a changeset to my personal server then pull it down at home. After I pull it I will rebase my temporary commit on top of any commits that I have pulled down from our true publishing server.
Any way to avoid the auto-moving of a pulled changeset to public would be great. This remote repository is a complete and total draft server for myself. Being forced to manually move the changesets back to draft after an unsuccessful rebase attempt is really starting to wear on the nerves.
References
https://www.mercurial-scm.org/wiki/Phases
Introduction to Mercurial Phases
How do I tell (locally) mercurial that a server is non-publishing?
After pushing to a review repository, "abort: can't rebase immutable changeset" on rebase
That seems like a bug. What version of Mercurial are you using? Have you tried filing a bug at https://bz.mercurial-scm.org ?
I got this resolved through help on Bugzilla. Here is a snippet of my final post there (thanks for putting me on the right track djc).
It looks like we can chalk it up to User Error/Bad Test Cases (of
course). If there is anyone to blame (besides myself) it could be
TortoiseHg. My test cases consisted of stripping commits and from one
of the two repositories and then changing the phase of that commit in
the other repository and push/pull again. It seems like TortoiseHg
would pull in the wrong phase sometimes (possibly a caching issue). I
can still reproduce this when I switch my server back and forth
between publishing and non-publishing (but not quite as consistently
before).
However, doing a command line pull seems to get it working every time.
I was using command line to check outgoing/incoming, do the phase
changes and such, but possibly never used it for the actual pull.
Sorry to waste everybody's time. I resolved it as invalid, not sure if
someone wants to change it to a better category. Let me know if there
is anything else you want me to check or expand upon.
I'll edit in any updates, let me know if anyone is still having trouble with this.

Is cvs2hg still potentially producing corrupted repositories?

Trying to migrate a repository from cvs to hg, I found the tool cvs2hg, and it seems to do nicely he job (conversion goes fine, and I have all the tags and branches).
However, the hg documentation warns about "fixup commits" making the repository somewhat corrupted or at least dangerous.
Is this still a problem ? Maybe hg or cvs2hg have benefited from fixes since this warning was written.
If it is, potentially, how can I check if I am in such a dangerous situation, on the resulting hg repository ?
Fixup commits are good and necessary. And cvs2hg does much better job than hg convert.
But maybe first about the problem. In CVS repository you can play various dirty tricks with tags and branches. For example, you can manually fine-tune some tag tagging today's version of 3 files, yesterday's version of 4 others, and month-long version of yet another. In practice, I did it a lot of times to make "patch tags" (there is some old tag, I have various commits afterwards, there turns out to be a bug, I fix the bug, make fixup tag by old tag, moving it on 1-2 files).
In the result, you get tag which points to release which naver has existed or will exist at any point of repository history, if the history is taken for whole repo.
Similar tricks could be made with branches. Or branches can start from "ugly" tag.
Any kind of „natural” conversion of CVS to HG is dead lost on such cases. There is no place in the time-based history at which such tag or branch could be hooked. And hg convert just binds such tags at more-or-less random places, and branches at very ugly places.
Fixup commits simply are those missing revisions: artificial commits which are bound at appropriate place and introduce changes which put repository at state at which it should be at given tag. With those, we get both "artificial" tags, and branches, properly bound to proper code.
So if you:
commited a.c(1.1), b.c(1.1) and c.c(1.1)
commited a.c(1.2), b.c(1.2)
commited c.c(1.2)
artificially created tag blah_1.0 which points to a.c(1.1), b.c(1.1) and c.c(1.2)
commited a.c(1.3), b.c(1.3)
...
then hg convert based history will have 4 edit changesets (just like those above) and blah_1.0 bound at some ugly place with wrong content. At the same time, cvs2hg will create "fixup commit" which will artificially create changeset at which we really have a.c(1.1), b.c(1.1) and c.c(1.2), and tag there. In a history, such changeset is reasonably similar to transplanted/grafted/cherry-picked commit.
You should carefully check the resulting repository to make sure it represents your code history and doesn't contain any of these crappy fixup commits.
BTW, it might be worthwhile to check out the newer http://www.catb.org/esr/reposurgeon/ tool.

Pull commits on repo post-rebase

I'm looking for a simple way to pull in additional commits after rebasing or a good reason to tell someone not to rebase.
Essentially we have a project, crons. I make changes to this frequently, and the maintainer of the project pulls in changes when I request it and rebases every time.
This is usually okay, but it can lead to problems in two scenarios:
Releasing from two branches simultaneously
Having to release an additional commit afterwards.
For example, I commit revision 1000. Maintainer pulls and rebases to create revision 1000', but at around the same time I realize a horrible mistake and create revision 1001 (child of 1000). Since 1000 doesn't exist in the target branch, this creates an unusable merge, and the maintainer usually laughs at me and tells me to try again (which requires me getting a fresh checkout of the main branch at 1000' and creating and importing a patch manually from the other checkout). I'm sure you can see how the same problem could occur with me trying to release from two separate branches simultaneously as well.
Anyway, once the main branch has 1000', is there anything that can be done to pull in 1001 without having to merge the same changes again? Or does rebasing ruin this? Regardless is there anything I can say to get Maintainer to stop rebasing? Is he using it incorrectly?
Tell your maintainer to stop being a jacka**.
Rebasing is something that should only be done by you, the one that created the changesets you want to rebase, and not done to changesets that are:
already shared with someone else
gotten from someone else
Your maintainer probably wants a non-distributed version control system, like Subversion, where changesets follows a straight line, instead of the branchy nature of a DVCS. In that respect, the choice of Mercurial is wrong, or the usage of Mercurial is wrong.
Also note that rebasing is one way of changing history, and since Mercurial discourages that (changing history), rebasing is only available as an extension, not available "out of the box" of a vanilla Mercurial configuration.
So to answer your question: No, since your maintainer insists on breaking the nature of a DVCS, the tools will fight against you (and him), and you're going to have a hard time getting the tools to cooperate with you.
Tell your maintainer to embrace how a DVCS really works. Now, he may still insist on not accepting new branches or heads in his repository, and insist on you pulling and merging before pushing back a single head to his repository, but that's OK.
Rebasing shared changesets, however, is not.
If you really want to use rebasing, the correct way to do it is like this:
You pull the latest changes from some source repository
You commit a lot of changesets locally, fixing bugs, adding new features, whatnot
You then try to push, gets told that this will create new heads in the target repository. This tells you that there are new changesets in the target repository that you did not get when you last pulled, because they have been added after that
Instead, you pull, this will add a new head in your local repository. Now you have the head that was created from your new changesets, and the head that was retrieved from the source repository created by others.
You then rebase your changesets on top of the ones you got from the source repository, in essence moving your changesets in the history to appear that you started your work from the latest changeset in the current source repository
You then attempt a new push, succeeding
The end result is that the target repository, and your own repository, will have a more linear changeset history, instead of a branch and then a merge.
However, since multiple branches is perfectly fine in a DVCS, you don't have to go through all of this. You can just merge, and continue working. This is how a DVCS is supposed to work. Rebasing is just an extra tool you can use if you really want to.

Help understanding the benefits of branching in Mercurial

I've struggled to understand how branching is beneficial. I can't push to a repo with 2 heads, or 2 branches... so why would I ever need/use them?
First of all, you can push even with two heads, but since you probably don't want to do that, the default behavior is to prevent you from doing it. You can, however, force the push to go through.
Now, as for branching, let's take a simple scenario in a non-distributed version control system, like Subversion.
Let's assume you have a colleague that is working in the same project as you. The current latest changeset in the Subversion repository is revision 100, you both update to this locally so that now both of you have the same files.
Ok, now your colleague has already been working on his changes for a couple of hours now, and so he commits. This brings the central repository up to revision 101. You're still on revision 100 locally, and you're still working on your changes.
At some point, you complete, and you want to commit, but Subversion won't let you. It says you have to update first, so you start the update process.
The update process wants to take your changes, and pretend you actually started with revision 101 instead of 100. If your changes are not in conflict with whatever it was your colleague committed, all is hunky dory, but if your changes are in conflict, you have a problem.
Now you have to merge your changes with his changes, and things can go haywire. For instance, you might end up merging one file OK, the second file OK, or so you think, and then the third file, and you suddenly discover that you've got some of the details wrong, it would've been better to merge the second file differently.
Unless you made a backup of your changes before updating, and sooner or later you will forget, you have a problem.
Now, the above scenario is actually quite common. Well, perhaps not the merging part, it depends on how many is working in the same area or files at the same time, but the "must update before committing" part is quite common with Subversion.
So how does Mercurial do it?
Well, Mercurial commits locally, it doesn't talk to any remote repository at all, so it won't stop you from committing.
So, let's try the above scenario again, just in Mercurial this time.
The tipmost changeset in the remote repository is revision 100. You both have cloned this down, and you're both starting to work on the changes, from revision 100.
Your colleague completes his changes and commits, locally. He then pushes his changeset up to the central repository, bringing the tip there up to revision 101.
You then complete your changes, and commit, also locally, and then you want to push, but you get the error message you've already discovered, and is asking about.
So how is this different?
Well, your changes are now committed, there is no way, unless you try really hard to accidentally lose them or destroy them.
Here's the 3 repositories in play and their current state:
Colleague ---98---99---100---A
Central ---98---99---100---A
You ---98---99---100---B
If you were to push, and was allowed to do this (or force the push through), the Central repository would look like this:
Central ---98---99---100---A
\
+--B
Two heads. If your colleague now pulled, which one should he continue working from? This question is the reason Mercurial will by default prevent you from causing this.
So instead you pull, and you get the above state in your own repository.
In other words, you can chose to impact your own repository and create multiple heads there, but you are not imposing that problem on anyone else.
You then merge, the same type of operation you had to do in Subversion, except your changeset is safe, it was committed, and you won't accidentally corrupt or destroy it. If, mid-merge, you want to start over, you can, nothing lost, no harm done.
After the merge, your local repository looks like this:
You ---98---99---100---A----M
\ /
+--B--+
This is now safe to push, and if your colleague now pulls, he knows that he has to continue from the M changeset, the one that merged his and your changes.
The above description is what happens due to Mercurials distributed nature.
You can also name branches, to make them more permanent. For instance, you might want to name a branch "stable", to signal that any changesets on that branch have been thoroughly tested and is safe for release to customers or to put into production. Then you would only merge changes onto that branch when said testing has been completed.
The nature, however, is the same as the above description. Whenever more than one person works on a project with Mercurial, you will get branches, and that's a good thing.
Whenever more than one clone of a repo is made and commits are made in those clones, branches happen, whether you name them by using the hg branch command or not. My philosophy is, you might as well give them a name. It makes things less confusing.
A good explanation of mercurial branches: http://stevelosh.com/blog/2009/08/a-guide-to-branching-in-mercurial/