Adding uncommitted change into a new patch with Mercurial queues - mercurial

The process of creating patches in Mercurial is as follows:
Create patch with qnew -> Make changes -> Refresh patch
What if I have already made (uncommited) changes and I want to add them to the queue?

It depends on your version, and it looks like it changed in 1.5.1.
1.5.1 or later
The command will add any uncommitted changes by default
qnew creates a new patch on top of the currently-applied patch (if any).
The patch will be initialized with any outstanding changes in the working
directory.
earlier than 1.5.1
You want to use qnew -f. From the docs:
-f: Create a new patch if the contents of the working directory are modified. Any outstanding modifications are added to the newly created patch, so after this command completes, the working directory will no longer be modified.

Actually, the patch process works the same whether there are uncommitted changes or not. I always do it as follows:
[... make changes ...]
hg qnew -m "foo bar changes" foobar.patch
--> new empty patch at top of queue
hg qrefresh
--> this adds all diffs from 'hg diff' into the current top patch
EDIT: #CaseBash has correctly pointed out that I'm wrong about the current default behavior!

Related

Apply patches in branch

I receive a patches from a contributor. Apparently, he produces the patches in trunk. However, I would like to commit his changes to a new branch. May I know how I can do so through Tortoise-Hg?
The directions found below assume you want the patches isolated on a named branch.
Using the command-line, it is easy to apply patches to a new named branch. However if you want to do this using TortoiseHg only, you must create the named branch with an empty commit prior to applying the patches.
Using the command-line:
hg branch <branch name>
hg qimport --push <patch-file-1>
hg qimport --push <patch-file-2>
hg qfinish --applied
Using TortoiseHg v1.1.x:
Import the patch files to your patch queue (without applying them).
Create a new named branch using an empty commit:
Open a commit dialog
Click the button labeled "branch: default"
Select "Open a new named branch"
Enter your branch name in the provided text box
Click the OK button.
Enter a commit message (e.g. "Created branch for patches from ") and click "Commit".
Apply the patches in your patch queue (one at a time or all together).
Right-click on the patches in your patch queue and select "Finish Applied"
Using TortoiseHg v2.0.x:
The same process used for TortoiseHg v1.1.x will work. The process to create the named branch with an empty commit is slightly different, but the overall concept is the same.
In TortoiseHg 2.7, update to the branch you want to apply the patch to (or create it if it doesn't exist). Choose Repository > Import and use Browse... to select an individual patch file, or Browse Directory... to import a folder of patches.
The original branch from where the patch came from doesn't matter to hg import unless you use the option --exact. So, you should create the branch you want and apply the patch:
hg branch new_branch
hg import /tmp/patch.txt

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

Mercurial Patch Creation and Usage

I have come across a problem that I "think" can only be resolved using patches.
I cloned a project from our main repository, made quite a few changes (updates, deletion of files & directory and additions) to it. These changes are not even committed. The problem is, project from the main repository has been deleted/removed and recreated as a new project (name is same, all the directory structures everything is same as before). I cloned that project again from the main repository and would like to transfer all my uncommitted changes to it.
I am still exploring the hg patch to resolve that. It would be helpful if someone could confirm that creating and adding a patch IS the right approach to this, any resources explaining the process would be of great help.
You're correct — a patch is what you need to transfer the information from one repository to another (unrelated) repository. This will work since the files are the same, as you note.
So, to transfer your uncommitted changes from your old clone, you do
$ hg diff -g > uncommited.patch
$ cd ../new
$ hg import --no-commit ../old/uncomitted.patch
That will restore the information saved in the patch. This includes information about files that are added or renamed in the old clone.
The following steps can be performed with a standard Mercurial install:
Commit the changes in your local repository. Note the revision number.
Use "hg export -r REV >patch.diff" to create a patch.
Clone the new repository.
Use "hg import patch.diff" to apply the patch to the new repository.
Example
C:\>hg init example
C:\>cd example
C:\example>echo >file1
C:\example>hg ci -Am file1
adding file1
C:\example>hg clone . ..\example2
updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
C:\example>rd /s/q .hg
C:\example>hg init
C:\example>hg ci -Am same-but-different
adding file1
At this point example and example2 have identical contents, but the repositories are unrelated to each other due to deleting and reinitializing the .hg folder.
Now make some changes and commit them in one of the repositories, then export them as a patch:
C:\example>echo >>file1
C:\example>echo >file2
C:\example>hg ci -Am changes
adding file2
C:\example>hg export -r 1 >patch.diff
Below shows that the other repository can't pull the changes, because of the reinitialization. It can, however, apply the patch successfully:
C:\example>cd ..\example2
C:\example2>hg pull
pulling from c:\example
searching for changes
abort: repository is unrelated
C:\example2>hg import ..\example\patch.diff
applying ..\example\patch.diff
I would first make copies of everything so you have a way of backtracking.
Then, in the working copy with the changes, I would first delete the .hg directory, then copy in the .hg directory from the new repo. This basically transfers all of the changed files into the new repo without the need to delete any files and directories.
You will still need to tell the repo about whether to remove any files marked as missing. You will also have to handle renames manually. If this is a small number of operations, it's easier than trying to use the patch method.
Once this is done, commit your changes and push, if necessary.
seems like what you want is patch queues. In that you have uncommitted changes, and you want to pull from the new repo before committing them....
$ hg qinit -c # initialize mq for your repo containing the uncommitted changes
$ hg qnew name_of_patch # create patch that contains your uncommitted changes
$ hg qpop # resets your working dir back to the parent changeset
no worries though, your changes are safe and sound in .hg/patches/name_of_patch to see for yourself.....
$ cat .hg/patches/name_of_patch
now pull in the new repo
$ hg pull -u http://location.of.new/repo # pull in changes from new repo update working dir
$ hg qpush # apply your uncommitted changes to new repo
If you are lucky you will have no merge conflicts and you can go ahead and commit the patch by....
$ hg qfinish -a # change all applied patches to changeset
And then if you want....
$ hg push http://location.of.new/repo
If the repos are unrelated, just init a patch repo on your new repo. and manually copy the patch in and add it to .hg/patches/series file.
assuming patch was created. clone new repo
$ hg clone http://location.of.new/repo ./new_repo
init patch repo
$ cd ./new_repo && hg qinit -c
copy patch
$ cp ../old_repo/.hg/patches/name_of_patch .hg/patches/
edit series file using an editor of some sort
$ your_favorite_editor .hg/patches/series
name_of_patch # <---put this in the series file
apply your patch to new repo
$ hg qpush
if no merge conflicts and you are convinced it works
$ hg qfinish -a
If the layout is the same, you can just copy all the files over (excluding .hg) and then use hg addrem.
Try to look into the MQ plugin, it does exactly this if I recall. I've never had a use for that though, so I can't say.
If the old repository was simply moved/cloned to a new URL then you could simply change the remote repository you talk to the new one.
If, however, it was recreated from the ground up (even with the same structure) then I don't believe Mercurial has any built-in functionality to help you here. Mercurial patches reference specific changesets which won't exist in your new repository.
You could use a merge tool to perform the diff and bring across any changes you made.
Edited To answer the question in the comment:
When you clone the repository you are taking a complete snapshot of the entire change history - along with the associated change-set IDs, etc.
Mercurial tracks changes by change-sets to the repository, rather than at the file level like Subversion.
If you clone, then you can easily push/merge into another repository that was also cloned from the same source.
If you recreated the repository then the change IDs won't match, and can't be merged in Hg.
The only option in this scenario would be to use a Merge tool which will let you see mismatches in files/folder structure.
Also: Worth pointing out http://hginit.com/ because it explains (indirectly) some of this.

Can I revert a range of lines in a file with mercurial?

I often notice a few unwanted changes when I review my working copy (with hg status and hg diff) right before a commit. For example, I might have temporarily added or remove some code just for the duration of a debugging session.
I know I can use hg revert to remove unwanted changes, but this removes all the changes in the entire file. Is there a way to revert just a part of a file?
I'm not sure if you can revert individual lines explicitly, but what I do in situation like yours is to commit the good code and revert the rest (the bad code). This workflow is easy using Mercurial's record or crecord extension (I recommend the latter one).
I've been doing this with the interactive ncurses style ui hg revert -i that lets you walk around and select the parts you want to destroy, either file, diff chunk or line by line, as you please, depending on how deep you unfold your changes.
I am not sure if this is a standard hg feature or not, you can verify easily enough if yours has it:
> hg revert --help --verbose | grep -- -interactive
-i --interactive interactively select the changes (EXPERIMENTAL)
Just remember that the changes you mark (X) will be what gets nuked, not what you retain.
Assuming you have the record and shelve extensions enabled, you can do as follows:
hg record -m "garbage" # pick out and commit the change you want to revert
hg shelve --all # temporarily hide other changes
hg strip tip # remove the garbage changeset
hg unshelve # restore the changes you want to keep
One way is using a graphical diff tool like kdiff3. If you feed it the diff and choose "merge current file" you can go line by line and pick what you want.
The better way is to commit more often. If you make a habit to commit right before adding debugging code, then either commit or revert your debug code before adding your "real" code, it makes it very easy to remove your debug code because it has its own revision. Alternately, you can put your debugging code in a separate branch altogether.
The TortoiseHg GUI's "Commit" window has a "Hunk Selection" tab that allows selection of specific sections of a file's changes to be committed.
I've got a different answer for your problem, which is the same problem I have. This is a great use case for mercurial queues!
When I am about to start adding debugging code to a change that I think is ready, I do the following:
hg qnew -m "fix for bug #123" fix.patch # basically a local-only commit
hg qnew -m "debugging" dbg.patch # prepare the next changeset at the tip
[add my debugging]
hg qrefresh # update the changeset at the tip
[...]
hg qpop # pop the debugging off the repo history
It takes a little bit of getting used to -- you end up having to reorder your patches to then fold whatever fixes you made into the original work patch.
Also, check out Bill Barry's attic extension. This page talks about how to use it for a few different workflows and how that compares to using mq. https://www.mercurial-scm.org/wiki/AtticExtension
If the commit where you want the change is e.g. ba1c841aaff4,
the easiest is to use:
hg meld -r ba1c841aaff4^ <filename>
Now click on a right arrow (pointing to the right) in the middle to revert your lines, save the file and close meld. kdiff3 (old and unintuitive) can be an alternative.
Side note: in order to use meld you need to configure it in our ~/.hgrc file:
[extdiff]
cmd.meld =
[merge-tools]
meld.args=$base $local $other
Download and install meld https://meldmerge.org/
(You should have Mercurial installed locally or TortoiseHg)
Launch Meld and select Version Control View and then select you will be asked to choose a parent directory of the file you want to modify.
Select the file which has the uncommitted changes
Choose which lines you want to revert to the last commit by selecting the arrow. The document on the left is from the last commit and the right document is the file with the uncommitted changes.

No changes are pushed when using hg-git

I'm trying to get the hg-git extension working under Windows and after hours of fiddling, I finally seem to have it working. However, nothing shows up in my git repository even though the output of hg push reads:
importing Hg objects into Git
creating and sending data
github::refs/heads/master => GIT:8d946209
[command completed successfully Wed Oct 20 15:26:47 2010]
Try issuing the command hg bookmark -f master
(use -f to force an existing bookmark to move)
Then try pushing again.
This works because Hg-Git pushes your bookmarks up to the Git server as branches and will pull Git branches down and set them up as bookmarks. (from the official README.md)
And it seems that just after I asked this, I made a trivial change. This was picked up and pushed. So it seems that you have to wait until you've made a new commit in order for hg-git to pick it up.
I had chosen to 'Initialize this repository with a README'. This meant I ended up with two heads, which I couldn't hg merge because one had a bookmark.
To get pushing working, I had to:
configure hg-git and github remote as per https://blog.glyphobet.net/essay/2029
pull from github and update
force the merge (checking which id to use with hg heads),
commit the merge
add a trivial change to a file (add a space char to the end),
commit, then
move the bookmark to the tip
push to my configured github remote
This ended up with commands as follows (substituting in <x> sections)
hg pull github
hg update
hg merge <revision-id-of-incoming-git-version>
hg addremove
hg commit -m 'merged with github'
# make some trivial change to a file - eg add a space where it doesn't cause harm
hg add <changed-file>
hg commit -m 'trivial change'
hg bookmark -f master
hg push github
make sure you pick the remote revision for the merge above - if you don't it doesn't work!