My setup is as follows: I've a repository R with default branch and another named branch "a". On both branches, there exists a file "myFile.txt". I've cloned repository R, yielding the clones R_1 and R_2. Now I "hg up default" in R_1 and "hg up a" in R_2. In both working directories of R_1 and R_2, I "echo 'a' >> myFile.txt". Subsequently, I "hg ci -m 'fix in default' and "hg ci -m 'fix in a' in R_1 and R_2, respectively. Next, I "hg push" in both directories.
In R, I can now "hg up default" and "hg up a" and see the fix in both branches (which is expected). However, hg log appears to only reflect one changeset with one commit message. My feeling is that somehow Mercurial figures out that the changesets are equivalent and "merges" them, which is probably the right thing to do for efficiency.
Is this correct? How does it work? Which commit-message is actually chosen? This may sound like an artificial setup but I'm actually encountering this situation in a real setup (obviously more complicated) and want to make sure that nothing bad happens.
The following is a part of hg help log:
Note:
for performance reasons, log FILE may omit duplicate changes made on
branches and will not show removals or mode changes. To see all such
changes, use the --removed switch.
So you should be able to see both changesets this way.
I'm not sure which change will be shown and which will not. This will probably depend on how the history is built up in your repository. Since this is not mentioned, you probably can't rely on that.
Related
According to Mercurial's help page, hg incoming (and the "check for incoming changes" button in tortoiseHG does:
"Show new changesets found in the specified path/URL or the default
pull location. These are the changesets that would have been pulled by
'hg pull' at the time you issued this command."
My question is: What is the point of this behavior? In what situation would I want to not just pull? If I wanted to see what changesets were pulled, I could just look at the log.
Have I been using mercurial wrong my whole life?
There are various scenarios in which it can be useful. I'm just listing down my use cases hre.
a) I do hg incom to make sure I can pull and get ready to rebase my commits if there is anything on the remote.
b) I use hg log -G followed by hg incom quite often when doing offline peer review to make sure I don't pollute my repo and to know which rev to strip (if required) after the pull. FWIW, as a result of hg incom I have asked people to change their commit message even without pulling their changes for review.
I have some uncommitted changes C in my repo. I would like remember that changes in any way and get clean code (without that changes), make a little change and commit it. Now, I would like to recover my changes C and continue working on it. I know that I can deal with it using a lot of ways, but that ways are irritating. How to do it using mercurial?
So, to be more precise I need something like a stack:
Working on the code. Remember changes C on the stack.
hg update --clean
Make a change C2. Commit it.
Pop from stack a changeset C and work on it. But, now the repositorium contains committed change C2 and uncommitted C. It may cause that I need to merge but I expect that this merge will be invisible from the point of view repositorium.
While you certainly can work with mercurial queues, there's IMHO an easier and nicer way: change your default phase to secret and work with those commits like normal commits. Commits in phase secret are mutable and will not be exposed by push and pull commands acting on the repo.
This process has the advantage that you do not need to change your workflow - whether you work with commits you want to share (phase draft or public), or whether you still consider them work-in-progress and keep them locally only.
Additionally if you enable the evolve extension, you gain several benefits: it becomes even easier to amend commits and evolve (thus rebase) all child commits which depend on it.
The big advantage over the use of the mercurial queues is that you can make full use of the inbuild merge features - thus if the underlaying code changes, rebasing the new changesets is WAY easier and natural than using queues and hg shelve.
See the introduction to hg phases and changeset evolution which needs the evolve extension.
Enable the Mercurial Queues extension in your mercurial.ini or .hgrc file:
[extensions]
mq =
Then you can,
hg qnew save # save work in progress as a temporary commit
hg qpop # remove that commit
Make some more changes....
hg ci -m "new changes"
hg qpush # push the saved commit back.
hg qfinish -a # convert all temp commits to full commits.
You can also enable the shelve extension:
[extensions]
shelve =
Then you can:
hg shelve # "put away" current uncommitted changes.
*do other work*
hg unshelve # bring the shelved changes back
See hg help mq and hg help shelve for more info.
I've tried shelve, mq as described in other answers but to be honest I generally stick with:
hg diff > saved.patch # This assumes you've not aliased diff to a UI!!!
hg update -C
.. work
hg patch -f --no-commit saved.patch # I alias this for less typing
.. continue
Less book-keeping involved, its never gone wrong unlike shelve, and the patch itself is more easily portable. Just use common-sense and either make sure the patch applies fully, or use the --partial option and manually complete the patch.
I cloned a project to my local directory and made a lot of changes. My current goal is to push my changed code to a new branch in the remote repository. And eventually this new branch will be merged back to default.
My problem is, because of some stupid effort in the past a few weeks to try to recover some missing files, I end up with a few branch names that I don't want being shown in public.
Here's what I have:
$hg branches
dev-v02 197:xxxxx
dev2 194:xxxxx
dev 183:xxxxx
qa 189:xxxxx
$hg branch
dev-v02
My question is, if I push my current branch dev-v02 to the remote repository by "hg push --new-branch", and this branch later get merged back to default, will the unwanted branches show up in history of default? And if so, is there a safe way to purge them?
I do NOT want to discard my changes. I just don't want the unwanted branches showing up in "hg branches" or "hg his" commands by whoever later clones the project from the remote repository. I searched online and found "hg strip" but I couldn't tell from the article if it would also remove the changes I've made. Thanks.
Edit: I just cloned my local repository by "hg clone -r 197 original-dir dest-dir" as suggested by both kevin and chessbot and now hg branches shows:
dev-02 192:xxxxx
qa 187:xxxxx (inactive)
I guess "qa" remains because I had pushed it to the remote as a QA branch and closed it later, and I just have to live with that. I will push from this new directory from now on. Thanks guys for your help.
Try hg push --new-branch -b dev-v02 to specify that you're pushing only that branch.
(See: https://www.mercurial-scm.org/repo/hg/help/push)
Another thing you could do: Clone the repository locally on your machine, strip out the branches you don't want, and then push that clone to the server. Then you retain your history locally without pushing it to everyone else.
It depends.
Branches are permanently associated with a commit. The branch is part of the commit, and contributes to the hash. Changing the branch of a commit in the past would alter all commit hashes from that point forward. This is quite different from Git, where a branch is little more than an ephemeral pointer to a HEAD. Such pointers are implemented in Mercurial as bookmarks.
If the unwanted branches appear on commits which are (ancestors of) commits you want to publish, there is very little you can do, short of recreating the history with all-new hashes. This could (for instance) be done with hg export and hg import, along with local cloning and (probably) a certain amount of shell scripting. More efficiently, you could use the convert extension to automate the process. Since this changes commit hashes, it will likely cause serious problems if any of the commits have already been distributed publicly.
If you have no interest in sharing the offending commits, you can simply not publish them. This can be done with selective pushing. However, since you'll always have to manually exclude those commits every time you push, it's probably safer to clone and strip (or clone selectively with the -r flag). You can then push from your partial clone with impunity. Assuming you have a sufficiently recent version of Mercurial, you can also force the commits into the secret phase, so that they will not be pushed:
hg phase -fs revisions
You don't want to use hg strip, because it permanently removes the commits from the history (see Editing History in the Mercurial wiki)
If I were you, I would close the branches instead:
hg up -C badbranch
hg commit --close-branch -m 'close badbranch, this approach never worked'
hg up -C default
(source: Pruning branches in the Mercurial wiki)
After closing a branch, hg branches doesn't show it anymore.
If you do want to see closed branches, use the -c parameter:
hg branches -c
Disadvantage:
hg his still shows closed branches.
You could use the -b parameter though, to show only the default branch:
hg his -b default
what would be an equivalent mercurial command (or workflow) for
git reset --mixed HEAD^
or
git reset --soft HEAD^
i.e. I want leave the working tree intact but get the repository back into the state it was before the last commit. Surprisingly I did not find anything useful on stackoverflow or with google.
Note that I cannot use
hg rollback
as I've done some history rewriting using HistEdit after the last commit.
Added to clarify:
After some rebasing and history editing I had ended up with A<--B<--C. Then I used HistEdit to squash B and C together, obtaining A<--C'. Now I want to split up the commit C' (I committed the wrong files in B). I figured the easiest way to do this was to get the repository back to state A (which technically never existed in the repository because of all the rebasing and history editing before hand) and the working tree to the state of C' and then doing two commits.
The right way to replicate git reset --soft HEAD^ (undo the current commit but keep changes in the working copy) is:
hg strip --keep -r .
-1 will only work if the commit you want to strip is the very last commit that entered the repository. . refers to the currently checked out commit, which is the closest equivalent Mercurial has to Git's HEAD.
Note that if . has descendants, those will get stripped away too. If you'd like to keep the commit around, then once you have the commit ID, you can instead:
hg update .^
hg revert --all -r <commit id>
This will update to the commit's parent and then replace the files in the working copy with the versions at that commit.
I believe the more modern and simpler way to do this now is hg uncommit. Note this leaves behind an empty commit which can be useful if you want to reuse the commit message later. If you don't, use hg uncommit --no-keep to not leave the empty commit.
hg uncommit [OPTION]... [FILE]...
uncommit part or all of a local changeset
This command undoes the effect of a local commit, returning the affected
files to their uncommitted state. This means that files modified or
deleted in the changeset will be left unchanged, and so will remain
modified in the working directory.
If no files are specified, the commit will be left empty, unless --no-keep
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