Why is '-Di' not the default for a 'p4 integrate'? - version-control

Imagine the following scenarion: we have two perforce-managed directories: trunk and branch.
File1 is added to branch and then integrated to trunk
File1 is renamed File2 on the branch
a new File1 is added to the branch
In this case, the default integrate from branch to trunk will not contain File1, until the integrate is committed and a second integrate will then pick up the change.
This problem can be circumvented by doing the integrate with the -Di option. My question is: why is this option not the default? Does it lead to problems elsewhere?

Yes. A good example of how -Di might be problematic is the case where File1 has been edited on the trunk and/or branch.
Suppose that the old File1 contains "A", trunk/File1 is "AB", and branch/File2 is "AC". Suppose the new File1 is "D".
branch/File1: D
branch/File2: AC
trunk/File1: AB
If you do the default integrate, you start by merging branch/File2 with trunk/File1, which both merges the content (producing "ABC") and then moves trunk/File1 to trunk/File2. The follow-up integrate branches branch/File1 to the now-vacant spot on trunk, so trunk/File1 becomes "D". This is the ideal outcome.
If you do the integrate with -Di, you branch branch/File2 to trunk/File2, giving you a trunk/File2 with content "AC", and you do a baseless merge between branch/File1 and trunk/File1, with "AB" on one side and "D" on the other.
So in summary, with the default (two step integrate) you get:
trunk/File1: D (branch)
trunk/File2: AB+AC=ABC (clean merge)
With the -Di (one step baseless integrate) you get:
trunk/File1: D+AB=??? (baseless merge)
trunk/File2: AC (branch)
In theory you can sort this out by resolving the conflict, picking through the history manually, noticing that the B change should be in another file, and then manually editing it into File2. You're likely to end up having to repeat this performance when integrating into other branches (and now it's complicated by the fact that the connection between File2 and File1 is less obvious).

Related

Mercurial merge results in files labelled modified but which are binary equal

I'm in the process of doing a merge, and I'm ready to commit at this point but my commit dialog in TortoiseHg is showing many files as modified but when I diff to parents it says all files are binary equal.
I do not have and have never had the eol extension enabled.
Revert changes nothing, the file is still registering as modified.
hg parents shows two parents for the file.
hg stat shows the file as modified, e.g.
c:\Projects\MyProject>hg stat Authorization\AuthorityGroups.cs
M Authorization\AuthorityGroups.cs
hg diff --git shows nothing, e.g.
c:\Projects\MyProject>hg diff --git Authorization\AuthorityGroups.cs
c:\Projects\MyProject>
I've tried this on two different machines on two separate clones and I'm seeing the same thing.
Any other thoughts for how I could diagnose or fix this?
Clearly something has changed but if it's not showing in hg diff --git how can I establish what that might be?
Update 2014/12/10:
I've done a bit more checking on the history of the two parent revisions and I think I see why it's getting confused.
We've got the original parent file added in revision 1 on default.
On the Apple branch the file has been renamed to move it to a new location.
On the Orange branch the file has been added to move it to the same new location.
So the file on both branches is binary identical and at the same location, but presumably Mercurial is flagging it as a difference to be merged because they arrived there by apparently different means.
So the question then becomes:
Is there any way to retrospectively repair the move being treated as an add and delete on a long committed changeset (a new commit would be fine, but I can't edit the history) , or do I just need to let it go through in the merge?
Is there any way to retrospectively repair the move being treated as an add and delete on a long committed changeset (a new commit would be fine, but I can't edit the history)
Well... sort of. Update to the most recent Orange commit in which the files had their old names (you can use hg bisect to find it if you're not sure exactly when it happened), do hg rename to the new names, commit, and then merge this into the current Orange head. Mercurial should be smart enough to register the files as properly renamed, and it won't cause conflicts (we know this because the more complex Apple/Orange merge didn't).
or do I just need to let it go through in the merge?
This is easier. Mercurial's merging algorithm is quite smart. It can deal with situations like this just fine.
Unless you have a third branch in which the files were never moved, the second option is unlikely to cause a problem. If you do have such a branch, you should be fine as long as you merge it into a descendant of the Apple rename (or merge from such a descendant). The major difficulty would be with merges to or from the Orange branch.

Fossil SCM - Revert to a specific revision like Mercurial

While using Mercurial if I want to change the current working copy with a specific revision I just do:
$> hg revert good_revision
$> hg commit -m "Now I'm in the good revision"
Then I can see that all my files are int the good_revision state and can start working on it.
So far on fossil I can do a revert but only on specific files, not the entire repository, and update or checkout don't seem to work as I would expect.
How can fossil revert my entire repository to a certain revision?
I'm not sure I quite follow but I think what you want is to be able to create a "multiple heads on one branch" sutuation in Fossil. If yes, then Fossil does support this, just it calls branch's heads "leaves", and this process is called "forking".
To do that, you
fossil update good_revision
and then
fossil commit --allow-fork
You may now spawn fossil ui, navigate to your branch and see it having two leaves.
You now may close the then-current leaf.
Note that, while supported, this does not appear to be a recommended practice. Instead, Fossil recommends a rather peculiar approach for throwing away changes:
Rename the branch at the "bad" leaf to "mistake" (or create that branch if it doesn't yet exist). By doing this you effectively "mark" the resulting subleaf as a mistake.
Note that the name "mistake" name is just a convention; this branch does not exist in a freshly created repository.
Close the "bad" leaf.
Return to the last-good state using fossil update, continue hacking.
Since that "last-good" commit still inherits the branch tag of its parent commit, the next commit you record will also inherit it and won't be on the branch "mistake".
For an example, see how it looks in the SQLite repo—there's a bunch of assorted short chains of commits on this branch. See also this.
My slightly different solution to what I think I understand the question to be (paraphrased: how to work on an older "good_revision" than current leaf of this branch/trunk that I'll call bad_leaf, and treat changes since "good_revision" as bad), which is sort of equivalent to applying diffs between the two versions but in reverse from/to order:
Merge in a (empty) fork from the good_revision, using the baseline from the bad_leaf instead of the default, last common commit; hence the diffs that will be applied are the original branch's differences back to a good_revision fork you create as it won't see they've already been applied. Using the latest as baseline "hides" those which would otherwise make it ignore all changes as they're already applied.
fossil update good_revision
fossil commit --allow-fork --allow-empty
# note the uuid from that commit (for use as forked_basis below)
fossil merge -f --integrate --baseline bad_leaf forked_basis
Then of course once happy,
fossil commit
It doesn't create any branches that should be called "mistake", it just applies the reverse diffs from good_revision to bad_leaf into bad_leaf to put you back where you were and you can continue committing to that same (new) leaf that used to be at bad_leaf.
A diff (straight gnu, not fossil diff) against a checkout at the original good_revision compared to a checkout after the above commands matched. Except for empty directories that had lost their files, but fossil doesn't track/tidy-up dead directories anyway.
caveat: I haven't been using fossil that long and its different in several ways to the common ways I've been used to with cvs/svn/git/hg/ perforce/clearcase.
Reason for adding this answer: I found the existing answers harder to understand and wasn't sure I trusted myself to do them correctly as a result.
If I understand your question correctly, there was a problem somewhere along the development cycle and you would now like to go back one or more revisions to a known good revision and start using that. Furthermore, you'd like that revision to become your trunk. The approach in Fossil is similar to that of Mercurial:
fossil revert -r good_revision
fossil commit -m "Now I'm in the good revision"
This will replace the files in the working directory with the ones from the specified revision. The commit will commit them to whatever branch you're working on (I am assuming that it is the trunk in this example). If you don't specify the revision number, it will use the last committed version.
One of the more common usages of the revert command is to roll back a single file:
fossil revert -r good_revision my_file
(or)
fossil revert my_file_from_the_last_commit
However, as shown in the first example, leaving out the file name causes all files to be reverted. For further information please see https://www.fossil-scm.org/index.html/help?cmd=revert
Sorry for the latest response, but I just came across the question while looking for something else. I post this in case someone else is looking into how to revert to a previously committed version stored in Fossil.

How to prevent divergent mercurial branches from merging?

I have a project stored in mercurial which has grown well beyond its original remit, and now I want to split the repository into two separate projects. Say the file tree looks something like this:
src/
a/
a.h
a.c
b/
b.h
b.c
Say I've hg cloned this repository twice, and in the first clone I've performed an hg rm src/b/*, and in the second I've performed an hg rm src/a/*. This gives me the two separate source trees I want, with history and revision numbering preserved in each branch.
The problem I now have is what happens if, in the future, I ever accidentally pull from the a/ clone into the b/ clone. What I'd like is for mercurial to simply refuse to pull from, or push to, the other branch, if I ever try to do so, as if the two had never shared history.
Is this possible? If not, is there a canonical way to create two parallel projects from one original which preserves history and revision numbering?
The canonical way would be to use hg convert with a filemap like this:
exclude b
rename a .
(and afterwards with the roles of a and b reversed to create the second repository)
This way you end up with two unrelated repositories (which Mercurial will refuse to push/pull without the use of force) only containing the history of a single directory.
Also see this question for reference.

Why is a 3-way merge advantageous over a 2-way merge?

Wikipedia says a 3-way merge is less error-prone than a 2-way merge, and often times doesn't need user intervention. Why is this the case?
An example where a 3-way merge succeeds and a 2-way merge fails would be helpful.
Say you and your friend both checked out a file, and made some changes to it. You removed a line at the beginning, and your friend added a line at the end. Then he committed his file, and you need to merge his changes into your copy.
If you were doing a two-way merge (in other words, a diff), the tool could compare the two files, and see that the first and last lines are different. But how would it know what to do with the differences? Should the merged version include the first line? Should it include the last line?
With a three-way merge, it can compare the two files, but it can also compare each of them against the original copy (before either of you changed it). So it can see that you removed the first line, and that your friend added the last line. And it can use that information to produce the merged version.
This slide from a perforce presentation is interesting:
The essential logic of a three-way merge tool is simple:
Compare base, source, and target files
Identify the "chunks" in the source and target files file:
Chunks that don't match the base
Chunks that do match the base
Then, put together a merged result consisting of:
The chunks that match one another in all 3 files
The chunks that don't match the base in either the source or in the target but not in both
The chunks that don't match the base but that do match each other (i.e., they've been changed the same way in both the source and the target)
Placeholders for the chunks that conflict, to be resolved by the user.
Note that the "chunks" in this illustration are purely symbolic. Each could represent lines in a file, or nodes in a hierarchy, or even files in a directory. It all depends on what a particular merge tool is capable of.
You may be asking what advantage a 3-way merge offers over a 2-way merge. Actually, there is no such thing as a two-way merge, only tools that diff two files and allow you to "merge" by picking chunks from one file or the other.
Only a 3-way merge gives you the ability to know whether or not a chunk is a change from the origin and whether or not changes conflict.
A three-way merge is where two changesets to one base file are merged as they are applied, as opposed to applying one, then merging the result with the other.
For example, having two changes where a line is added in the same place could be interpreted as two additions, not a change of one line.
For example, file a has been modified by two people, one adding moose, one adding mouse.
#File a
dog
cat
#diff b, a
dog
+++ mouse
cat
#diff c, a
dog
+++ moose
cat
Now, if we merge the changesets as we apply them, we will get (3-way merge)
#diff b and c, a
dog
+++ mouse
+++ moose
cat
But if we apply b, then look at the change from b to c it will look like we are just changing a 'u' to an 'o' (2-way merge)
#diff b, c
dog
--- mouse
+++ moose
cat
Borrowed from AWS CodeCommit:
Developer Tools > CodeCommit > Repositories > RepositoryName >
Pull requests > Pull request name > Merge

How can I back out a merge in Mercurial and later remerge with that branch?

I have two branches, default and branch1. By mistake one person in our team merged branch1 with default. The content in branch1 is not yet ready to merge with default (it contains a major rework of the build & deploy environment).
We did an experiment with 'hg backout', backing out the merge (not sure this is the right way to do it). Then the changes from branch1 gets deleted on default, which is fine - but we can not remerge with branch1.
How should we solve this issue?
There are many scenarios here where you might want to do this, I'll make each scenario a headline, so that you can find the scenario that fits your case. Note that I'm still learning Mercurial, and I'd like pointers if something I say is wrong, using the wrong terminology, could be done better, etc.
No further changes, merge not shared (no pushes/pulls)
The programmer has merged, but not done anything else, nor has (s)he shared the changes with anyone, in any way
In this case, simply discard the local clone, and get a fresh clone from a safe repository.
Local changes on top of merge, not shared
The programmer has merged, and continued working based on that merge. The changesets that followed the merge should be kept, but the merge itself should be removed. The changes (merge + following changesets) have not been shared with anyone
In this case I would do one of four:
Try to use the REBASE extension, this will move the changesets from one location to another. If the changesets are based on code-changes that were introduced with the merge, some manual work must be done to reconcile the differences.
Try to use the MQ extension to pull the changesets that are to be kept into a patch-queue, then push them back in a different location. This will, however, have the same problem as the REBASE extension in terms of changes based on the merge
Try to use the TRANSPLANT extension to "copy" the changes from one location to another. Still, same problem exists as with the first two.
Do the work again, probably with the help of a diffing tool to take changes done in the changesets I want to discard, and re-do them in the correct location.
To get rid of the merge changeset + all the following changesets, there's a couple of options:
Use the strip command in the MQ extension
hg strip <hash of merge changeset>
Clone and pull, and specify the hash of the changesets leading up to, but not including the merge. In essence, create a new clone by pulling from the damaged clone into a new one, and avoid pulling in the merge you don't want.
hg clone damaged -r <hash of first parent> .
hg pull damaged -r <hash of second parent>
Merge pushed to others, control over clones
The programmer has pushed to master repository, or to someone else, or someone pulled from the programmers repository. However, you (as in the group of developers) have control over all the repositories, as in, you can contact and talk to everyone before more work is done
In this case, I would see if step 1 or 2 could be done, but it might have to be done in a lot of places, so this might involve a lot of work.
If nobody has done work based on the merge changeset, I would use step 1 or 2 to clean up, then push to the master repository, and ask everyone to get a fresh clone from the master repository.
Merge pushed, you don't have control over clones
The programmer pushed the mergeset, and you don't know who will have the merge changeset. In other words, if you succeed in eradicating it from your repositories, a stray push from someone who still has it will bring it back.
Ignore the merge changeset and work in the two branches as though it never happened. This will leave a dangling head. You can then later, when you've merged the two branches, do a null-merge for this head to get rid of it.
M <-- this is the one you want to disregard
/ \
* *
| |
* *
| |
Simply continue working in the two branches:
| |
* *
| M | <-- this is the one you want to disregard
|/ \|
* *
| |
* *
| |
Then later you merge the two, the real merge you want:
m
/ \
* *
| |
* *
| M | <-- this is the one you want to disregard
|/ \|
* *
| |
* *
| |
You can then do a null-merge to get rid of the dangling head. Unfortunately I don't know how to do that except through TortoiseHg. It has a checkbox where I can discard the changes from one of the branches.
With TortoiseHg, I would update to the merge I want to keep (the topmost, lowercase m), then select and right-click on the dangling merge head below, and then check the "Discard all changes from merge target (other) revision":
We did an experiment with 'hg backout', backing out the merge (not sure this is the right way to do it). Then the changes from branch1 gets deleted on default, which is fine - but we can not remerge with branch1.
I use backout for merge cancel. You can not remerge, but you able to "backout backout merge", i.e. when you want remerge, you make 'hg backout' on "Backed out merge changeset ..." commit and then merge branch again.
Example:
7 M remerge
6 / \
5 * | hg backout 3 (backout backout)
4 | * fix error
3 * | hg backout 2
2 M | fail merge
/ \
1 * *
| |
Thanks to everyone for the great input! Since we were kind of in a rush to solve the problem, and our group is relatively new to Mercurial, we used a very pragmatic solution.
On our repository server we created a new repository, then we cloned the old repository up until the revision right before the merge. Then pushed the new clone to the server and sent out the new link to everyone. Luckily we are a quite small development team.
Perhaps not the most cosher way to solve the problem, but it worked :)
You cannot really backout a merge nicely. IMO, the best way to handle this would be just to abandon the merge and continue the strand of changesets from before the merge, leaving a dangling head (which can be stripped). If other changes have happened since the merge, they can be rebased onto the new "good" head.
This answer assumes that you have already pushed
This would result in (at least one) unresolved head, depending on what you just forgot. More depending on who just pushed from what branch.
I love HG and use it avidly, but their idea of a branch can drive someone batty when coupled with a history that is (by design) intentionally immutable.
I usually clone a backup (locally) of the repo prior to doing a branch merge, for just this reason. I always check before pulling.
Eric Raymond is working on something that is more or less DVCS agnostic that can (hopefully) help in situations just like the oops you described, but I don't think he's going to have HG support fully implemented for another week or two. Still, it might be worth watching.
But, only useful if nobody has pulled the 'ooopsie' tip.
I had this exact issue. A coworker accidentally merged my branch into the default branch while it was still incomplete. Initially I just backed out that merge which seemed to work fine until I wanted to merge my branch into default for keeps. Files that I needed were being marked for deletion upon merging.
The solution was to go all the way back to my original back out that fixed my coworker's mistake and back out that back out. This stopped the files from being marked as deleted and let me successfully merge my branch into default.