Mercurial: how to change between branches without updating? - mercurial

I creaated a branche, let's say new_branch.
I would like to change to the default branch without updating. This is, keeping the changes in my working copy (in new_branch) even if there were common files in default.
Not doing this:
hg update default <-- don't want to update! Just to change the branch
Thank you.

Be careful. This answers your question as asked, but I'm not convinced it's what you want.
debugsetparents will do it. But as noted, be careful.
~$ hg help debugsetparents
hg debugsetparents REV1 [REV2]
manually set the parents of the current working directory
This is useful for writing repository conversion tools, but should be used
with care.
Returns 0 on success.
options:
--mq operate on patch repository
use "hg -v help debugsetparents" to show global options
You want to set the parent to the last revision in the default branch.
You'll also need to use hg debugrebuildstate to fix the internal state (most obviously, its notion of which branch you're on).
hg debugsetparents default
hg debugrebuildstate

Related

Mercurial revision/revset for unpushed changesets

What's the best way to specifiy a revision/revset for -r options that includes your unpushed changesets, this is useful for example to clone locally or create a bookmark in a repo with only changesets that have been pushed.
This is often needed when you need to switch to working on something else in the middle of work that you are not ready to push.
There are a few revsets that can be used to get this information:
first(outgoing()) will refer to the first changeset that has not been pushed to your default push location.
first(!public()) will refer to the first changeset that has not been pushed anywhere.
You might want to combine these with the p1([set]) predicate if you want to update to the parent of those changesets to start a new anonymous branch for your new fix.
Also, before you start work on the fix, you might want to set the phase of the changesets of the unfinished work to be secret to prevent them from being accidentally pushed.
You can do this using hg phase --secret --force -r "!public()" and use hg phase --draft -r "secret()" to switch them back again (although this would change all secret changesets to draft so you'd need to be more specific if you use phases on other branches)
If you want revset(s), you have to use outgoing([path]) (or ! outgoing([path]) as changesets specification.
[path] is URL of the repository (or alias???), if not default
If i understand correctly you want the equivalent of git stash, this is implemented as an extension on mercurial.
Check out the wiki page. https://www.mercurial-scm.org/wiki/ShelveExtension
EDIT: I have to mention, I don't use this workflow, in your case I would make a "dev" branch and commit regularly (even WIP commits), when stable you can merge to master.

Stuck with two default branches in Mercurial after broken branch commits

I've come across a difficulty at work (as presented in the simplified sketch below). During the creation of a branch the default for some reason got stuck as a parent to a branch, but still keeping a default branch separately (that is the default branch we're using going forward). This has left us with two default branches.
Someone mistook how to commit changes before branching, so we ended up merging changes made in branch1 into a branch2.
I've been looking into Mercurial: the definitive guide to see if this is a resolvable issue, but couldn't find out what commands regarding backing out or closing that would help. The easiest way would be if it's somehow possible to rename the left-over default branch.
What is the best and/or easiest way to resolve this?
I'm preparing the merging of development branches into the correct default branch and want to have this headache fixed before I start any major merging that could make it even more difficult to fix this in the future.
Remember that the branch names are just labels put on the commits — there's nothing really broken about your graph. The branch names doesn't affect what happens when you merge, only the file content is important when merging.
That being said, you can close the extra head on default, the one below branch1:
$ hg update "min(heads(branch(default)))"
$ hg commit --close-branch -m "closing this head"
That will leave a dangling close changeset in your graph, which is fine. The close changeset will hide the head from hg heads and commands such as hg merge will then no longer suggest to merge with this head.
Old question, but I though this might be useful to someone.
..this occurs when a single branch diverges, usually when someone does an hg push -f instead of pulling and updating. In your case, the forced head happens to be also on another branch, but this can happen on a single branch as well. My solution would be to let it sit until the branches are merged -- at least, if there's a plan to merge them at some point. This solution is more clean than closing the erroneous head, in my opinion.
However, doing hg update default will take you to the newest commit with the 'default' name. Although I think this idea is the right one in your case, this is because the "default" that you actually want is the newest commit with the "default" branch name, so there should be no problem.
However, if the erroneous head were newer, hg update default would take people to the erroneous head, which could be quite confusing.
In either case, this would resolve the issue:
hg update <revision number of correct 'default' head>
hg merge <branch the erroneous 'default' head is on>
So in this case, hg update default will update to the erroneous head:
1-2(default)
\
3(default)-4(branch1)
You would need to do:
hg update 2
hg merge branch1
# results in this graph:
# 1-2---5(default)
# \ /
# 3-4(branch1)
whereas below, hg update default will update to the one you actually want anyways:
1-2------------5(default)
\
3(default)-4(branch1)
..and you could just ignore the erroneous default, because it won't affect anyone. ..then, once someone does an hg update default; hg merge branch1, the erroneous head will silently disappear, because at that point it's an ancestor of the erroneous 'default'. ..which would result in something like this:
1-2-5----------11(default)
\ /
3-4-[...]-10(branch1)
..you could also do a useless commit on your desired default, and then it will be the newest, and it would be the one people get when they do hg update default, but I don't really like having junk commits in the history.

Mercurial: how to amend the last commit?

I'm looking for a counter-part of git commit --amend in Mercurial, i.e. a way to modify the commit which my working copy is linked to. I'm only interested in the last commit, not an arbitrary earlier commit.
The requirements for this amend-procedure are:
if possible, it should not require any extensions. It must not require non-default extensions, i.e. extensions which do not come with an official Mercurial installation.
if the commit to amend is one head of my current branch, no new head should be created. If the commit is not head, a new head may be created.
the procedure should be safe in a way that if for whatever reasons the amending fails, I want to have the same working copy and repository state restored as before the amending. With other words, if the amending itself can fail, there should be a fail-safe procedure to restore the working copy and repository state. I'm referring to "failures" which lie in the nature of the amend-procedure (like e.g. conflicts), not to file-system-related problems (like access restrictions, not being able to lock a file for writing, ...)
Update (1):
the procedure must be automatable, so it can be performed by a GUI client without any user interaction required.
Update (2):
files in the working directory must not be touched (there may be file system locks on certain modified files). This especially means, that a possible approach may at no point require a clean working directory.
With the release of Mercurial 2.2, you can use the --amend option with hg commit to update the last commit with the current working directory
From the command line reference:
The --amend flag can be used to amend the parent of the working directory with a new commit that contains the changes in the parent in addition to those currently reported by hg status, if there are any. The old commit is stored in a backup bundle in .hg/strip-backup (see hg help bundle and hg help unbundle on how to restore it).
Message, user and date are taken from the amended commit unless specified. When a message isn't specified on the command line, the editor will open with the message of the amended commit.
The great thing is that this mechanism is "safe", because it relies on the relatively new "Phases" feature to prevent updates that would change history that's already been made available outside of the local repository.
You have 3 options to edit commits in Mercurial:
hg strip --keep --rev -1 undo the last (1) commit(s), so you can do it again (see this answer for more information).
Using the MQ extension, which is shipped with Mercurial
Even if it isn't shipped with Mercurial, the Histedit extension is worth mentioning
You can also have a look on the Editing History page of the Mercurial wiki.
In short, editing history is really hard and discouraged. And if you've already pushed your changes, there's barely nothing you can do, except if you have total control of all the other clones.
I'm not really familiar with the git commit --amend command, but AFAIK, Histedit is what seems to be the closest approach, but sadly it isn't shipped with Mercurial. MQ is really complicated to use, but you can do nearly anything with it.
GUI equivalent for hg commit --amend:
This also works from TortoiseHG's GUI (I'm using v2.5):
Swich to the 'Commit' view or, in the workbench view, select the 'working directory' entry.
The 'Commit' button has an option named 'Amend current revision' (click the button's drop-down arrow to find it).
||
||
\/
Caveat emptor:
This extra option will only be enabled if the mercurial version is at least
2.2.0, and if the current revision is not public, is not a patch and has no
children. [...]
Clicking the button will call
'commit --amend' to 'amend' the revision.
More info about this on the THG dev channel
I'm tuning into what krtek has written. More specifically solution 1:
Assumptions:
you've committed one (!) changeset but have not pushed it yet
you want to modify this changeset (e.g. add, remove or change files and/or the commit message)
Solution:
use hg rollback to undo the last commit
commit again with the new changes in place
The rollback really undoes the last operation. Its way of working is quite simple: normal operations in HG will only append to files; this includes a commit. Mercurial keeps track of the file lengths of the last transaction and can therefore completely undo one step by truncating the files back to their old lengths.
Assuming that you have not yet propagated your changes, here is what you can do.
Add to your .hgrc:
[extensions]
mq =
In your repository:
hg qimport -r0:tip
hg qpop -a
Of course you need not start with revision zero or pop all patches, for the last just one pop (hg qpop) suffices (see below).
remove the last entry in the .hg/patches/series file, or the patches you do not like. Reordering is possible too.
hg qpush -a; hg qfinish -a
remove the .diff files (unapplied patches) still in .hg/patches (should be one in your case).
If you don't want to take back all of your patch, you can edit it by using hg qimport -r0:tip (or similar), then edit stuff and use hg qrefresh to merge the changes into the topmost patch on your stack. Read hg help qrefresh.
By editing .hg/patches/series, you can even remove several patches, or reorder some. If your last revision is 99, you may just use hg qimport -r98:tip; hg qpop; [edit series file]; hg qpush -a; hg qfinish -a.
Of course, this procedure is highly discouraged and risky. Make a backup of everything before you do this!
As a sidenote, I've done it zillions of times on private-only repositories.
Recent versions of Mercurial include the evolve extension which provides the hg amend command. This allows amending a commit without losing the pre-amend history in your version control.
hg amend [OPTION]... [FILE]...
aliases: refresh
combine a changeset with updates and replace it with a new one
Commits a new changeset incorporating both the changes to the given files
and all the changes from the current parent changeset into the repository.
See 'hg commit' for details about committing changes.
If you don't specify -m, the parent's message will be reused.
Behind the scenes, Mercurial first commits the update as a regular child
of the current parent. Then it creates a new commit on the parent's
parents with the updated contents. Then it changes the working copy parent
to this new combined changeset. Finally, the old changeset and its update
are hidden from 'hg log' (unless you use --hidden with log).
See https://www.mercurial-scm.org/doc/evolution/user-guide.html#example-3-amend-a-changeset-with-evolve for a complete description of the evolve extension.
Might not solve all the problems in the original question, but since this seems to be the de facto post on how mercurial can amend to previous commit, I'll add my 2 cents worth of information.
If you are like me, and only wish to modify the previous commit message (fix a typo etc) without adding any files, this will work
hg commit -X 'glob:**' --amend
Without any include or exclude patterns hg commit will by default include all files in working directory. Applying pattern -X 'glob:**' will exclude all possible files, allowing only to modify the commit message.
Functionally it is same as git commit --amend when there are no files in index/stage.
Another solution could be use the uncommit command to exclude specific file from current commit.
hg uncommit [file/directory]
This is very helpful when you want to keep current commit and deselect some files from commit (especially helpful for files/directories have been deleted).

How do I check for potential merge/rebase conflicts in Mercurial?

Is there a simple way to check if a merge/rebase will yield file conflicts, without actually performing the merge/rebase?
I want to be able to decide whether to:
rebase if the touched file set (mine vs. theirs) are different
merge if we've been messing with the same files.
Since a bad merge (caused by resolving conflicts the wrong way by human error) is easier to detect and reverse if I do a merge of two heads, rather than having done rebase. Especially if I push my changes and than later realized that something was messed up.
(It's not possible to always check everything beforehand, as we don't have a totally comprehensive test-suite.).
And.. I'm running Windows. :)
So, with some aid from Martin's answer, I've come up with the rebaseif extension, which does what I want.
Essentially, it tries to rebase using the internal merge tool, if that fails (which it does for any conflict), it aborts and does a merge with the user's preferred tool.
See https://bitbucket.org/marcusl/ml-hgext/src/tip/rebaseif.py for details.
Update
In recent months, I've gone back to just do a merge, since it's inherently safe. A non-conflict rebase might still muck things up since dependent files can affect the change. (i.e. a rebase loses information of how the code looked before merge).
As the rebaseif author, I recommend to use plain old merge instead. :)
There is no reason to use hg merge if the changes overlap and hg rebase otherwise since hg rebase make a merge internally and will let you resolve it using the same tools as hg merge.
As for testing if a merge or rebase will cause conflicts, then you can use
$ hg merge --tool internal:merge
in Mercurial 1.7 to override your normal merge tool configuration. (The --tool internal:merge part is new, use --config ui.merge=internal:merge in earlier versions of Mercurial.)
Following the merge,
$ hg resolve --list
will tell you the results and you will get back to where you started with
$ hg update --clean .
You can see if two changesets, REV1 and REV2, affect any of the same files doing something like:
(hg status --change REV1 --no-status ; hg status --change REV2 --no-status) | sort | uniq --repeated
If that has any output then the same file is touched in both revisions.
That could easily be made a shell script like:
#!/bin/sh
(hg status --change $1 --no-status ; hg status --change $2 --no-status) | sort | uniq --repeated
which could be run as any of these:
./find_overlaps c8f7e56536ab d9e2268e20b9
./find_overlaps 1 33
If you really wanted to get fancy you could tweak the script to automatically run merge or rebase depending on whether or not any lines were found.
If you're on windows my god help you. :)

How do I unadd a not yet committed file I have added?

I typed in hg add and I am brand new to mercurial and the result of this was a bunch of dll's exe's pdb's etc all got added
Nothing's been committed yet and I basically want to undo the add.
the documentation for hg forget is not very clear not sure if that is want I want
How do I undo the add before the next commit
I do have some real files that need adding so after I can undo the add I will use add with the exclude flag
Thanks
Check out this mercurial tip. To cite the link - if you have accidentally added a file, the way to undo that (changing its status from A back to ?, or unknown) is hg revert. For example, if you just ran hg add and realized that you do not want files foo or bar to be tracked by Mercurial:
hg revert foo bar
Either revert or remove can be used to un-add not yet commited stuff. However, they both have other uses too, so for clarity hg forget was (re-)added in 1.3, and despite its name it might be easier to remember.
If you are using a Unix like system i believe the best option is to run
hg status -an0 | xargs -0 hg revert
Two tips for these sorts of situations:
If nothing has been commited at all, just delete .hg and start over with hg init.
If you do something terrible to your repository and can't seem to figure out how to undo it, (and hg update -C or revert all won't fix), consider cloning the repository at the last good spot.