Someone committed in our central repository. How do we revert it? - mercurial

We're using a local central repository where everyone pushes to and pulls from. Until recently this repository only contained the .hg folder. Then someone went ahead and committed directly in the central repository creating an "island" changeset with no parent (parent = -1) nor child. The correct way would have been to add it in a local repository and push the changes.
Is there any way to get the working copy of the central repository to get back to the state where it only contain .hg and not be associated with a specific changeset?

The command:
hg update null
Updates a repository's working directory to the point before the first commit, so there are no files in the working directory and hg parents shows -1.
You'll still need to remove the commit if you don't want it, but that's a separate question/issue.

Since this is your local central repository and these are commands that edit history, please take every precaution, like trying things out on a copy of the repository first.
hg rollback (to remove the last repository change)
https://www.mercurial-scm.org/wiki/Rollback
Roll back the last transaction in a repository.
hg strip (to remove specific revisions)
https://www.mercurial-scm.org/wiki/Strip
hg strip rev removes the rev revision and all its descendants from a repository.
Also see:
https://www.mercurial-scm.org/wiki/EditingHistory
If you catch your mistake immediately (or reasonably soon), you can just use hg strip REV to roll back the latest (one or more) changes. ...
Edit: This answers the question in its original form. The OP has since edited the question.

Just delete everything except the .hg folder.

If I understand correctly what happened, someone changed into the repository directory and committed a single file as an 'added file' without first checking out any changeset from the active directory.
What you ended up with is something like the following history graph:
0:a43f 1:2843 2:bc81 3:2947
o ------ o ------ o ------ o
4:228f
o
where changeset 4:228f has the "null id" as its parent changeset.
Depending on whether you want to keep changeset 4:228f or not, you can follow one of the following strategies:
Option 1: Clone the repository without the offending change
You can create a new clone of the repository, specifying revision 3:2947 as the target revision. This will pull change 3:2947 and all its ancestor changesets into the new repository. Since the offending changeset is not linked into the ancestry of changeset 3:2947, it will not be part of the new clone.
I recommend saving the old repository first, and then moving everything over to a new clone, e.g. any repository-specific setup files you keep in its .hg/ directory.
If your current repository lives in /work/repo/foo, one way to do this would be:
$ cd /work/repo
$ mv foo foo.bak
$ hg clone -r 2947 foo.bak newfoo
Now copy over any .hg/ setup files, e.g. your original .hg/hgrc file:
$ cp foo.bak/.hg/hgrc newfoo/.hg/hgrc
Finally move the new foo repository in place:
$ mv newfoo foo
Option 2: Clone the repository and keep but rebase the change
Do the same as before, but before you move the newfoo repository in place, use the "hg export" command to extract a copy of the offending change from the old repository. Then check out a working copy of the tip-most changeset in the newfoo tree and import the file changes of the offending change as a normal changeset of your current history graph.
So, right before mv newfoo foo, save the patch of the offending change by typing:
$ cd /work/repo/foo.bak
$ hg export -r 228f --git > /tmp/patchfile.diff
then you can check-out the latest revision of the new repository, and re-import the patch on top of your current history:
$ cd /work/repo/newfoo
$ hg update --clean tip
$ hg import /tmp/patchfile.diff
If the offending changeset merely adds a new file, this should work fine and you are ready to move the new repository in place!
Before doing the final rename though, it may be a good idea to remove the working-copy files of the new repository:
$ cd /work/repo/newfoo
$ hg update --clean null

Related

Add a parent to the original changeset in Mercurial

I have a project with 24 months of source control history in a Mercurial repository.
I've recently found some old tarballs of the project that predate source control, and i think they would be useful to import into the repository as "pre-historic" changesets.
Can i somehow add a parent to my initial commit?
Alternatively, is it possible to re-play my entire repository history on top of the tarballs, preserving all metadata (timestamps etc)?
Is it possible to have the new parent commits use the timestamps of these old tarballs?
You can use the convert extension to build a new repository where the tarballs are imported as revisions before your current root revision.
First, you import the tarballs based on the null revision:
$ hg update null
$ tar -xvzf backup-2010.tar.gz
$ hg addremove
$ hg commit -m 'Version from 2010'
$ rm -r *
$ tar -xvzf backup-2011.tar.gz
$ hg addremove
$ hg commit -m 'Version from 2011'
I'm using addremove above to give Mercurial a chance to detect renames between each tarball (look at the --similarity flag to fine-tune this and use hg rename --after by hand to help Mercurial further). Also, I remove all the files in the working copy before importing a new tarball: that way the next commit will contain exactly the snapshot present in the tarball you unpack.
After you've imported all the tarballs like above, you have a parallel history in your repository:
[c1] --- [c2] --- [c3] ... [cN]
[t1] --- [t2] --- [tM]
Your old commits are c1 to cN and the commits from the tarballs are t1 to tM. At the moment they share no history — it's as if you used hg pull -f to pull an unrelated repository into the current one.
The convert extension can now be used to do a Mercurial to Mercurial conversion where you rewrite the parent revision of c1 to be tM. Use the --splicemap flag for this. It needs a file with
<full changeset hash for c1> <full changeset hash for tM>
Use hg log --template '{node} ' -r c1 -r tM > splicemap to generate such a file. Then run
$ hg convert --splicemap splicemap . spliced
to generate a new repository spliced with the combined history. The repository is new, so you need to get everybody to re-clone it.
This technique is similar to using hg rebase as suggested by Kindread. The difference is that convert wont try to merge anything: it simply rewrites the parent pointer in c1 to be tM. Since there is no merging involved, this cannot fails with weird merge conflicts.
You should look at using rebase. This can allow you to make the changes the 2nd changeset on your repo ( you have to rebase from the 1st ).
https://www.mercurial-scm.org/wiki/RebaseExtension
However, note that if there are other clones of this repo existing ( such as for fellow developers, or on a repo server ), you will have issues with them pulling the revised repo. You will probably have to co-ordinate with the owners of those clone's to get all work into a single clone, rebase that clone, and then have everyone re-clone from the revised clone. You will also have to change the phase the of the changesets.
https://www.mercurial-scm.org/wiki/Phases
Honestly though, I would just add them to your 'modern-day' repo, I don't think making them pre-historic would give you any notable advantage over adding them to the top.

Mercurial pushing update to previous revision

I ran hg update -r REVISION to revert a branch to a previous revision, but when I try to push this to a remote repository it says "no changes found". How can I accomplish this?
To revert the files to a previous revision you can use
hg revert -r REVISION
This will change your working directory files to what they were at that revison. Then you
will need to commit these changes before pushing.
hg update -r REVISION changes the working directory's parent to be that revision as well as changes the contents of the working directory to that revision. This is not what you want here.
hg update only affects the state of your working directory, not the repository itself. If you want to "undo" the effects of one or more previous revisions, you will need to change the repository by committing a new changeset that reflects those changes. You could do it manually but hg's builtin backout command makes this easy to do. See a brief description here. There is a detailed explanation of backout here.

repo cloned from one remote branch needs to be commited to a different remote branch

We have a master repository located on a separate server. I originally cloned the default branch and made my changes locally. I have locally commited those changes. However, there has been a branch created on the master repository that I would like to push my changes to. Below is the description of my attempt at getting this accomplished.
I have cloned the branch. I am trying to export my changes from local default like so:
C:\hg\default>hg export -g -o mypatch -r tip
and when trying to import them into the clone of the new branch, I get the following:
C:\hg\newBranch>hg import C:\hg\default\mypatch
applying C:\hg\Fill1\mypatch
patching file .hgignore
Hunk #1 FAILED at 11
1 out of 1 hunks FAILED -- saving rejects to file .hgignore.rej
abort: patch failed to apply
I can manually fix the .hgingore.rej file just fine. The problem is that the patch also contains files that were moved. Instead of the files showing as moved, I get the following when running hg status:
C:\hg\newBranch>hg status -C
M someOtherFilesThatLookAsExpected.txt
! originalLocaion\fileA.txt
? newLocation\fileA.txt
This missing and new status is for all files that were moved in the commit contained the applied patch. Am I doing something wrong? Do I always have to manually move files when applying a patch? Is there an easier way to accomplish this branch transfer?
That's a bit difficult to answer without knowing more about your repository structure, but here's how I'd go about it without knowing more. I'm assuming that the reason for the conflict is that there are conflicting changes in the same branch of the repository.
First, get the contents of the newBranch repository:
cd c:\hg\default
hg pull c:\hg\newBranch
Then, either merge or rebase your changes on top. If you are working on the same branch, then just using
hg pull --rebase c:\hg\newBranch
in lieu of the regular pull should do (assuming you have rebasing enabled). Otherwise, do an explicit merge or rebase of the two heads that you need to reconcile. Finally, do:
hg push -r tip c:\hg\newBranch
in order to get your (now reconciled) changes back into newBranch.
Unless you have very specific and unusual requirements, push and pull should be your normal way to sync repositories or part of them (note that using -r will only push/pull the respective branch). Export/import are rather low-level mechanisms that may not give you the benefits of the standard machinery that handles renames, three-way merging logic, etc.

Mercurial suprepositories

I have got a question regarding suprepositories. Our project is set up like this:
+ projectA
+ some files
+ dependencyA
+ some files
dependencyA is a subrepository. It was created this way:
cd projectA
mkdir dependencyA
cd dependencyA
hg init
hg pull ssh://hg#somerandomiphere/dependencyA
cd ..
echo dependencyA = ssh://hg#somerandomiphere/dependencyA > .hgsub
hg add
hg commit
hg push
If I make changes to the suprepository, then commit and push them from main project. Both of them will be pushed to the server since its recursive. Now my colleague wants to pull changes from the server. But since nothing was changed in the main project, it wont work. But if I change something in the main project and push it to server. Upon hg pull he will get the newest changeset and if he does hg update then, it will update the subrepository as well. This is expected behaviour.
Now my question would be, if there is a way to pull changes, but only for subrepository without making a new clone of it or what would be the best way to do it.
Subrepository in Mercurial wiki, p. 2.5 "Pull"
The 'pull' command is by default not recursive. This is because
Mercurial won't know which subrepos are required until an update to a
specific changeset is requested. The update will pull the requested
subrepositories and changesets on demand. To get pull and update in
one step, use 'pull --update'.
Note that this matches exactly how 'pull' works without
subrepositories, considering that subrepositories lives in the working
directory:
'hg pull' gives you the upstream changesets but doesn't affect your working directory.
'hg update' updates the contents of your working directory (both in the top repo and in all subrepos)
It might be a good idea to always pull with --update if you have any
subrepositories. That will generally ensure that updates not will miss
any changesets and that update thus not will cause any pulls. If the
pull with update fails due to crossing branches then 'hg update' must
be used to get all the subrepository updates.
What was suggested above works like I thought it would. The real problem was my way of creating a subrepository.
Instead of:
cd projectA
mkdir
dependencyA
cd dependencyA
hg init
hg pull ssh://hg#somerandomiphere/dependencyA
It should have been a simple:
hg clone ssh://hg#somerandomiphere/dependencyA dependencyA
As we know .hgsusbtate will lock the subrepo on specific revision after commit. This is what happened, but (!) doing hg pull in subrepository ended with an error
paths cannot contain dot file components
So this means my subrepo was locked on the revision it was updated after commit and it could not pull changes from its repository due to the error shown above. Why this happened is explained pretty well in this accepted answer.
Solution:
cloning is the way to go

Mercurial Patch Creation and Usage

I have come across a problem that I "think" can only be resolved using patches.
I cloned a project from our main repository, made quite a few changes (updates, deletion of files & directory and additions) to it. These changes are not even committed. The problem is, project from the main repository has been deleted/removed and recreated as a new project (name is same, all the directory structures everything is same as before). I cloned that project again from the main repository and would like to transfer all my uncommitted changes to it.
I am still exploring the hg patch to resolve that. It would be helpful if someone could confirm that creating and adding a patch IS the right approach to this, any resources explaining the process would be of great help.
You're correct — a patch is what you need to transfer the information from one repository to another (unrelated) repository. This will work since the files are the same, as you note.
So, to transfer your uncommitted changes from your old clone, you do
$ hg diff -g > uncommited.patch
$ cd ../new
$ hg import --no-commit ../old/uncomitted.patch
That will restore the information saved in the patch. This includes information about files that are added or renamed in the old clone.
The following steps can be performed with a standard Mercurial install:
Commit the changes in your local repository. Note the revision number.
Use "hg export -r REV >patch.diff" to create a patch.
Clone the new repository.
Use "hg import patch.diff" to apply the patch to the new repository.
Example
C:\>hg init example
C:\>cd example
C:\example>echo >file1
C:\example>hg ci -Am file1
adding file1
C:\example>hg clone . ..\example2
updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
C:\example>rd /s/q .hg
C:\example>hg init
C:\example>hg ci -Am same-but-different
adding file1
At this point example and example2 have identical contents, but the repositories are unrelated to each other due to deleting and reinitializing the .hg folder.
Now make some changes and commit them in one of the repositories, then export them as a patch:
C:\example>echo >>file1
C:\example>echo >file2
C:\example>hg ci -Am changes
adding file2
C:\example>hg export -r 1 >patch.diff
Below shows that the other repository can't pull the changes, because of the reinitialization. It can, however, apply the patch successfully:
C:\example>cd ..\example2
C:\example2>hg pull
pulling from c:\example
searching for changes
abort: repository is unrelated
C:\example2>hg import ..\example\patch.diff
applying ..\example\patch.diff
I would first make copies of everything so you have a way of backtracking.
Then, in the working copy with the changes, I would first delete the .hg directory, then copy in the .hg directory from the new repo. This basically transfers all of the changed files into the new repo without the need to delete any files and directories.
You will still need to tell the repo about whether to remove any files marked as missing. You will also have to handle renames manually. If this is a small number of operations, it's easier than trying to use the patch method.
Once this is done, commit your changes and push, if necessary.
seems like what you want is patch queues. In that you have uncommitted changes, and you want to pull from the new repo before committing them....
$ hg qinit -c # initialize mq for your repo containing the uncommitted changes
$ hg qnew name_of_patch # create patch that contains your uncommitted changes
$ hg qpop # resets your working dir back to the parent changeset
no worries though, your changes are safe and sound in .hg/patches/name_of_patch to see for yourself.....
$ cat .hg/patches/name_of_patch
now pull in the new repo
$ hg pull -u http://location.of.new/repo # pull in changes from new repo update working dir
$ hg qpush # apply your uncommitted changes to new repo
If you are lucky you will have no merge conflicts and you can go ahead and commit the patch by....
$ hg qfinish -a # change all applied patches to changeset
And then if you want....
$ hg push http://location.of.new/repo
If the repos are unrelated, just init a patch repo on your new repo. and manually copy the patch in and add it to .hg/patches/series file.
assuming patch was created. clone new repo
$ hg clone http://location.of.new/repo ./new_repo
init patch repo
$ cd ./new_repo && hg qinit -c
copy patch
$ cp ../old_repo/.hg/patches/name_of_patch .hg/patches/
edit series file using an editor of some sort
$ your_favorite_editor .hg/patches/series
name_of_patch # <---put this in the series file
apply your patch to new repo
$ hg qpush
if no merge conflicts and you are convinced it works
$ hg qfinish -a
If the layout is the same, you can just copy all the files over (excluding .hg) and then use hg addrem.
Try to look into the MQ plugin, it does exactly this if I recall. I've never had a use for that though, so I can't say.
If the old repository was simply moved/cloned to a new URL then you could simply change the remote repository you talk to the new one.
If, however, it was recreated from the ground up (even with the same structure) then I don't believe Mercurial has any built-in functionality to help you here. Mercurial patches reference specific changesets which won't exist in your new repository.
You could use a merge tool to perform the diff and bring across any changes you made.
Edited To answer the question in the comment:
When you clone the repository you are taking a complete snapshot of the entire change history - along with the associated change-set IDs, etc.
Mercurial tracks changes by change-sets to the repository, rather than at the file level like Subversion.
If you clone, then you can easily push/merge into another repository that was also cloned from the same source.
If you recreated the repository then the change IDs won't match, and can't be merged in Hg.
The only option in this scenario would be to use a Merge tool which will let you see mismatches in files/folder structure.
Also: Worth pointing out http://hginit.com/ because it explains (indirectly) some of this.