I'm working on a website using two branches: develop (for actively working on code) and default (where I merge features when they're ready to go live). I've just merged changes and I'm getting this message when switching between the two branches:
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
I've run hg status --rev default:develop to figure out which file it's referring to, but I haven't been able to figure out what is different.
Here's what I've tried so far:
hg diff --rev default:develop
hg diff --rev default:develop file.php
Could someone point me in the right direction?
Diff between two branches:
hg diff -r BRANCH1:BRANCH2
Diff a single file between two branches:
hg diff -r BRANCH1:BRANCH2 the_sinlge_file_to_diff
Reference
How do I diff one branch with my default branch
I've just merged changes and I'm getting this message when switching between the two branches:
When you merge a branch into default the branch still has it's head at the previous change-set. The merge commit is on the default branch and not the develop branch. This lets you switch to the develop branch and continue work
So if you did:
# hg update default
# hg merge develop
# hg commit
...you'd get...
D1 --- D2 --- D3 --- D4
/
/
V1 --- V2 --- V3 -
D is default, V is deVelop. D4 is the merge. This is the state you're in. When you switch between branches you switch between D4 and V3. The one file that's changing is a file that was changed in the default branch. If you then went on and did...
# hg update develop
<some work>
# hg commit develop
...you'd get...
D1 --- D2 --- D3 --- D4
/
/
V1 --- V2 --- V3 --- V4
V4 is based on V3, the change on the develop branch before the merge, not the merged version.
This doesn't answer your diff question but it tells you why your branches don't contain the same thing.
Related
Let's say I have the following commit history:
A - newest
B
C - oldest
I have changed a file foo in B, but I want to move the change from B to A.
How can I do that with hg?
There are several different ways that I can think of. Both have some moderate user requirements (please make sure you have backed up or pushed to a non-publishing repository first).
1 ) Use histedit and amend (requires that each extensions be enabled).
- copy file you wish to move to a safe location
- run hg histedit and edit changeset b and restore file to its original state
- run hg histedit --continue to commit changes.
- copy file from safe location to repository
- run hg amend to append file to changeset A
2 ) Use split and histedit.
- run hg split -r b
- split out all but the file you wish to move into a new changeset
- create a new changeset onto of that containing the fie (give it a temporary description)
- run hg histedit
- move the temp change above A
- roll the temp change into A
3 ) Use hg-evolve uncommit / amend. While this is a somewhat advanced method, I much prefer this one myself.
- run hg update B
- run hg uncommit and select the file you wish to move.
- run hg evolve to correct the stack
- run hg update A
Note: if you get a warning about needing to use --clean use hg shelve before
running the update followed by hg unshelve afterwords.
- run hg amend to add file to change A
4 ) Use hg uncommit all contents of changesets A and B and then recommit using hg commit -i to reassemble the changesets with the desired content.
There are likely a number of other ways, but this is what came to me.
Here's a simplistic but functional approach, maybe for people not very experienced with some of the more advanced HG commands:
Make sure your working directory is clean (no changes).
hg update -r C
hg revert -r B
hg commit ... as needed, but leave out the change destined for A. This creates B'.
hg shelve ... store away the uncommitted changes
hg revert -r A
hg unshelve ... restore uncommitted changes, merging them into the workding directory
hg commit ... creates C'.
hg strip -r B ... (optional!)
(where it says -r A that means use the revision # corresponding to A)
Explanation: we just create two (or more) entirely new changesets and then delete the originals (which is optional).
We're using revert basically to mean "copy from some changeset to the working directory" which I find is very handy in many circumstances.
After these operations -- but prior to using strip -- the history would look like:
*
|
A A'
| |
B B'
|/
C
and strip will just delete A, B.
I have two hg branches (dev and stable) that aren't merging like I'd expect.
On stable: I grafted in a one-line commit from dev.
On dev: Changed that one line that was grafted, committed change.
On stable: merged dev into stable (no conflicts).
However after this merge stable still has the grafted version of the line (step 1). Not the latest changes to that same line from dev (step 2). Why is this?
The file looks like:
This
file
to
be
merged
Changesets:
Changes "to" to "might" on dev
Grafts changeset 1 to stable
Changes "might" back to "to" on dev
Merges dev into stable. Result is "might" (not "to" like I'd expect to see from changeset 3).
Sorry about the delay here: As soon as you shrank the reproducer to the five commits, I knew what was going on, but I wanted to write my own reproducer before answering, and the priority of this dropped a lot. 😀 The script I used, mktest.hg, to create the commits, the graft, and the merge, appears at the end of this answer.
The key issue here is the way merge actually works in Mercurial. It uses the same algorithm as Git does: that is, it completely ignores any of the branch information, and completely ignores any timing information. It looks only at three specific commits, as found by examining the commit graph, as shown in your image. Here's a text variant via my own reproducer:
$ cd test-hg-graft/
$ cat file.txt
This
file
might
be
merged
$ hg lga
# 4:b027441200d2:draft stable tip Chris Torek
|\ merge dev into stable (9 minutes ago)
| |
| o 3:01c6cc386a08:draft stable Chris Torek
| | back to "to" on stable (9 minutes ago)
| |
| o 2:ad954507e465:draft stable Chris Torek
| | s/to/might/ (9 minutes ago)
| |
o | 1:f7521e4f0941:draft dev Chris Torek
|/ s/to/might/ (9 minutes ago)
|
o 0:a163d2c4874b:draft stable Chris Torek
initial (9 minutes ago)
The lga alias is one I stole borrowed copied from someone else:
lga = log -G --style ~/.hgstuff/map-cmdline.lg
where map-cmdline.lg is in the link above. It's just log -G (aka glog) with a more-compact format.
What's going on
When we run:
hg merge dev
Mercurial locates three specific commits:
The current commit on stable, -r3 in this case (the SHA ID will vary), is one of the two endpoint commits.
The target commit on dev is the result of resolving dev to a revision. We can do this ourselves with hg id -r dev for instance:
$ hg id -r dev
f7521e4f0941 (dev)
$ hg id -n -r dev
1
Note that we can do the same thing with # to identify our current revision, although hg summary spills everything out more conveniently.
Last (or in some sense first, though we need the other two to get here), Mercurial locates a merge base commit from these two commits. The merge base is the first commit in the graph that is reachable from both of the other inputs to the merge. In our particular case, that's rev zero, since we split the branches apart right after -r0.
Technically, the merge base is the output of a Lowest Common Ancestor algorithm as run on the Directed Acyclic Graph. See Wikipedia for some examples. There can be more than one LCA; Mercurial picks one at (apparent) random for this case. In our case there is only one LCA though.
Having found the merge base, Mercurial now runs the equivalent of two diff operations:
hg diff -r 0 -r 3
to see what we changed, and:
hg diff -r 0 -r 1
to see what they changed, since the merge base snapshot.1 If we do this ourselves, we see what Mercurial sees:
$ hg diff -r 0 -r 3
$ hg diff -r 0 -r 1
diff --git a/file.txt b/file.txt
--- a/file.txt
+++ b/file.txt
## -1,5 +1,5 ##
This
file
-to
+might
be
merged
(I have my hg diff configured with git = true so that I get diffs that I can feed to Git—long ago I was doing a lot of conversion work here.)
As far as Mercurial is concerned, then, we did nothing on our branch. So it combines do nothing with make this change to file.txt and comes up with this one change to file.txt. That one change is applied to the files from the merge base commit. The resulting files—well, file, singular, in this case—are the ones that are ready to go into the final merge commit, even though they're not the ones you wanted.
Because Mercurial has more information than Git—in particular, which branch something happened on—it would be possible for Mercurial to behave differently from Git here. But in fact, both do the same thing with this kind of operation. They both find a merge base snapshot, compare the snapshot to the two input commit snapshots, and apply the resulting combined changeset to the files from the merge base. Mercurial can do a better job of catching file renames (since it knows them, vs Git, which just has to guess) and could do a different job of merging here, but doesn't.
1Some might object that Mercurial stores changesets, not snapshots. This is true—or rather, sort of true: every once in a while, Mercurial stores a new copy of a file, instead of a change for it. But as long as we have all the commits needed, storing changes vs storing snapshots is pretty much irrelevant. Given two adjacent snapshots, we can find a changeset, and given one snapshot and a changeset to move forward or backward, we can compute a new snapshot. That's how we can extract a snapshot in Mercurial (which stores changesets), or show a changeset in Git (which stores snapshots).
Script: mktest.hg
#! /bin/sh
d=test-hg-graft
test "$1" = replay && rm -rf $d
if test -e $d; then
echo "fatal: $d already exists" 1>&2
exit 1
fi
set -e
mkdir $d
cd $d
hg init
hg branch stable
cat << END > file.txt
This
file
to
be
merged
END
hg add file.txt
hg commit -m initial
hg branch dev
ed file.txt << END
3s/to/might/
w
q
END
hg commit -m 's/to/might/'
hg checkout stable
hg graft -r 1 # pick up s/to/might/; graft makes its own commit
ed file.txt << END
3s/might/to/
w
q
END
hg commit -m 'back to "to" on stable'
hg merge dev
hg commit -m "merge dev into stable"
When you do an hg pull which results in multiple heads, which of the heads is 'active' on your file system?
Ie: Given a local repostory with changesets:
A1 - A2 - A3
And then doing an hg pull which results in multiple heads:
A1 - A2 - A3
\
B3
Prior to doing an hg merge, which changeset will be active on the file system? Will it be A3 or B3?
Not sure what you mean by "active on the filesystem", but after doing hg pull the working directory is not changed. You need to do hg update to update the working folder to one of the pulled commits.
When doing hg update you can specify the changeset you need. If you don't, the working directory is updated to the tip of the current named branch.
I've got a project running under Mercurial and am finding a lot of situations where a file needs manually merging, when I believe it should be able to merge automatically. I am wondering whether there are any options that can be given to Mercurial to help it out in these areas.
The project has an underlying platform with a couple of hundred files that can't be edited on the project. When the platform is updated, the project gets updated versions of these core files outside of Mercurial. The sequence I'm seeing repeatedly is:
On central dev system (linked to the core platform update mechanism):
Get a new version of core platform.
Commit these changes e.g. hg commit -m "New platform release"
Push to central mercurial server
On my Linux box:
Commit local changes
Pull from central mercurial server, and try to merge
Find merge conflicts on core files
The last two core files I've had to merge have no changes between the base and local versions (the access time is updated during a build, but the content is the same). The only changes are on the remote revision I'm merging with.
The only non-standard configuration I'm aware of is that the central mercurial instance is running under Rhodecode, with a commit hook setup to update a Redmine repository.
Is there anything else that can be configured in mercurial to help it figure out merges?
You can redo a merge with --debug to get more information about a merge. That is, take your repository and do
$ cd ..
$ hg clone my-project -r 123 -r 456 merge-test
where 123 and 456 is the two parents of the merge you want to examine closer. Then run
$ hg merge --debug
to see what Mercurial says. It should look like this if the file foo has only been changed in the branch you're merging in:
$ hg merge --debug
searching for copies back to rev 2
resolving manifests
overwrite: False, partial: False
ancestor: 932f5550d0ce, local: b0c286a4a76d+, remote: c491d1593652
foo: remote is newer -> g
updating: foo 1/1 files (100.00%)
getting foo
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
Here I was on revision b0c286a4a76d and merged with c491d1593652.
You can also use
$ hg status --rev "ancestor(b0c286a4a76d, c491d1593652)" --rev "c491d1593652"
M foo
$ hg status --rev "ancestor(b0c286a4a76d, c491d1593652)" --rev "b0c286a4a76d"
M bar
to double-check which files have been changed between the ancestor revision and the two changesets you're merging. Above you see that I changed foo on one branch and bar on the other.
If you see a platform file appear in both status lists, well then something went wrong in your procedures and this can explain the merge conflicts.
If this isn't enough to figure out what went wrong, then I suggest asking this question on the Mercurial mailinglist. That's a great place for discussion and bug-hunting — much better than Stack Overflow.
Take this repo structure:
Server (main repo)
ProjectA (subrepo)
SharedLibrary (subrepo)
Client (main repo)
ProjectB (subrepo)
SharedLibrary (subrepo)
SharedLibrary points to the same folder (this is Windows), it is not a separate copy/clone under each main repo.
Assume each main repo has two changesets, 0 and 1(tip). We start with both main repos at the 1(tip) revision.
Take the following steps:
In the Client repo, update to changeset 0. This updates ProjectB and SharedLibrary to earlier but matching revisions.
ProjectA is now out of sync with SharedLibrary. Step 1 updated SharedLibrary to an older revision than what is required for ProjectA, which is still at 1(tip).
In the Server repo, we want to update SharedLibrary to the correct revision for ProjectA, so we run hg update tip in the Server main repo. This does NOT update SharedLibrary to the correct revision. It leaves SharedLibrary in the same revision as step one.
Go back to Client repo and run hg update tip. SharedLibrary is now at the correct revision for both ProjectA and ProjectB.
It appears updating in the Server repo isn't checking to see if SharedLibrary is at the correct revision. Is this behavior expected, or is there a better way to do this?
What you're seeing is that hg update will merge when the working copy is dirty. Let me explain it with a normal file first. Imagine you have a repository with two revisions. I'm at revision 0 and foo is modified:
$ hg diff
diff --git a/foo b/foo
--- a/foo
+++ b/foo
## -1,3 +1,3 ##
first
second
-third
+third line
You see I changed the third line. Now, if I run hg update 1 the modification will be merged with how foo looks like in revision 1:
$ hg update 1
merging foo
0 files updated, 1 files merged, 0 files removed, 0 files unresolved
The modification is still there and foo is still dirty:
$ hg diff
diff --git a/foo b/foo
--- a/foo
+++ b/foo
## -1,3 +1,3 ##
first line
second
-third
+third line
When you did
$ cd client
$ hg update 0
you made sure that SharedLibrary was updated to the revision described in .hgsubstate for revision 0 in client.
When you then went to server, the SharedLibrary subrepo was no longer at the revision mentioned in .hgsubstate at revision 1 in server. In other words, the working copy in server was dirty — a hg commit would result in a new .hgsubstate file being committed.
Mercurial preserves this modification when you hg update in server and this is why you see that SharedLibrary wasn't made current when you updated. Use hg update -C if you want to make the subrepo current.
The idea behind this feature is that you can test different versions of your subrepos. When hunting for a bug, it's often necessary to update the main repository to older versions and so it's convenient that the modifications to the subrepo revisions stay in place.
Note that the confusing situation you're seeing isn't caused by how you've re-used the same subrepo twice. However, as Lasse points out, the real way to use a single subrepo in multiple projects is to put it once on your server and then clone it into your local clones — once per clone.
I've described this in more detail, but briefly you should follow the recommendations and maintain an identical structure on both server and clients. Use SharedLibrary = SharedLibrary paths in your .hgsub file to maintain this structure. Link the repositories together on the server-side (see my other answer) to make a single repository appear under several different URLs/directories.
When starting out with subrepos, then beware of tight coupling. If you can, then try to use a proper dependency management system such as Maven+Nexus for Java based projects.