Propagation of changes in Mercurial - version-control

I have a following question. Suppose I have a bunch of repositories hosted that form an hierarchy, e.g.: A -> B -> C (means A is the central repository and all the rest are it's descendants).
Now suppose I work with the clone of C. Suppose I want to get the changes not just from C, but from the central repository, so I do the following commands:
hg pull [Address of A]
hg up
That seems perfectly legal, but what happens then I commit my changes and push them to C? Not only my local modifications will be pushed, but also the modifications of central repository (if there are any). What will happen if someone will try to pull the changes from A to C? Will there be a conflict or it will merge successfully the changes A -> local -> C with changes A -> C. Will Mercurial recognize it as the same changeset or not?
The identical situation takes place if I decide that my code is stable enough and can be placed in central repository:
hg commit -u spirit -m "A local modification that is stable"
hg push [Address of A]
What will happen if I make a pull from A to C and then pull from C to my local repo again, will it recognize these changeset as originating from my local repo, or will it report a conflict and suggest a merge?
And what is the best practice in that case anyway? Performing just subsequent pulls and pushes (i.e. A<->B, B<->C, C<->local)? But the problem is that I have just access to my local repository that is clone of C. How can I make a pull from B to C if I would want to on my local machine? How does Mercurial handle

What will happen if someone will try to pull the changes from A to C? Will there be a conflict or it will merge successfully the changes A -> local -> C with changes A -> C. Will Mercurial recognize it as the same changeset or not?
The changesets that already exist in C will be seen to already exist because their IDs will already be present. This is why changes are immutable. If you could modify a changeset it's ID would change (The ID is a hash of the changeset contents). Mercurial then (correctly) sees it as a different changeset. By keeping the changesets immutable we can be sure that their hash will be the same whichever repo they come from.
pull = copy changesets from over there that have IDs I haven't seen
push = copy changesets to over there that have IDs they haven't seen
Will there be a conflict
No
or it will merge successfully the changes A -> local -> C with changes A -> C.
No merge takes place because they are the same changes. It just sees that it already has them.
Merges don't happen unless you want to combine the changes from two parallel sets of changes, and not unless you explicitly ask for it.

As long as the changeset are identical, i.e. they have the same id hash. Mercurial considers them to be the same.
When you modify changesets with rebase or mq commands, the id hashs change and mercurial might place two very similar changesets into a repository.

Related

Are these two meanings of a pull request both correct, and contrary to each other?

On my local repository, I created a branch A from branch B. I did some work on branch A, and pushed A to github.
Then I created a pull request on github, in order to merge branch A into branch B, I heard that it is said to be "branch A is pulled from branch B". Is it correct?
Doesn't a pull request mean merging branch A into branch B?
What does "pulling" branch A from branch B mean?
It seems to me that the two meanings of a pull request are contrary to each other.
If you merge A into B it will be:
Branch A is pulled into B.
or
Branch B is pulled from A.
It refers to the branch you're currently at. If you're in branch B and pull from A, that pull will first fetch A and then merge A.
Pull and Merge are two different process.
"branch A pull from branch B" means you make a editable copy, call it A, from B (If A originally not exist).
"Merge A into B" means you are applying all the changes you made in A back into B
the reason people saying that "branch A is pulled from branch B" is because if A is not pull from B, it can not be merged back to B
There are two different uses of 'pull' in git terminology and, while not contradictory, they can be confusing at first.
1. Pull:
From the command-line command git pull (also known as a combination of fetch and merge). Essentially just gets the remote code (or 'pulls' it to your computer) and merges it into your local code. Read more here.
2. Pull request:
When you want to merge your changes into the repo. Generally opens a discussion and review of your changes before the pull request (or 'PR') is accepted. Read more here.
As an aside, there is also a 'push' command, which may make things clearer (and reiterate the direction pushing and pulling to and from local and remote repos). git push 'pushes' your commits from local to remote (i.e. the opposite of #1, git pull). Read more here.

How to push only a subset of committed files?

We recently switched from svn to mercurial. We're using Aptana as our IDE with the MercurialEclipse plugin, along with BitBucket for our repositories and SourceTree as our (additional) source control GUI.
I created 2 new files in Aptana, and committed each of them. Now in the Synchronize view, where the 2 files are listed as "outgoing", I'd like to push only one of them. I avoided using the "push all" icon at the top which would push all outgoing changes - instead I right-clicked a specific file in the outgoing list and chose "push" from the context menu. However, this caused both outgoing changes to be pushed. I can't seem to find any option to push only a specific file or subset of files of the committed changes. Is there any way to accomplish this in Aptana?
Note: My answer doesn't relate to Aptana, but instead covers what I think your issue is.
I think the main problem is a misunderstanding of how Mercurial stores its changes, which coming from a Subversion background is perfectly reasonable.
In Subversion, change history can be considered to be stored per file. That is, if you change two files and commit them, you can easily, and often do, have a situation where files in your working copy are at different versions.
In Mercurial, change history is stored across the whole repository. Committing will create a new "Changeset", which stores the state of the entire repository at that time. When you decide to push a change out to another repository, all modifications (or adds, or deletes, or...) will be pushed out with that change.
A caveat is that when you decide to commit a new changeset to your repository, you can selectively include or exclude files. Files not included will remain in your working copy as pending modifications, which can be committed in a new changeset.
I hope that makes sense to you - if you already understand it, it's a logical concept, but I find it tricky to explain.
So, on to your problem.
Lets say you have two files in your repository, file1 and file2 (it's that or foo and bar). You've changed them both, but they relate to different issues - they can be committed as different changesets:
$ hg log
changeset 0:....
summary: First commit
$ hg st
M file1
M file2
$ hg commit -I file1 -m "Changed file1"
$ hg log
changeset 1:....
summary: Changed file1
changeset 0:....
summary: First commit
$ hg st
M file2
Here you can see that we've committed only one file into the repository, and it's made a new changeset with the complete state of the repository at that time, minus the changes to file2. We can now do the same, committing file2, which will create another changeset. The problem with this approach is that changesets are ordered according to their parent, and so you couldn't easily push just the change to file2, without also pushing its parent - but it may be closer to what you're after.
TL;DR : SVN stores the state of individual files, Mercurial stores the state of the repository as a whole.
I very much recommend reading Mercurial: The Definitive Guide. It's a little out-of-date in places, but I think it will do a much better job of getting the concept across.

Ignore one parent when pulling a merge commit

I want to pull a set of changesets from a certain branch in a remote repository. One of the changesets is a merge from another branch, which I don't want to pull. However, it will be pulled even if I specify the branch name:
hg pull -r REV -b mybranch REMOTE_REPO
Is there a way to pull this commit as a regular changeset, ignoring its other ancestors?
No, this is impossible. All changesets (whether they are regular or merge changesets) depend on their ancestors and cannot be pulled in isolation. This is a fundamental design decision in Mercurial.
Merging prematurely results in the annoying situation you describe — each branch is no longer clean and cannot be pulled without also pulling in other stuff. The best way to avoid this is to use rebase (if the development is local-only and thus elegible for rebasing) or to simply ask people to stop mixing unrelated things together until you can make a firm decision about what needs to be merged.

Update branch from parent branch?

In CVS I have a branch (b) off another branch (a) which is off the trunk/head.
Some bug fixes were made in branch (a) that I'd like to go ahead and use in branch (b). How can I pull those fixes into my branch in Eclipse?
head
|
v
a (with bug fixes)
|
v
b (needs bug fixes)
Ideally what you need is to have two tags on a for every feature you want to merge, and then merge the difference between those two tags into b. However, you would also need to remember which ones you have already merged, because CVS doesn't remember that.
When I was working in a company that used CVS and branches, our policy was that bugfixes from branches (a in this case) that ought to be used by other branches need to get merged into the trunk first, and all the other branches merge them from there.
However, it was still very painful if you wanted to cherry-pick individual bugfixes. Essentially, you'd have to remember every fix you've merged (by two tags, marking the beginning and the end of the changes making up that fix).
Generally, in CVS it's much better to remember (in a tag) up to which revision you have merged, and merge everything from there to the head (and then move the tag to the head). In CVS, cherry-picking is painful and requires you to store the merge history somewhere.

Extending the history of a Mercurial repository into past

I've started development on a project (which used CVS) by downloading its sources, creating a fresh HG repository, and using that. However, the original project now has converted to using Mercurial as well.
Can I add its history before my initial commit into my repository?
Alternately, how can I push my repository to the remote one so as to preserve the history of both?
You can't change the ancestors of your current repo without altering the hash id's of every changeset, which essentially makes it a different repo. The hash of "left-parent" and "right-parent" are part of "who a changeset is" and so giving a parent to the first parent-less changeset in your current repo would change that first changeset's hash, which since it's the parent of the second changeset would change it's hash and so on.
If you're okay with changing the hashes of your existing repo (which you shouldn't be if anyone else out in the wild has clones of it) you could use the convert extension or even just import/export to attach your repo to the their newly converted repo.