Apply mercurial bundle file on different changeset - mercurial

TLDR: I have an HG bundle with parent X, but revision X does not exist in my repo. However, I am sure that the files of revision Y are identical to revision X. How do I apply the bundle?
Background:
I use hgsubversion to interact with an SVN repo.
There were some changes I did not want to commit. hgsubversion does not support partial pushes.
I used to workaround by manually creating temporary exports/patch files, or manually restoring .orig files (result of hg revert).
In this case I committed the changes I did not want to push, then used hg strip, then pushed, then tried to use hg unbundle .hg/strip-backup/file.
Problem: hgsubversion replaces the original changeset with a new one it imports from SVN after it's committed it. Result: the changeset ID changes. This is a problem because now hg unbundle no longer works, as it relies on the parent changeset being there (but it's been stripped).
Ironically, hgsubversion itself uses strip and thus has a backup file I can use to strip the new rev, add the stripped old revision, then apply the bundle with my revisions, export the patch, strip both, and restore the SVN revision. But this sounds... extremely painful and stupid. Is there nothing better I can do?
(hg transplant doesn't seem to like the bundle without having the parent in the repo, either)

It's effectively impossible to use a bundle without the bundle's precise parent changesets. Bundles consist of compact binary deltas that can only be applied to the precise binary source. There is no 'context' available that would allow Mercurial to guess how to apply them to other revisions the way patch does. In core Mercurial, this is never an issue because changesets are never removed, but extensions like hgsubversion and mq break the rules.
(If you can recover the stripped changesets from a backup bundle in .hg/strip-backup, you can then rebase your changes and strip again.)

Background: After hitting an issue with hgsubversion pushing only 1 revision successfully, I got lazy in my commits (partial commits are possible if you update to the latest revision you want to push), and ended up starting to commit everything. So I killed the push and for the first time it failed to keep my later revisions.
I tried recover, but that was not able to find the parent commit. What worked for me was restoring the ...-backup.hg file (there was a ...-temp.hg file in strip-backup too).
The strange thing (which is why I'm answering this) is that it only gave me a warning about the parent not being there (I have no idea why)...
warning: ignoring unknown working parent d5663567bc4b!
adding changesets
adding manifests
adding file changes
added 21 changesets with 1255 changes to 941 files
(run 'hg update' to get a working copy)
BTW, I'm running Mercurial version 2.0

Related

Mercurial: Unable to unshelve shelved changes

It seems like ShelveExtension only shelves your modified files leaving untracked or deleted.
I am new to Mercurial and coming from git so for me this is not expected.
Even bigger problem I am not able to hg unshelve with what I assume is an error message.
See below:
unshelving change 'main'
temporarily committing pending changes (restore with 'hg unshelve --abort')
rebasing shelved changes
abort: uncommitted changes
Is that an expected behavior and I am just missing something?
How could I unshelve my modified files without restoring/committing/etc.?
Is there an extension which behaves exactly like git stash?
Steps to reproduce:
Environment:
OS: Windows 8
Mercurial: Mercurial Distributed SCM (version 3.0.1).
Installed as cygwin /usr/bin/hg (Tortoisehg is not installed, Windows hg is installed but not used)
Extension: ShelveExtension.
Is that an expected behavior and I am just missing something?
Yes, this is normal behavior. You need to do hg addremove (or manually hg add and hg rm the individual files) if you want Mercurial to track file creation and deletion. Renaming should be done with hg mv. This is vaguely similar to git add, except that you do not need to do it for modified files.
When you unshelve, your working directory should be clean. At the very least, it should not have any missing files (prefixed with ! in hg st) nor any modified files (prefixed with M). You can always make a temporary commit and hg strip it later.
How could I unshelve my modified files without restoring/committing/etc.?
There's no sane way to do this in the general case. What if the shelf contains changes to a file which no longer exists? If the file deletion had been committed, you could generate a patch conflict, and that's what Mercurial does. But without a commit to conflict with, there's no obvious response to this situation.
Is there an extension which behaves exactly like git stash?
Not to my knowledge, but this is beyond the scope of StackOverflow.

How to rename a file using Mercurial Queues?

Mercurial Queues is about patches, and patches know nothing about file renames. Is this the reason why Mercurial Queues don't support file renames, or am I doing something wrong renaming the file? I have worked on a patch queue modifying just one file called foo. Now I go back to patch 4 and rename the file via hg mv:
hg qpop 4 # Unapply all patches until patch 4.
hg mv foo bar # Rename file and led Mercuial know about it.
hg qrefresh # Should apply changes to unapplied patch 4.
hg qpush -a # Should apply all unapplied patches.
I get the following error:
unable to find 'foo' for patching
1 out of 1 hunks FAILED -- saving rejects to file foo.rej
patch failed, unable to continue (try -v)
patch failed, rejects left in working dir
errors during apply, please fix and refresh 5.diff
So how should I do to handle file renames with Mercurial Queues? Mercurial commits handle file renames for a reason (as without, it would lose the whole history about the editing of the file after renaming).
Update
Just noticed that hg histedit folding changesets and hg collapse also lose the information of file renaming, the file shows up as a new one instead of a renamed one, and I guess this is for the same reason. Seems like collapsing private changesets is not possible in Mercurial without loosing that information?
Update 2
Found out collapsing private changesets without loosing rename information is possible with hg rebase and its --collapse option, e.g. hg rebase -s 5 -d 4 --collapse. The issue that the other commands should sostain rename information is still vacant, but using the hg rebase command there is at least a way to achieve the desired result.
Is this the reason why Mercurial Queues don't support file renames,
no.
or am I doing something wrong renaming the file?
no.
Yes, patches in chain will have troubles, if they was prepared for foo file, but later in will be bar, but due to different reasons: patches are independent, and every and each patch know nothing about changes in others - they work with context, not with sequence of operations in separate patches. You done rename correctly, but this changeset invalidates later changesets, prepared on old content

How do you delete a commit in Mercurial?

I want to completely delete a Mercurial commit as if it was never entered in the repository and move back to my prior commit.
Is this possible?
If it was your last commit and you haven't pushed it anywhere, you can do that with rollback. Otherwise, no. Not really. Time to change your passwords.
Edit: It has been pointed out that you can clone from an older revision and merge in the changes you want to keep. That's also true, unless you have pushed it to a repo you don't control. Once you push, your data is very likely to be very hard to get back.
You can try to remove mq info about your commit.
For this you need to go File->Settings->Extensions.
There check mq and restart gui.
After that just right click on unneeded commit and
ModifyHistory->Strip
To edit the history I would use the Histedit Extension extension.
hg histedit 45:c3a3a271d11c
However keep in mind this only makes sense in a situation where you have not yet pushed the commits to the public repository, you own the public repository and/or you can account for all the clones out there. If you receive the following error:
abort: can't rebase immutable changeset 43ab8134e7af
It means that Mecurial thinks this is a public changeset (see phases) that has already been pushed - you can force it to be a draft again doing:
hg phase -f -d 45:c3a3a271d11c
I encounter this fairly often. I make a commit and then pull to push. But then there is something incoming that makes my newly made commit unnecessary. A plain hg rollback isn't enough because it only undoes the pull...
This is the thing to do:
hg strip <rev>
Things are painless when you don't push your changesets anywhere.
If it's more than one commit and/or you already pushed it somewhere else, you can clone your repository and specify the last changeset that should be cloned.
See my answer here how to do this:
Mercurial: Fix a borked history
If you only committed locally and didn't push, you can just create a clone locally (as described in my link) and you're done.
If you already pushed to some remote repository, you would have to replace that with your clone.
Of course it depends if you are able (or allowed) to do this.
You can use "hg backout" to do a reverse merge basically. All options are discussed in the freely available book "Mercurial: The Definitive Guide":
http://hgbook.red-bean.com/read/finding-and-fixing-mistakes.html
If using tortoise you can use modify history > strip...
Yes. Unless I am mistaken, as of v2.3 (rel. 2012/08/01) you can use the HisteditExtension with a drop command to drop a commit, along with strip or backout to remove changes.
A simple Google search on the feature: https://www.google.com/webhp#q=histedit+drop
In 2022 I do use evolve extension. It is one of the best extensions for this purpose.
To prune unwanted changeset, if you for example did a quick hack to get the code working:
$ echo 'debug hack' >> file1.c
$ hg commit -m 'debug hack'
Now you have a proper patch you can do hg prune .:
$ hg prune .
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
working directory is now at 2a39221aaebb
1 changesets pruned
If you push the change to the remote repository you will find only obsolescence markers:
$ hg push
searching for changes
no changes found
remote: 1 new obsolescence markers
To check the changes to your local repo you can pull from the remote one:
$ hg pull
pulling from ssh://userid#server/repo
searching for changes
no changes found

How do I get changes to propagate to all subrepos in Mercurial?

I have recently switched from Subversion to Mercurial for source control and in doing so have split up one repository into several. I used subrepos to manage the dependencies between repositories. The problem is that pull is not suprepo aware so I have to go into each subrepo and pull changes in order to update a repository. Is there a better way to do this?
pull is not suprepo aware
hg pull should be subrepo aware, provided it is used with the -u (--update) option.
The hg update should, when it comes to subrepos, take them into account:
Whenever newer Mercurial versions encounter this .hgsubstate file when updating your working directory, they'll attempt to pull the specified subrepos and update them to the appropriate state.
Subrepos may also contain their own subrepos and Mercurial will recurse as necessary.
The OP CoreyD adds:
That has not worked for me.
I create two repos /repo and /sub and I clone sub into repo (/repo/sub).
Then I create an .hgsub file in /repo with this a line like this sub = ../sub and commit it.
When I make changes to /sub and then do an update in /repo /repo/sub is unchanged.
Am I doing something wrong?
That looks about right:
SubRepos or submodules (for Git) are all about referening a precise configuration (changeset ref for hg, or commit ref for Git, as explained in this SO question)
When you change anything outside of /repo, you don't change the .hgsubstate file within /repo recording the exact configuration (changeset reference).
Hence no change at all.
You could rather do your /sub changes directly in /repo/sub, commit them, then commit /repo.
Then, a clone of /repo would have the new configuration.

How to 'hg merge' without affecting the working directory?

Suppose that:
I have a repo called MyRepo.
I have uncommitted changes in my working directory.
I do a pull from Repo1 and that creates a branch in MyRepo
I want to do a merge of what I already had in my repo with what I have just pulled.
As described here, the merge process changes the state of the working directory.
In my scenario, I don't want to loose the uncommitted changes that are in my working directory because at that point, I'm interested in changing the state of MyRepo; not the state of my working directory.
Is there a way to go through the merging process, including resolving conflicts manually, without affecting the content my working directory? Can this process be done in temporary files only? Or should I shelve my changes, do the merge and then unshelve to restore my working dir to the state it was before the merge?
Use shelve or attic extensions to temporarily stash your changes while you merge.
You could clone your repository to your latest checkin, merge the changes with the clone, commit, and then pull the new changeset from your cloned repository back into your current one.
You can't do that. As the documentation says, merge really is a working tree operation. Resolving conflicts without testing the result is crazy. Either shelve or commit the changes and then do the merge. If you don't want the commit to be visible anywhere, you can commit the change, update to the previous revision, merge the new branch, apply the temporarily committed patch and strip that temporary branch.
Just do it in a clone.
Clone MyRepo to MyRepo-merger, which will pull over all the committed changes, but not your uncommitted changes
Inside MyRepo-merger do a pull from Repo1.
Inside MyRepo-merger do all the hg merges you want
Inside MyRepo-merger push to either Repo1 or the original MyRepo
Pushing back to MyRepo won't alter the working dir of MyRepo, so that's a safe action.
Remember in Mercurial that clones are incredibly fast and cheap -- on modern file systems they use hardlinks under the covers so they're not even taking up much space on disk.
I'm pretty new to mercurial, but couldn't you clone from your local repository and copy your working copy over to the clone? You could then run your merge in the original? You'd preserve the state of your working copy in the clone while being free to allow the change of the original's working copy.
Test this first. I've never done it in hg.