views:

1060

answers:

3

We have a Current branch where the main development happens. For a while I have been working on something kind of experimental in a separate branch. In other words I branched what I needed from the Current branch into an Experimental branch. While working I have regularly merged Current into Experimental so that I have the changes others have made, so that I am sure what I make work with their changes.

I now want to merge back into Current. First I merged Current into Experimental, compiled and made sure everything was working. So in my head, Experimental and Current should be "in sync". But when I try to merge Experimental back into Current, I get a whole bunch of conflicts. But I thought I had already kind of solved those when I merged Current into Experimental.

What is going on? Have I totally misunderstood something? How can I do this smoothly? Really don't want to go through all of those conflicts...

+2  A: 

This has happened to me before. When TFS merges Experimental into Current, it does so using the workspaces on your hard drive. If your Current workspace is out of date on your local computer, TFS will get merge conflicts.

(Experimental on HD) != (Current in TFS) != (Old Current on HD)

Try doing a forced get of Current to refresh your local coppy of Current and try the merge again.

Ryan Michela
Tried that, but didn't seem to help anything.
Svish
Good guess, nonetheless. This can definitely cause symptoms like Svish's.
Richard Berg
A: 

You probably have lines like this before you start the merge...

  • Main branch - Contains code A, B, C
  • Current branch - Contains code A, B, C, D, E
  • Experimental branch - Contains code A, B, C, D, F, G, H

When you push from Current to Exp, you are merging feature E into the experimental branch.

When you then push from Exp to Current, you still have to merge F, G, and H. This is where your conflicts are likely rooted.

----Response to 1st comment---- Do you auto merge, or use the merge tool? What is an example of something that is "in conflict"?

StingyJack
I only have two branches. And, to continue on your example, I have already merged Current into Experimental, so that Experimental contains E as well. So there should be no conflicts, only new code. If that made any sense...
Svish
Tried to auto merge when possible, but it wasn't always.
Svish
+6  A: 

When you click Resolve on an individual conflict, what does the summary message say? If your merges from Current -> Experimental were completed without major manual work, it should be something like "X source, 0 target, Y both, 0 conflicting." In other words, there are no content blocks in the target (Current) file that aren't already in the source branch's copy (Experimental). You can safely use the AutoMerge All button.

Note: AutoMerge should be safe regardless. It's optimized to be conservative about early warnings, not for the ability to solve every case. But I recognize that many of us -- myself included -- like to fire up the merge tool when there's any question. In the scenario described, IMO, even the most skittish can rest easy.


Why is there a conflict at all? And what if the summary message isn't so cut & dry? Glad you asked :) Short answer - because the calculation that determines the common ancestor ("base") of related files depends heavily on how prior merge conflicts between them were resolved. Simple example:

  1. set up two branches, A and B.
  2. make edits to A\foo.cs and B\foo.cs in separate parts of the file
  3. merge A -> B
  4. AutoMerge the conflict
  5. merge B -> A

TFS must flag this sequence of events as conflicting. The closest common ancestor between B\foo.cs;4 and A\foo.cs;2 lies all the way back at step 1, and both sides have obviously changed since then.

It's tempting to say that A & B are in sync after step 4. (More precisely: that the common ancestor for step 5's merge is version #2). Surely a successful content merge implies that B\foo.cs contains all the changes made to date? Unfortunately there are a number of reasons you cannot assume this:

  • Generality: not all conflicts can be AutoMerged. You need criteria that apply to both scenarios.

  • Correctness: even when AutoMerge succeeds, it doesn't always generate valid code. A classic example arises when two people add the same field to different parts of a class definition.

  • Flexibility: every source control user has their own favorite merge tools. And they need the ability to continue development/testing between the initial Resolve decision ["need to merge the contents somehow, someday"] and the final Checkin ["here, this works"].

  • Architecture: in a centralized system like TFS, the server simply can't trust anything but its own database + the API's validation requirements. So long as the input meets spec, the server shouldn't try to distinguish how various types of content merges were performed. (If you think the scenarios so far are easily distinguished, consider: what if the AutoMerge engine has a bug? What if a rogue client calls the webservice directly with arbitrary file contents? Only scratching the surface here...servers have to be skeptical for a reason!) All it can safely calculate is you sent me a resulting file that doesn't match the source or target.

Putting these requirements together, you end up with a design that lumps our actions in step 4 into a fairly broad category that also includes manual merges resulting from overlapping edits, content merges [auto or not] provided by 3rd party tools, and files hand-edited after the fact. In TFS terminology this is an AcceptMerge resolution. Once recorded as such, the Rules of Merge(tm) have to assume the worst in pursuit of historical integrity and the safety of future operations. In the process your semantic intentions for Step 4 ("fully incorporate into B every change that was made to A in #2") were dumbed down to a few bytes of pure logic ("give B the following new contents + credit for handling #2"). While unfortunate, it's "just" a UX / education problem. People get far angrier when the Rules of Merge make bad assumptions that lead to broken code and data loss. By contrast, all you have to do is click a button.

FWIW, there are many other endings to this story. If you chose Copy From Source Branch [aka AcceptTheirs] in step 4, there would be no conflict in step 5. Ditto if you chose an AcceptMerge resolution but happened to commit a file with the same MD5 hash as A\foo.cs;2. If you chose Keep Target [aka AcceptYours] instead, the downstream consequences change yet again, though I can't remember the details right now. All of the above get quite complex when you add other changetypes (especially Rename), merge branches that are far more out of sync than in my example, cherry pick certain version ranges and deal with the orphans later, etc....


EDIT: as fate would have it, someone else just asked the exact same question on the MSDN forum. As tends to be my nature, I wrote them another long answer that came out completely different! (though obviously touching on the same key points) Hope this helps: http://social.msdn.microsoft.com/Forums/en-US/tfsversioncontrol/thread/e567b8ed-fc66-4b2b-a330-7c7d3a93cf1a

Richard Berg
Woah, good and thorough explanation. Not sure if I fully get it all, but I guess that is why I am not on the TFS team :P Anyways, makes sense that it is not as simple as I wanted it to be :P I ended up having to go through them all. Some of them were rename conflicts, where I was just asked to choose one of the alternatives. The others were a bit more complex. But it seems to be working now :)
Svish
I posted another explanation, hopefully less confusing :)
Richard Berg