Mercurial error: repository is unrelated - mercurial

I've just started with Mercurial, I have a 'central' repository on Bitbucket which I cloned onto one machine and made changes and committed and pushed. I then cloned from Bitbucket to another machine committed and pushed which was fine. I then came back to the first machine, made changes committed and attempted to push, but got the error message. What am I doing wrong? Should I have pulled first? How can I resolve the error and push? Any help is appreciated!
Darren.

A Mercurial repository gets its identity when you make the first commit in it. When you create a new repository on Bitbucket, you create an empty repository with no identity.
When you clone this repository to machine A and make a commit and push it back, then you brand the repository. If you have cloned the repository on the second machine before pushing from the first, then you can end up in the situation you describe.
Please run hg paths on the machine where you cannot push. Then make a separate clone of the repository it says it will push to. Now examine the first changeset in each repository with
hg log -r 0
If the initial changesets are different, then you have two unrelated repositories, as we call it in Mercurial. You can then export the changes you cannot push as patches and import them in the other.

If you're pretty sure the push path is correct, it may be worth it to just export your changes to patches from the problem repo, clone again from Bitbucket and then import the patches into the new repo. This will either just work or reveal a bad/corrupted commit.

I would like to share knowledge about Mercurial internals.
Repositories unrelated when they have no any same revisions.
Corresponding piece you can find in mercurial/treediscovery.py:
base = list(base)
if base == [nullid]:
if force:
repo.ui.warn(_("warning: repository is unrelated\n"))
else:
raise util.Abort(_("repository is unrelated"))
base is a list of roots of common parts in both local/remote repositories.
You always may know how repositories are different by:
$ hg in $REMOTE
$ hg out $REMOTE
You always may checks roots of both (after cloning both locally):
$ hg -R $ONE log -r "roots(all())"
$ hg -R $TWO log -r "roots(all())"
if output from above commands doesn't share IDs - those repositories are unrelated. Due to hash properties it is very impossible that roots be equal accidentally. You may not trick roots checking by carefully crafting repositories because building two repositories looks like these (with common parts but different roots):
0 <--- SHA-256-XXX <--- SHA-256-YYY <--- SHA-256-ZZZ
0 <--- SHA-256-YYY <--- SHA-256-ZZZ
impossible because that mean you reverse SHA-256 as each subsequent hash depends on previous values.
Having this info I believe any Devs be able to troubleshoot error: repository is unrelated.
See also Mercurial repository identification
Thanks for attention, good hacking!

You get this message when you try to push to a repository other than the one that you cloned. Double-check the address of the push, or the default path, if you're just using hg push by itself.
To check the default path, you can use hg showconfig | grep ^paths\.default (or just hg showconfig and look for the line that starts paths.default=).

Related

Mercurial diff/patch by example

I have read only permission to an hg repo and am trying to develop and test changes to it locally. The problem is that I am in the middle of changing dev machines and am caught in a weird/akward state across the two machines.
On my old machine I made lots of changes to the repo, locslly. I just cloned the repo on my new machine, but obviously that doesn't contain the changes from my old machine. I need a way to createe a patch/diff from my local working copy on my old machine, and then apply them to my local working copy on my new machine. The problem is that I already commited (hg commit -m "Blah") the changes on my old machine to the distributed repo on it.
What set of specific commands can I use to create a patch/diff of my old machine and then apply it to the repo on my new one?
Update
I commited all changes on my old machine and then ran hg serve, exposing http://mymachine.example.com:8000.
On my new machine, where I had made some different changes (locally) than the changes from my old machine, I ran hg pull http://mymachine.example.com:8000 and got:
myuser#mymachine:~/sandbox/eclipse/workspace/myapp$ hg pull http://mymachine.example.com:8000
pulling from http://mymachine.example.com:8000/
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 16 changes to 10 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
So I run hg merge:
myuser#mymachine:~/sandbox/eclipse/workspace/myapp$ hg merge
abort: uncommitted changes
(use 'hg status' to list changes)
What do I do now?!?
You can use:
$ hg diff > changes.patch
To create a patch file, then:
$ patch -p1 < changes.patch
To apply that patch file on your new machine.
Well, that's actually fantastic, mercurial is a distributed version control system and you do not need to go via any patch file at all: simply pull the changes from your old machine to your new machine:
hg pull URL
where URL can be any network URL or also ssh-login, e.g.
hg pull ssh://mylogin#old.maschine.box or hg pull path/to/old/repository/on/nfs/mount
`
Alternatively you can also use bundle and unbundle. They create bundles which can be imported in the new mercurial easily and keep all meta-information.
hg bundle -r XXX --base YYY > FILENAME
where YYY is a revision you know you have in your new repository. You import it into your new repo with hg unbundle FILENAME. Of course you can bundle several changesets at once by repeating the -r argument or giving a changeset range like -r X:Y.
The least comfortable method is a via diff or export:
hg export -r XXX > FILENAME or equivalent hg diff -c XXX > FILENAME where you need to import the result with patch -p1 < FILENAME or hg import FILENAME.
The easiest way is to do this is to ensure that all work on your old machine is committed. Then use this command on it from the base of your repo:
hg serve
which creates a simple http server on this repo. The monitor should state the name of the http URL it is serving.
On your new machine, just pull from that URL.
Once you've pulled your old changes you can stop the hg serve process with ^C.
The advantages of this method are that it is very quick, and that it works on just about any system. The ssh method is also quick, but it won't work unless your system is configured to use ssh.
Answer to Update
The OPs update is asking an orthogonal question about how to merge changes pulled from a server with local changes. If you haven't already done so, try to digest the information in this merge doc and this one.
Merging is for merging changesets. The error is happening because you have local changes that haven't been committed which mercurial can't merge. So the first thing to do is to commit your local changes, then you will be able to merge.
But before you merge, I strongly recommend that you are merging what you think you are merging. Either ensure there are only 2 heads, or specify which head you are merging with. When merging, you have to be at one of the heads you wish to merge; it's usually better to be at the head with the most changes since the common ancestor because the diffs are simpler.
After you've merged, don't forget to commit the merge. :-)

Mercurial: why do pulled changesets not become public?

Consider the following situation:
$ md repo1; cd repo1
$ echo some data > myfile
$ hg init; hg addremove; hg commit -m "First commit."
adding myfile
myfile
committed changeset 0:32c7aa047f3b
$ hg serve
listening at http://vostro.rath.org:8000/ (bound to *:8000)
And then in another terminal:
$ hg clone http://vostro.rath.org:8000/ repo2
requesting all changes
adding changesets
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
updating to branch default
resolving manifests
getting myfile
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd repo2; hg phase tip
0: public
..and in the first terminal again:
127.0.0.1 - - [25/May/2013 16:38:40] "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
^Cinterrupted!
$ hg phase tip
0: draft
To me this looks very wrong. Someone just pulled the changeset from the first repository, so it is obviously public. However, it still appears as "draft" in the repository.
Can someone explain the rationale for this behavior? As the owner of the first repository, I would very much like to know when someone has pulled a revision (so that e.g. I don't rebase it anymore), so I think it would be sensible if the hg server process would update the phase accordingly.
You will probably get a better answer on the mailing list for this, but my understanding is this:
hg pull has always been a read-only command and can be run without write access to the remote repository. Changing the phase in the remote repository would (obviously) require a write. On the other hand, hg push has always written to the remote repository, and so phases introduced no change.
Changing hg pull from read-only to read-write could cause some people's work flows to break, and that's a mortal sin in mercurial development. (E.g. An anonymous user pulling from a public server, sending back changes via e-mail bundles)
Basically it's a historical quirk because phases are a retro-fit.
The hole this leaves open is that the original owner of the change-set could amend it, without realising that the change has already gone into the wild. I expect that this hole hasn't worried too many people because the "change-set evolution" features that are being developed solve the problem in a better way.
I tend to think of the phases as:
Public - Publicly visible and immutable
Draft - Publicly visible and mutable
Secret - Not publicly visible and mutable
I think draft is only there because that's basically where we were before phases were added, and is a bit of a weak concept. Really, if your working in an environment where people may pull directly from you, then I suggest working more with public and secret phases, and avoid draft.
As #zerkms said, pull isn't intended to change the remote repository.
If your working repository is being used as a server, you have a few options:
Set the default of commits to "public" instead of "draft". Others can pull at any time so just assume they are public.
Set the default of commits to "secret". Others won't be able to pull them. Set them to "public" when you are ready to share.
Set your repository as "non-publishing". Others can pull your draft changesets, but they will still be marked as "draft".
Here's how to specify these behaviors in mercurial.ini/hgrc.
[phases]
publish = False
new-commit = public
pull isn't intended to change the remote repository phase but the phase of your local repository.
And to be clear - you shouldn't care what phase is in the remote repository.
And even more - remote repository may be hosted using old mercurial version which doesn't support phases.
Why this behavior?
Because phases are only make sense for the local repository and made to help preventing history modification mistakes.

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.

After pushing to a review repository, "abort: can't rebase immutable changeset" on rebase

We have a code review repository where people hg push -f all sorts of stuff. After reviews are complete, we pull from the project's central repository, rebase, and push. I recently upgraded to mercurial 2.1 and get this message:
abort: can't rebase immutable changeset 43ab8134e7af
(see hg help phases for details)
when I try to hg pull --rebase from the central repository. How do I fix it?
In the review repository's .hg/hgrc file, add these lines:
[phases]
publish = False
The problem is due to a new feature in mercurial 2.1 called phases. It's great. Here is a nice introduction to its use.
To make the changesets in question mutable right now, use hg phase -f -d REV to force REV to be mutable again. Once the hgrc file has been changed, you shouldn't have to do that any more.
As a side note, hg push -f is lame. Make an alias hg review that pushes with -f to that repository.
I don't think disabling phase support on the server is the correct solution, but your problem sounds weird.
Pull --rebase should rebase your local changes, on top of the remote changes, which should be allowed, even if phases are supported by the client, as long as these changes have not been seen by anyone else, eg. they haven't been pushed out anywhere.
Is it possible that you have already pushed your your own changes, to somewhere else (which set them to public phase), and after that tried pulling from the testing repo? Because then, this is the correct behaviour that you are seeing.
Most of the time it is a bad idea to mess with phases manually (with hg phase -f), because it can easily lead to a history rewrite, which can lead to duplicated changesets, or various errors when other people try to pull/push. If a changeset became marked as public (as in your case), it probably happened for a good reason.
I've encountered such behaviour with collapsed rebase. Phasing out back to draft hadn't helped me. So I've just pulled up (hg pull -u) to sync with remote repo, then just grafted the problem commit (hg graft <problem_commit>) and then amended this very new commit.

Can I mark a branch as 'not going to push'?

I use named branches in Mercurial.
In doing so I have created one branch called playground where I can try out various wacky experiments. I never intend to merge this branch into any others and I never want to push it to our main repository.
Since creating it, every time I do a push I am told I have added a new branch and I have to use the --new-branch flag. At this point hg push -b default (or whatever branch I'm pushing) works fine but it's annoying. Is there any way to suppress that message by letting Hg know that I am not interested in pushing that branch ever?
Starting with Mercurial 2.1 (released in February 2012), you can mark your changesets secret to keep them from being pushed to another repository. You use the new hg phase command to do this:
$ hg phase --force --secret .
This mark the current working directory parent revision (.) as being in the secret phase. Secret changesets are local to your repository: they wont be pushed or pulled. Pushing now looks like this:
$ hg push
pushing to /home/mg/tmp/repo
searching for changes
no changes to push but 2 secret changesets
There is no equivalent mechanism in older versions of Mercurial. There your best bet is to create a local clone for the changesets you don't want to push.
Update:
Mercurial 2.1 introduced the hg phase command which allows users to control what change sets are exchanged with remote repositories. #MartinGeisler answer to this question details this method.
Original Answer:
If you want to create a local branch of your code you have a couple options. You can hg clone the repository which will locally create a branch of the entire repository in your filesystem. The other alternative is you can try to use a Mercurial extension like LocalbranchExtension.
There are many ways to branch in Mercurial without using a named branch. Just find a method that suits your needs.
Further reading: http://stevelosh.com/blog/2009/08/a-guide-to-branching-in-mercurial/
In addition to the excellent answer above concerning phases, you can also specify 'default-path' (in the [paths] section of your .hgrc) to refer to the local repository:
[paths]
default = ...
default-push = .
This will cause all outgoing changesets to be compared to the specified repository. In this case, comparing outgoing changesets in your local repository TO your local repository results in nothing to push.
You can still pull/update/merge from the main repository, but no push will ever send anything back to that main repository.
If you work on multiple machines/repositories, you can set one up as described above, and configure the others to specify the 'default' path to point to the server that pushes to itself. In this way, the other machines can push/pull to your local central repository, and these changesets will never escape your carefully configured collection of repositories.