Undo a remove action in Mercurial - mercurial

Suppose that I have made some changes in the working directory and accidentally marked several files (that include some of the modified ones) for removal. How do I unmark the files for removal without losing the changes I have made?

Just hg add the files.
I don't know why you're getting some many answers that modify the working directory. If you've accidentally marked some files for removal you can undo it with add.
ry4an#four:~/hgtest$ hg status --all
M another_file
C a_file
ry4an#four:~/hgtest$ hg remove --after --force *
ry4an#four:~/hgtest$ hg status --all
R a_file
R another_file
ry4an#four:~/hgtest$ hg add *
ry4an#four:~/hgtest$ hg status --all
M another_file
C a_file
That said, don't use --force with hg remove or ever really. Also try to get in the habit of using hg forget instead of hg remove --after,

there are two options using hg revert :
hg revert -a
which will go back to the previous revision and put all your changes in new files with .orig appended to the names
hg revert [names of files to unremove] to just revert those files
i'd probably go with the latter

hg revert
I'm pretty sure Mercurial even makes backups of your changes by default.

If the file exists, (likely if you've marked it for removal with hg forget or if you've modified it then hg removed it), do hg add [file] to add it back with any changes made after the last commit and before forgetting the file.
If the file does not exist (likely if the file was unmodified and you've marked the file for removal using hg remove), do hg revert [file] to revert it back to its state in the parent of the working directory.

I had the exact same problem. hg add is the inverse to hg forget (just as the opposite is true). However, attempting to re-add the directory itself did not work. Instead, I had to use hg add on each file:
hg st | egrep "^R" | sed -e "s/R //" | xargs hg add
Hope that helps. Note that in my case, there was nothing I legitimately wanted to remove. If you have files you definitely want to remove, adjust the grep accordingly.

Following your comment to jk, I checked hg forget. It seems to be just a shortcut for hg remove -Af, meaning that this is the real opposite of hg add.
Following that, if you've used hg remove -Af, then you should be able to revert that using hg add (I just tried it and seems to work).

The markers are stored in .hg/dirstate file. All you need to do i to get a one from before issuing hg remove -Af. It may look like this (not tested):
hg clone bad-repo orig-repo
cp orig-repo/.hg/dirstate bad-repo/.hg/dirstate
cd bad-repo
hg status
The last command should show the status from before removing files.

I removed a bunch of unmodified files:
hg remove *
This is what I had to do to get them back:
hg revert --all
Nothing else worked. Not hg add not hg add * nor hg revert *

Related

How to undo "hg qnew"?

I issued hg qnew without realizing that it includes any outstanding changes into the patch. I'd like to back that out and pick only specific changes using hg qrecord. How can I undo qnew?
Your answer definitely works — with newer Mercurial's you can use hg strip --keep to avoid doing the import step:
$ hg strip --keep .
$ hg qdelete patch-name
The --keep flag makes strip ignore the working copy while working, that is, it deletes the commit (like hg qpop would do) but it doesn't undo the changes to the files. After stripping you still have the patch in your series (unapplied) and you can then delete it.
I've found an anwer here:
hg qpop
hg import --no-commit .hg/patches/patch-name
hg qdelete patch-name
Please add a better way, if you know.
Update: Based on Aldo's answer, there is another way:
hg qnew test
# We can undo the above qnew as:
hg qrefresh -X '*'
hg qpop -f
hg qdelete test
If you just want to undo the latest qnew retaining all your local changes, one option is:
qcrefresh 123
hg qpop -f
hg qdelete <name of the patch>
Notice that 123 is just a random string: you are telling mercurial to only include the (hopefully nonexistsnt) 123 file in the current patch.
Newer versions of Mercurial When you issue will issue a warning about the fact 123 file does not exist, but this is exactly what we want here.
If you want to retain some of the changes in the current path, you can use the qcrefresh command from the crecord extension, which allows to graphically select the changes to be included in the current patch. You need to download it from Bitbucket, extract the archive and configure it in .hgrc:
[extensions]
crecord = <path/to/crecord/package>

Accidentally Working on the Wrong Named Branch in Mercurial

I have been making some changes to my working directory, and noticed that I have accidentally been working on the wrong branch. I have not committed anything yet, and I would like my next commit to go against another branch. What is the best way to do this?
The Shelve extension can give you grief, and this can be done entirely with Mercurial commands. Krtek almost had it but he used export instead of diff. Try this:
hg diff --git > ~/saved-work.patch
hg update --clean desiredbranch
hg import --no-commit ~/saved-work.patch
You should be able to just hg up otherbranch. It is important that you do not use the --clean option to hg up, either directly or via an alias as that will discard your uncommitted changes.
Another option is to use one of the extensions that provides hg shelve. The process would then be:
$ hg shelve --all
$ hg up otherbranch
$ hg unshelve
That will create a patch of your changes within the .hg directory, returning your working directory to a clean state, switch to the 'otherbranch', and then apply the saved patch.
I don't know if it is the best solution, but you can follow these steps :
1° hg diff --git > modifications.patch
2° hg update -C the_right_branch
3° hg patch modifications.patch
Maybe it's better to copy modifications.patch somewhere safe, just in case.
edit: update with diff instead of export. Thanks to the commenters.

Discard all and get clean copy of latest revision?

I'm moving a build process to use mercurial and want to get the working directory back to the state of the tip revision. Earlier runs of the build process will have modified some files and added some files that I don't want to commit, so I have local changes and files that aren't added to the repository.
What's the easiest way to discard all that and get a clean working directory that has the latest revision?
Currently I'm doing this:
hg revert --all
<build command here to delete the contents of the working directory, except the .hg folder.>
hg pull
hg update -r MY_BRANCH
but it seems like there should be a simpler way.
I want to do the equivalent of deleting the repo, doing a fresh clone, and an update. But the repo is too big for that to be fast enough.
Those steps should be able to be shortened down to:
hg pull
hg update -r MY_BRANCH -C
The -C flag tells the update command to discard all local changes before updating.
However, this might still leave untracked files in your repository. It sounds like you want to get rid of those as well, so I would use the purge extension for that:
hg pull
hg update -r MY_BRANCH -C
hg purge
In any case, there is no single one command you can ask Mercurial to perform that will do everything you want here, except if you change the process to that "full clone" method that you say you can't do.
hg up -C
This will remove all the changes and update to the latest head in the current branch.
And you can turn on purge extension to be able to remove all unversioned files too.
To delete untracked on *nix without the purge extension you can use
hg pull
hg update -r MY_BRANCH -C
hg status -un|xargs rm
Which is using
update -r --rev REV revision
update -C --clean discard uncommitted changes (no backup)
status -u --unknown show only unknown (not tracked) files
status -n --no-status hide status prefix
hg status will show you all the new files, and then you can just rm them.
Normally I want to get rid of ignored and unversioned files, so:
hg status -iu # to show
hg status -iun0 | xargs -r0 rm # to destroy
And then follow that with:
hg update -C -r xxxxx
which puts all the versioned files in the right state for revision xxxx
To follow the Stack Overflow tradition of telling you that you don't want to do this, I often find that this "Nuclear Option" has destroyed stuff I care about.
The right way to do it is to have a 'make clean' option in your build process, and maybe a 'make reallyclean' and 'make distclean' too.
If you're looking for a method that's easy, then you might want to try this.
I for myself can hardly remember commandlines for all of my tools, so I tend to do it using the UI:
1. First, select "commit"
2. Then, display ignored files. If you have uncommitted changes, hide them.
3. Now, select all of them and click "Delete Unversioned".
Done. It's a procedure that is far easier to remember than commandline stuff.

hg remove directory from repo?

I'd like to remove a directory and all the files in it from a repo.
I have removed all the files with hg remove, but how do I remove the directory itself?
Will it just automatically vanish once I commit all the removed files?
Yes. Because mercurial doesn't track directories at all, only files, it only creates directories that have files in them, and if someone hg updates to a revision any directories that become empty are automatically removed. So if you do:
hg remove directory/*
hg commit -m 'removed all files in directory'
hg update -r 0 # updates to a different revision
hg update tip # jump back to the tip
That last update would remove the directory. For everyone else it's even easier. When they hg update to your new changes their directory will just vanish (provided they have no uncommitted file in it).
hg remove dir
If you end up with empty directories and you want to get rid of them, an easy way is the purge extension. (add purge= under the [extensions] group in your .hrgc file to unlock).
You can then use
hg purge
to clean up the empty dirs... You must be careful with the purge command as it removes everything that is untracked in your repos. I strongly suggest you run a
hg purge -p
beforehand to see what the command will do ( -p will print a "test run" without doing anything.) Never forget the --help option! ;)
edit: I prefer using purge to hg update in succession as updating triggers rebuilds in my IDE if it is open (and it's a good bet it is when I do that). hg purge will probably be smoother. And you can use --all to include ignored files too (must be careful though).
To remove a directory, Just do
hg remove <dir>
hg commit -m "..."
This will remove the directory and all files under it.

How can I recover a removed file in Mercurial (if at all)?

Accidentally, by using a GUI as opposed to CLI, I removed every file in a Mercurial project.
I recovered with Revert ok and lost some work, which as I have time machine I could easily get back. But is there a way of un-remove/undelete such files? Trawled through the manual and googled but cannot see anything. Any plugins?
I am probably answering my own question here but the files were gone from the directory and were not in the trash to recover so I am assuming Remove is irrevocable?
p.s. I know that hg forget or hg remove -Af will remove without deleting from the directory but my question has to do with the error I made as opposed to cool thinking the action through.
First, use hg grep to find the deleted file you wish to recover. The output of this command will show you the last revision for which the file was present, and the path to the deleted file.
Second, run hg revert -r <revision number> <path to deleted file>
The deleted file will now be in your working copy, ready to be committed back into head.
Quote from comment:
I set up a repository, committed all, Removed and then committed again
If this is the case then you just need to update the working directory to the previous revision:
$ hg update -C -r-2
Note the negative revision number. If the files you deleted aren't in the previous revision, you can find them by using:
$ hg log -v
For Mercurial 1.6 and above
If you know the name of the delete file you can find its revision easily with:
hg log -r "removes('NAME.c')"
This will give you the revision in witch a file called NAME.c (in the root) is deleted.
Then you can revert the file to the previous revision with (like other answers):
hg revert -r <revision number> <path to deleted file>
You can use a file name pattern instead to adapt to what you know, for example you can use **/NAME.c to search in all directories. You can read about it in File Name Patters. And use this link to know about the new revset specifications.
Well this worked for me.
hg revert -r revision pathToTheFile
An addition to the accepted answer - this is faster if you want to undo all removals in a commit. I deleted a large folder with a few hundred files in it and did hg addremove, which was not at all my intent, so had to undo all of those deletes.
Using Find deleted files in Mercurial repository history, quickly? + xargs + tr, revert all revision -3 removals to the version from revision -4:
hg log -r -3 --template "{rev}: {file_dels}\n" | tr ' ' '\n' | xargs hg revert -r -4
Note that this will fail if any of your files have spaces in the name; http://hgbook.red-bean.com/read/customizing-the-output-of-mercurial.html doesn't appear to have any templates where {file_dels} is split by \n at the moment.
You can undo the last commit on a repo with hg rollback. There's only one level of rollback available, so if you did the remove with more than one commit, this won't completely undo your change. This only works on your local repository, so if you've pushed you won't be able to undo it in the remote repo.
You can remove committed revisions using the hg strip command, which is provided by the mq (Mercurial Queues) extension. This should give you back your files.
Make a backup before trying that out, because it will alter Mercurial's database of changesets.
The following worked for me.
hg revert -r <Revision Number> <File Name>
(Optional, to revert all files)
hg revert -r <Revision Number> --all
The below method is straightforward and so stupid that it cannot go wrong.
If you have deleted or renamed multiple files, it will be ok.
hg clone mydirectory mydirectory1
and now you start mc (or Far Manager) and compare what it was vs what it has become.
when it's done, just delete mydirectory1.