Check in single file with Mercurial? - version-control

Let's say you do hg status and you have three files modified. I know how to check in all three files (hg commit). But how can you check in (and then hg push) just one of the modified files?

Please check the output of hg help commit which reveals that you can do
hg commit foo.c
if you just want to commit a single file. This is just like Subversion and many other systems — no hocus-pocus :-)

Just do:
hg ci -I path/to/file -m "commited only one file"
That commits only one file, and you can push it, and none of the uncommitted changes will be affected.

On the off chance you're running on Windows, TortoiseHG (a graphical Mercurial interface) lets you select which files to commit every time.

Related

Reclone mercurial subrepo

This seems like it should be really easy, but I can't find anything that directly addresses it in my searching. I have a mercurial repo with subrepos, I have deleted one of the subrepos (the whole folder). How do I reclone that subrepo now? I could do it manually but surely there's a hg command that does the job?
Use the Clean parameter in your hg update to make it pull the subrepo again.
hg update -C
Beware that clean will discard any uncommitted changes in your working directory, so shelve or commit anything you want to keep before doing it.

Mercurial: How do you undo changes?

When using Mercurial, how do you undo all changes in the working directory since the last commit? It seems like this would be a simple thing, but it's escaping me.
For example, let's say I have 4 commits. Then, I make some changes to my code. Then I decide that my changes are bad and I just want to go back to the state of the code at my last commit. So, I think I should do:
hg update 4
with 4 being the revision # of my latest commit. But, Mercurial doesn't change any of the files in my working directory. Why not?
hg revert will do the trick.
It will revert you to the last commit.
--all will revert all files.
See the link for the Man Page description of it.
hg update is usually used to refresh your working directory after you pull from a different repo or swap branches. hg up myawesomebranch. It also can be used to revert to a specific version. hg up -r 12.
An alternative solution to hg revert is hg update -C. You can discard your local changes and update to some revision using this single command.
I usually prefer typing hg up -C because it's shorter than hg revert --all --no-backup :)
hg revert is your friend:
hg revert --all
hg update merges your changes to your current working copy with the target revision. Merging the latest revision with your changed files (=current working copy) results in the same changes that you already have, i.e., it does nothing :-)
If you want to read up on Mercurial, I'd recommend the very awesome tutorial Hg Init.
hg revert --all
and then
hg pull -u
works for me

Delete all local changesets and revert to tree

I'm using Mercurial and I've got into a terrible mess locally, with three heads. I can't push, and I just want to delete all my local changes and commits and start again with totally clean code and a clean history.
In other words, I want to end up with (a) exactly the same code locally as exists in the tip of the remote branch and (b) no history of any local commits.
I know hg update -C overwrites any local changes. But how do I delete any local commits?
To be clear, I have no interest in preserving any of the work I've done locally. I just want the simplest way to revert back to a totally clean local checkout.
When the simplest way (a new hg clone) isn't practical, I use hg strip:
% hg outgoing -l 1
% hg strip $rev # replace $rev with the revision number from outgoing
Repeat until hg outgoing stays quiet. Note that hg strip $rev obliterates $rev and all its descendants.
Note that you may have to first enable strip in your Mercurial settings.
PS: an even smarter approach is to use the revset language, and do:
% hg strip 'roots(outgoing())'
You'll want to make a local clone where you preserve only the changesets that are also present in the remote repository. Use TortoiseHg, hg log or similar to figure out which of your revisions is that lastest revision you didn't make (the one before the mess started). Using hg outgoing can help here -- it will list all the changesets you made -- pick a revision number earlier than any of those.
If the target revision is called good and your clone is called foo, then do:
hg clone -r good foo foo-clean
This will be a fast, local operation -- there is no reason to download everything again. The foo-clean clone will only contain changesets up to revision good. You can now replace foo-clean/.hg/hgrc with foo/.hg/hgrc in order to preserve your repository-local settings such as the default push/pull path.
When you are satisfied that foo-clean has everything you need from foo, then simply delete foo and rename foo-clean to foo. Do a hg pull to get any new changesets from the remote repository into your clone and continue like normal.
If nobody has pushed new changesets to the remote repository, then it is very simple to determine which revision you want to use as good above: hg id default will tell you the ID of the tip in the remote repository.
Ok. So just delete all the local stuff, hg init the new local repository and hg pull the latest tip you have. Don't forget to hg update after this.
You may use
hg strip revision
to kill any revision and its subtree in your local repository.
https://www.mercurial-scm.org/wiki/Strip
But don't try to use it for anything that has been already pushed.
Just delete everything you have on your local system and re-clone the remote repo.
hg strip `hg out --template "{rev} {author}\n" | grep YOUR_AUTHOR_NAME | cut -d " " -f 1`
does the trick for me.
It strips all revisions that aren't pushed to the default repository which are created with your author name.
You can also use this style to make it not checking with the default repository but with another Repository
hg strip `hg out OTHER_REPO_ALIAS --template "{rev} {author}\n" | grep YOUR_AUTHOR_NAME | cut -d " " -f 1`
If you are using TortoiseHg, one simple way to get out of a (small) mess is to first update to the latest revision, then select your changesets and initiate "merge with local". When the merge dialogue appears, simply click the little '+' icon to reveal some extra options, one of which is "discard changesets from merge target (other) revision". Doing this will mean your changesets will still be in the repo and get pushed, but will have no effect, because they will be discarded in the merge. If you have a lot of changesets spanning many heads, you might not want to pollute the repo this way, but it's a simple fix and worth considering if the changesets you are discarding contain data that you may later want to reference.

Committing only some files in Mercurial

Many times I'm making two different changes to files in my repository, I want those changes to be treated as two consecutive commits.
For example, in repository
prog.c
prog.h
README.txt
While fixing a bug prog.c and prog.h, I fixed a typo in README.txt. Now I want to commit the change to prog.c with its own commit message, and the change to README.txt afterwards.
In git, I could easily do that with the index
git add prog.c prog.h
git commit -m 'bug #1234'
git commit README.txt -m 'some typos fixed'
What's the best way to do that in Mercurial?
Clarification: I used (before the edit) a toy example where each changeset spans over a single file. But I want the general answer, what should I do when there are many files in each changeset.
hg commit -m "bug #1234" prog.c prog.h
then
hg commit -m "some typos fixed" README.txt
I LOVE the crecord mercurial extension for this purpose: it gives me file by file (and chunk by chunk, and line by line) control over what exactly I want in this commit.

Mercurial: Merging one file between branches in one repo

When I have two branches in Hg repo, how to merge only one file with another branch, without having all other files from changeset merged?
Is it possible to merge only certain files, instead of whole changeset?
WARNING: a "dummy merge", as is recommended by #Martin_Geisler, can really mess you up, if later you want to do a true merge of the two branches. The dummy merge will be recorded, and say that you merge into the branch you did the dummy merge to -- you will not see the changes. Or if you merge into the other branch, the changes on that other branch will be undone.
If all you want is to copy an entire file from one branch to another, you can simply do:
hg update -r to-branch
hg revert -r from-branch file
hg ci -m 'copied single file from from-branch to to-branch
If you want to select different parts of that file, then "hg record" is useful.
I just did this on my home directory .hgignore.
If both branches have made changes to a file that you want to keep, a dirty trick would be to create a merge of the two branches using hg merge, possibly/probably on still another branch, check that in, and then copy a single file between the merge and the to-branch:
hg update -r to-branch
branch merge-branch
hg merge -r from-branch
hg ci -m 'temp merge to be discarded"
hg update -r to-branch
hg revert -r merge-branch single-file
hg ci -m 'merged single-file from from-branch to to-branch"
hg strip merge-branch
It is worth mentioning: the way to "copy a single file between branches" (or revisions, or from revision to merge, or....) is "hg revert". I.e.
hg update -r Where-you-want-to-copy-to
hg revert -r Where-you-want-to-copy-from file-you-want-to-copy
...
hg ci
For some reason I, and some of my coworkers, find this VERY confusing. "revert"=="copy" ... makes sense for some usage patterns, but not all.
Nope. Mercurial works on a changeset basis.
But you can do a "dummy merge" where you ignore the incoming changes from one of the branches. Before you commit you could then revert selected files to whatever state you want:
% HGMERGE=internal:local hg merge # keep my files
% hg revert --rev other-branch a.txt # update a.txt to other branch
% hg commit -m 'Dummy merge to pick a.txt from other-branch.'
Maybe that will help you a bit.
One fairly clean way of getting the desired result is to do it in two steps: first use graft, then second use histedit.
Say this is the starting point and you need to select some portions of C and D to "merge" after E:
A---B---C---D
\
-E
Then you would graft C and D on top of E:
A---B---C---D
\
-E--C'--D'
Then use hg histedit to edit C' and D'. During the edit you can make any changes you want, but in this case you would just revert any unwanted files, (or even portions of them).
(Note that histedit edit works by temporarily updating your working folder to match the content of the given changeset as though it were not committed yet. So you can easily revert unwanted files and then hg histedit --continue which will effectively replace the edited changeset.)
So the final result would be:
A---B---C---D
\
-E--C''--D''
Where the '' revisions were modified as required.
I would say this approach is more beneficial when you have large changesets that probably should have been multiple smaller commits in the first place; this approach allows you to "disentangle" only the parts that you need. Using this for just a single file would be fine but could be overkill.
I would just use an external tool like vimdiff to diff the two files that I want to merge and then merge them. The advantage of this is that you can do selective editing on parts of the file. E.g:
hg update -r branch-merging-to
hg extdiff -p vimdiff -r branch-merging-from file-I-am-merging
To do this you need to enable the external tools in your .hgrc, which just means adding these lines:
[extensions]
hgext.extdiff =
If you are using an IDE:
Merge the old branch with new branch
Go inside the the IDE and remove the unwanted changes
Generate the diff file
Update and clean the new branch
Apply the diff in the new branch