I created some branch (call it brnch) from default and made some commits there. Now there is a need to cherry-pick all diffs from the branch to some release branch for hotfix. Is something like that possible in mercurial?
^ ^
| |<--- brnch
| - commit3
| |
| - commit2
- |
| |
| - commit1
- |
| |
| |
|--
|
-
default
Is it possible to create a patch from all commmits in the branch brnch (commit1, commit2, commit3) and apply to some another branch (release hot-fix in my case)?
See hg help export and hg help import.
Examples:
hg export branch(brnch) -o brnch.patch # export everything on brnch
hg export -r commit1 -r commit2 -o cherrypick.patch # cherrypick two changesets
hg update <somewhere_else>
hg import brnch.patch # import the revisions in the patch
hg import --no-commit brnch.patch # import (but don't commit) revisions.
hg commit -m hotfix # commit changes as one commit with "hotfix" description.
You can simply achieve that by means of hg rebase.
For simplicity I will assume that you want to see everything from commit1 onward in your brnch see put into your release branch. Thus checkout the release branch and then rebase your brnch commits:
hg update release
hg rebase --source commit1 --collapse --keep --dest . --message "bla bla something fixed"
Alternatively you can give a revision range. If the commits you want to see collapsed are non-linear in the brnch branch, then you will need to resort to graft (which is cherry-picking individual commits) - and you can collapse (hg fold) all grafted changesets subsequently in your release branch, provided you keep them initially in phase draft.
Related
I have a mercurial repo with the following history (most recent commit at the top) on a feature branch:
mergeDefaultA
|
mergeDefaultB
|
C
|
mergeDefaultD
mergeDefaultXXXX are merge commits that came as the result of merging the default branch into the feature branch.
What has happened is commit C is screwed, but this was not noticed until after I had pushed mergeDefaultA to Bitbucket. What I want is the following picture:
exactlyWhatIsInMergeDefaultD
|
mergeDefaultA
|
mergeDefaultB
|
C
|
mergeDefaultD
Where exactlyWhatIsInMergeDefaultD is literally exactly what was the state of the code in mergeDefaultD. However, everything I'm reading seems to indicate either you can't undo a series of commits like this (only a single commit back) and even then many of the options aren't available once you've pushed "into the wild".
How do I achieve this?
If this was git, I'd do:
git revert mergeDefaultD
How do I do the same in Mercurial?
Here's what I think you want:
hg revert -r GOOD_REVISION_NUMBER --all
hg commit -A -m "reverting back to revision GOOD_REVISION_NUMBER"
Once that is committed, as soon as someone pulls from you (or you push to them) they will get the most recent revision containing only the good stuff. If they ever do want to go back to the bad stuff, you could always update to that revision:
hg update -r BAD_REVISION_NUMBER
To expand a bit on Harvtronix' answer (which is fine, by the way):
One simple way is to revert to the old revision number ('GOOD') and commit. Note: reverting means that you set the files to the same content as in revision 'GOOD', you don't go back down the tree to that commit. If you did, you would indeed branch off and have two heads.
hg revert -r GOOD --all
hg commit -m "all is good now"
Another way can be to only throw out revision C (if I read your explanation correctly, it's actually just C that is causing the issue). 'hg backout'will introduce the reverse of C in your working directory, so you can then commit it.
hg backout -r C
hg commit -m "Backed out C"
Finally, one last option is to close the bad branch, update to the last commit that was fine and commit further there:
hg up -r BAD
hg commit --close-branch -m "This head is bad"
hg up -r GOOD
... continue here ...
So there was a new branch created where we made some breaking changes to the codebase.
Now we are going to merge, but before that I want to get a list of all the files that were changed in the branch.
How can I get a list of files? I tried:
hg status --change REV
But i'm not sure if that is what I want, since I want all files changed in this branch and not a specific revision in the branch.
BTW, how can I view the revision numbers?
Try with
$ hg status --rev "branch('your-branch')"
to get the changes between the first and the last changeset on the branch (hg status will implicitly use min(branch('your-branch')) and max(branch('your-branch')) when you give it a range of revisions like this).
Since you'll be merging, you should really look at
$ hg status --rev default:your-branch
to see what is changed between the default branch and your-branch. This shows you the modifications done, and filters out any modifications done on the branch due to merges with default.
This is necessary in case your history looks like this:
your-branch: x --- o --- o --- o --- o --- o --- y
/ / /
default: o --- a --- o --- b --- o --- c --- o --- o --- d
where you've already merged default into your branch a couple of times. Merging default into your branch is normal since you want to regularly integrate the latest stuff from that branch to avoid the branches drifting too far away from each other.
But if a new file was introduced on default and later merged up into B, then you don't really want to see that in the hg status output. You will see it if you do
$ hg status --rev a:y
since the file was not present in a, but is present in y. If you do
$ hg status --rev d:y
then you wont see the file in the output, assuming that it's present in both heads.
You write in a comment that you're working Kiln repository. They mean "clone" when they say "branch", but the above can still be adapted for your case. All changesets will be on the default named branch, but that's okay.
Run the following command in your local clone of the "branch" repository:
$ hg bookmark -r tip mybranch
This marks the current tip as the head of mybranch. Then pull all the changesets from the main repository:
$ hg pull https://you.kilnhg.com/Repo/public/Group/Main
You then mark the new tip as the tip of the main repository:
$ hg bookmark -r tip main
You can now run
$ hg status --rev main:mybranch
to see the changes between main and my-branch. If you want to see what you did on the branch itself, the use
$ hg status --rev "::mybranch - ::main"
The ::mybranch part will select changesets that are ancestors of mybranch — this is all your new work, plus old history from before you branched. We remove the old history with - ::main. In older versions of Mercurial, you would use hg log -r -r mybranch:0 -P main.
In cases like this, I prefer to do a test merge from a newly checked-out copy of the repo. This has the advantage that I can see how many conflicts the merge will produce, and I can keep the merge result because I did it in its own copy.
To view the revision numbers, enable the graphlog extension and run:
$ hg log -b your-branch -G
This gives you a nice ASCII graph. This can be handy to quickly look at the graph, but I recommend using TortoiseHg for a cross-platform log viewer:
I had to merge the default branch into my branch to get some fixes, now the commands above shows also files changed because of merges (this files changed after the merge again in the default branch).
Therefore, to get only the correct files I use something like this:
hg log --rev "branch('my-branch') and not merge()" --template '{files}\n' | sed -e 's/ /\n/g' | sort -u
if you have spaces in file names, you can do it this way:
hg log --rev "branch('my-branch') and not merge()" --template '{rev}\0' | xargs -0 -I # hg status -n --change # | sort -u
And to answer your last question, revisions can be shown this way:
hg log --rev "branch('my-branch') and not merge()" --template '{rev}\n'
TIP: I use a hg-alias for this:
[alias]
_lf = ! $HG log --rev "branch(\"$1\") and not merge()" --template '{rev}\0' | xargs -0 -I # hg status -n --change # | sort -u
With mercurial, if you want to get the list of all the files changed in your current branch (changes done of your changeset) you can use these commands: :
hg log --branch $(hg branch) --stat | grep '|' | awk -F\ '{printf ("%s\n", $1)}' | sort -u
Example result:
api/tests/test_my_app.py
docker/run.sh
api/my_app.py
Explanation of the commands:
hg log --branch $(hg branch) --stat
Show revision history of entire repository or files and output diffstat-style summary of changes
hg branch
Show the current branch name
grep '|'
Search for a text pattern, in this case, it is "|"
awk -F\ '{printf ("%s\n", $1)}'
Space separator denotes each field in a record and prints each one in a new line
sort -u
Sort all the printed lines and delete duplicates
I have searched here, but haven't found any question related to this. I got a problem like this in mercurial:
I manage open source project in bitbucket, so i have clone of the source code in my local. But I also using that project for my own live site, so I made 2 clone of bitbucket repo
Bitbucket Repo
|
==local_clone1
|
==local_clone2-> commit1 => commit2 => commit3
(personalization) (bug fix) (add feature)
The question is, I want to push commit2 and commit3 back to local_clone1, so later on I can push to Bitbucket repo. But don't want to push commit1, since it has my personal data.
Wondering how we do that in mercurial?
This can be done without too much difficulty in this case. See Removing history in the Mercurial guide for more information.
Here's the basics of what you'll need to do:
Go to local_clone2
Get the revision number (hg tip will show you) from the current number. We'll call it 731.
hg export 730-731 > ../local_clone1/changes.diff (or wherever you like)
Go to local_clone1
hg import changes.diff
You may need to edit things manually; refer to that guide for more info in that case.
Here are a couple of options:
backout
Given a history constructed as:
hg init db
cd db
echo >file1
hg ci -Am clone # rev 0
echo >file2
hg ci -Am personalization # rev 1
echo >file3
hg ci -Am bugfix # rev 2
echo >file4
hg ci -Am feature # rev 3 <tip>
Then if the current working directory is the tip, the following commands will "undo" the personalization revision:
hg backout 1
hg ci -m backout
The advantage is history remains immutable, but shows the addition and backout of the personalization changeset.
Mercurial Queues
With the mq extension, history can be edited to remove a changeset:
hg qimport -r 1:3 # convert changesets 1-3 to patches
hg qpop -a # remove all patches (can't delete an applied patch)
hg qdel 1.diff # delete rev 1's patch
hg qpush -a # reapply remaining patches
hg qfin -a # convert all applied patches back to changesets.
The advantage is the personalization changeset disappears. The disadvantage is the changeset hashes change due to the history edit, so this should never be done to changesets that have already been pushed to others. There is also the risk of a mistake editing history.
Have a scenario where we un-intentionally merged a named branch (ABC) into our default branch.
hg rollback is not an option because there have been a couple commits since.
Is there a way to undo this?
You're going to need the Mq extension. If you don't have it turned on, do so by adding this to your Mercurial.ini or .hgrc file.
[extensions]
hgext.mq=
If you're not familiar with it, the Mq extension let's you manipulate history. The good news is, this will allow us to fix your repo. The bad news is that anyone who has a clone of the messed up repo will have to clone it again, because we'll be changing history.
First, go make another clone of your repo to work in, so we don't mess anything up.
Now, find the revision id of the merge changeset (that merged default and your named branch). Write it down. We'll refer to it as changesetM. Now find the revision id of the next changeset. Write it down. We'll refer to it as changesetN.
Once you have those two revision ids, head over to your command prompt and cd into your repo. Then type out the following, replacing changeset[M|N] with the appropriate revision id.:
$ hg qimport -r changesetN:tip
# This will add all of your changes since the merge to the queue
$ hg qpop -a
# This pops them all out of your history.
$ hg strip changesetM
# This removes the merge changeset.
$ hg update -C default
# Make sure we're on the default branch
$ hg qpush -a
# Take the changesets in the queue and push them back onto your history.
$ hg qfinish -a
# Remove changesets from the queue and finalize them as normal changesets.
Essentially, you are rebasing the new changesets on top of the default branch, removing the merge changeset in the process. Once you're done, you'll need to push the changes to a new repository on the server, and have your colleagues clone fresh copies.
Lastly, if you have any other Mercurial questions, also check out kiln.stackexchange.com.
UPDATE
I forgot to mention: If someone has based changes on something that was only in the other branch, it is possible that hg qpush -a will fail. You'll see a foo.txt.rej and foo.txt.orig file laying around. Unfortunately, you'll have to fix this yourself. To fix it, open the original file, the .orig file, and the .rej file and choose the right changes to merge in, saving them in the original file. Once you've merged it in, use hg qrefresh to update that patch to the new, merged patch. From their, you should be able to run hg qpush -a again and continue. If you run into the same error again on another patch, just follow the same process.
If you haven't publish the repo publicly you can do this
hg clone -r (parent1 of bad merge) -r (parent2 of bad merge) old new
and delete the old repo.
I came across the following scenario today:
# changeset: 1728:5d703e1051d3
|\ parent: 1727:1a5f73b5edb4
| | parent: 1720:65ddd0bde225
| | user: nn
| | date: Wed Feb 27 10:35:00 2013 +0100
| | summary: Merge with SomeBranch
| |
| o changeset: 1727:1a5f73b5edb4
| | user: nn
| | date: Wed Feb 27 10:34:30 2013 +0100
| | summary: lorem ipsum
| |
[some more changesets]
| |
o | changeset: 1720:65ddd0bde225
| | branch: SomeBranch
| | user: nn
| | date: Wed Feb 27 07:44:46 2013 +0100
| | summary: lorem ipsum
Where SomeBranch should not have been merged into default. What we did to solve this, was to use the backout command with the parent option like so:
hg backout --rev=1728 --parent=1727
By this you don't undo the merge itself: Looking at a branch graph (either with graph log or in TortoiseHg) you'll still see SomeBranch going into default at r1728. The result of the merge is however undone, meaning that the changeset containing the backout (r1729 in my case) is identical to r1727.
(I am a relative newcomer to TortoiseHg, so bear with me :-) I use TortoiseHg on two machines to talk to my remote source repository. I made changes on one machine, committed them, and attempted to push them to the remote repository BUT I forgot to first do a pull to get the latest code first. The push gave me a few lines of output, suggesting I may have forgotten to pull first (true!) and mentioned something like "abort: push creates new remote branches...".
So I did a pull, which added several nodes to the head of my graph in the repository explorer. The problem is that the push I tried to do is now showing as a branch in the repository explorer. Looking from the server side (codeplex), it shows no sign of my attempted push, indicating this accidental branch is still local on my machine.
How could I remove this accidental branch? I tried selecting that node in the graph then doing "revert" but it did not seem to do anything. I am wondering if it would be simplest to just discard my directory tree on my local machine and do a completely new, clean pull from the server...?
First make sure you have committed all your local changes. Then merge the branches by calling hg merge and commit the result.
This should bring you back to a single branch, reuniting the two heads.
I had a branch I didn't want, but found I could not merge the branch (or it was very difficult for me to figure out how to merge) because it contained a conflict based on a file name case change.
Ultimately, I decided to hg commit --close-branch. Since the branch had existed only on my local repo, and not the repo I had cloned from, the subsequent hg push didn't even bother to push the closed branch out to the master repo! Very convenient. At that point, all I had to do to eliminate it entirely was delete my local repo and re-clone from the "master".
Do a "Merge With" and check "Discard all changes from merge target (other) revision". Of course you should make sure that the target shown as the merge target is really the one you want to throw away before you click the Merge button.
In the Repository explorer, choose the first rev of your local changes, then right click on the tip of the branch you just pulled and choose "Rebase on top of selected" or "Modify history->Rebase on top of selected" depending upon your client version. This will "re-base" your revs on the pulled ones.
Additionally, to help avoid it in the future...
In Repository Explorer, choose Tools->Settings. In the top left drop-down, choose "User global settings", so this applies to all repositories. Then choose Synchronize on the left. In "After Pull Operation" choose "rebase". This will cause your local revisions to be "rebased" upon the revisions you just pulled, rather than leaving them in a different branch.
This is how I do it and is probably what you typically want.
For more info, see the rebase project and the rebase extension.
This is how to do it with the command-line tool. I guess it can be easily mapped to TortoiseHg (although I'm not sure, since I never use it...) Anyway, since it should be done just once in a while, I think there is no problem on using the terminal here.
An example setup
Suppose your remote repository is something like this:
# changeset: 3:a4c18a1fba12
| tag: tip
| summary: commit 4
|
o changeset: 2:91c5dbfba15c
| summary: commit 3
|
o changeset: 1:4c77cb7952ea
| summary: commit 2
|
o changeset: 0:085dae46f27e
summary: commit 1
Locally, you did not have commit 4, so you committed something directly over commit 3:
# changeset: 3:39526003350f
| tag: tip
| summary: commit 4 made locally
|
o changeset: 2:91c5dbfba15c
| summary: commit 3
|
o changeset: 1:4c77cb7952ea
| summary: commit 2
|
o changeset: 0:085dae46f27e
summary: commit 1
So you try to push it, and get this message:
$ hg push
pushing to ssh://hg#bitbucket.org/brandizzi/mercurial-test-repo
searching for changes
remote has heads on branch 'default' that are not known locally: a4c18a1fba12
abort: push creates new remote head 39526003350f!
(pull and merge or see "hg help push" for details about pushing new heads)
As requested, you pull it:
$ hg pull
pulling from ssh://hg#bitbucket.org/brandizzi/mercurial-test-repo
searching for changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
You have it now...
o changeset: 4:a4c18a1fba12
| summary: commit 4
|
| # changeset: 3:39526003350f
|/ summary: commit 4 made locally
|
o changeset: 2:91c5dbfba15c
| summary: commit 3
|
o changeset: 1:4c77cb7952ea
| summary: commit 2
|
o changeset: 0:085dae46f27e
summary: commit 1
... but you would rather not merge as requested. You want to have this instead:
o changeset: 4:a4c18a1fba12
| summary: commit 4 made locally
|
o changeset: 3:a4c18a1fba12
| summary: commit 4
|
o changeset: 2:91c5dbfba15c
| summary: commit 3
|
o changeset: 1:4c77cb7952ea
| summary: commit 2
|
o changeset: 0:085dae46f27e
summary: commit 1
And then you want to push it to the remote repo.
How do you get that?
Solving it
To get that, you cannot have pushed the "commit 4 made locally". Also, there is no way to put it after the new remote commits. Said that, we can get what we asked.
Said that, you just need to rebase your local commit onto the new remote commit:
$ hg rebase --source 3 --dest 4
If you are lucky, that will be enough.
Handling conflicts
If you are unlucky, you may have some conflicts:
$ hg rebase --source 3 --dest 4
rebasing 3:39526003350f "commit 4 made locally"
merging test.txt
warning: conflicts while merging test.txt! (edit, then use 'hg resolve --mark')
unresolved conflicts (see hg resolve, then hg rebase --continue)
Then, just resolve the conflicts (by manually editing them):
$ hg st
M test.txt
$ nano test.txt # Edit and save
...mark the file as resolved...
$ hg resolve --mark
(no more unresolved files)
continue: hg rebase --continue
...and proceed with the rebase:
$ hg rebase --continue
rebasing 3:39526003350f "commit 4 made locally"
saved backup bundle to /home/adam/software/mercurial-test-repo/.hg/strip-backup/39526003350f-64863882-backup.hg
Here is your new history:
# changeset: 4:ca31fe8a15f0
| summary: commit 4 made locally
|
o changeset: 3:a4c18a1fba12
| summary: commit 4
|
o changeset: 2:91c5dbfba15c
| summary: commit 3
|
o changeset: 1:4c77cb7952ea
| summary: commit 2
|
o changeset: 0:085dae46f27e
summary: commit 1
Now, push it:
$ hg push
pushing to ssh://hg#bitbucket.org/brandizzi/mercurial-test-repo
searching for changes
remote: adding changesets
remote: adding manifests
remote: adding file changes
remote: added 1 changesets with 1 changes to 1 files
Those days, it is not as complex as it used to be, right? :)