2

We use TFS 2010 and have three branches: Dev -> QA -> Production.

In this particular example we decided to change the method parameter type and a name from "Guid reportGuid" to "int reportId". We did this change in the QA branch and checked it in. And now I'm trying to pull this change from QA to Dev branch. Usually it is done in the opposite direction but in this exceptional case we did what we did. Here is screenshot of merge tool. As you can see from the screenshot, the last check-in on the file in the Dev branch got change-set number 30282 and in the QA - 31002 (which is newer than 30282). As for me it's quite obvious that newer change should override an older one. But what TFS merge does is opposite. It applies old code from Dev branch (30282) over new code from QA branch (31002) and the result doesn't get the change.

Can somebody explain why TFS merge acts like that? Thanks in advance.

davidoff
  • 2,175
  • 3
  • 16
  • 23
  • 2
    Your link to uploaded image doesn't work. – Jacob Seleznev Jun 06 '12 at 12:52
  • Thanks, Jacob. I updated image link. – davidoff Jun 06 '12 at 15:39
  • 2
    Important to note that the tool pictured is **NOT** a three-way merge tool. Perhaps somewhat unintuitively, a three-way merge tool [will show **four windows of text**, as with kdiff3](http://naleid.com/blog/wp-content/uploads/2012/01/kdiff3_merge_window_fixed.png) -- 1.) "Yours" 2.) Theirs 3.) **The shared parent of 1. & 2.** and 4.) What you're editing now. – ruffin Dec 05 '12 at 18:29
  • @ruffin No. It's absolutely a three-way merge tool. Whether the tool uses a separate window for ancestor and another for the merge output, or it combines them, the algorithm is the same. A baseless merge of two files cannot identify the changes. – Edward Thomson Aug 20 '14 at 15:14
  • @EdwardThomson: We obviously agree on the truism of your last point. If you're saying CS31002 reflects the common ancestor, & 30282 is blue to reflect a change, that's fine. But what would it show if 31002 & 30282 were *both* different from the ancestor? My point is limited to saying that *you can't infer the relationship by looking at the picture of the tool*. If you wanted my comment amended to, "strangely, the tool pictured does not depict **3.) The shared parent** to allow the user to confirm its three-way merge", fair enough. ;^) Without the color, it essentially depicts a baseless merge. – ruffin Aug 20 '14 at 19:44
  • If 31002 and 30282 are both different, it shows the contents from the common ancestor in red, no? I don't still have this (admittedly terrible) tool on my machine. But to suggest that it's merely a "baseless" merge (which is not a merge at all, of course, but a diff between two branches) is not really accurate. – Edward Thomson Aug 20 '14 at 19:47

3 Answers3

8

You're posting a screenshot of the 3-way merge tool - and I suspect you're attributing to many smarts to it. TFS uses any 3-way merge tool for conflict resolution - by default, it includes the one pictured, however you can configure it to use any 3-way merge tool of your choice.

When a conflict is detected by TFS (in this case, a merge conflict, where the files were edited in both branches and then merged), it will attempt to automerge the contents of the files. If an automerge is impossible, it will require you to use the configured 3-way merge tool to resolve the conflict. TFS simply hands that tool 3 paths: "source" (the file in the branch that is the source of the merge), "target" (the file in the branch that is the target of the merge) and the "base" or common ancestor.

The common ancestor is the last version of the file that was merged between the two branches. If no merge has been performed, the common ancestor is the changeset that the file was branched at. This will remain the case until the first time you merge changes from the source to the target. For example, consider $/Main/A.txt is branched to $/Branch/A.txt at changeset 2. If an edit occurs on $/Main/A.txt (as changeset 3), and an edit occurs on $/Branch/A.txt (as changeset 4) then when you try to merge $/Branch to $/Main, you will have a merge conflict on A.txt. The common ancestor in this case will be changeset 2 (the version that was branched.)

If you were to then resolve that conflict in your merge tool and check in the result (as changeset 5), then the next time you merge from $/Branch to $/Main, the common ancestor of A.txt will be changeset 5. (Indeed, if you invoke "compare source to base" or "compare target to base" during TFS conflict resolution, you should be able to see the common ancestor as well as its version information.)

In any case, once the merge tool is invoked, it's ultimately the tool's responsibility to prompt you to deal with those changes. The workflow is tool-dependent, but a typical merge tool will compare the files line-by-line and will identify each line as one of the following:

  • Unchanged: lines that are unchanged in the source and target from the ancestor
  • Common: lines that have been changed from the common ancestor, but identically so
  • Conflicting: lines that have been changed in both the source and target and have different content
  • Source only: lines that have only changed in the source (the target is identical to the common ancestor)
  • Target only: lines that have only changed in the target (the source is identical to the common ancestor)

If there exist no conflicting lines, then an "automerge" can be performed, meaning that the common ancestor is modified by taking the source-only lines from the source, the target-only lines from the target and the common lines from either file to produce the merge output. (TFS will offer this as the "automerge" option, if it's possible.)

Note that just because an automerge is possible (and typically works in practice) that it's exactly as naive as taking lines and no syntax checking is performed so it's possible that your automerged output is not really what you want.

Some 3-way merge tools may offer a mode in which they do a partial automerge - either by default upon opening or after some interaction - taking the common, source-only and target-only lines and then requiring you to resolve the conflicts manually.

The merge tool in your screen shot is the default tool bundled with Visual Studio ALM. The tool in TFS 2012 is significantly improved over that version. Regardless, you may have a better experience with a third-party merge tool.

Note that despite the labels, the merge tool doesn't actually have any idea which file is newer chronologically. (TFS provides the labels to the merge tool to provide you some context about those files, the merge tool just treats them as opaque strings.) Nor is "newer chronologically" necessarily the best merge strategy in all branching strategies. (I work on a team that uses a feature branch strategy - my feature branch has a relatively high velocity and I merge in changes from a main branch that takes well-tested changes from all the feature branches at a comparatively slower cadence. In this case, chronology is fairly unimportant and I need to merge my conflicts regardless.

Community
  • 1
  • 1
Edward Thomson
  • 74,857
  • 14
  • 158
  • 187
  • Thanks a lot for your answer. I understood pretty much how merge tool works. But one thing I still a mystery for me is how "common ancestor" is defined. Once I get that I will understand the result my merge tool produces. In my case, we created Dev branch from QA branch. In that merge QA is a source and Dev is a target. Here is history of check-ins in both branches [dev_hist](http://www.freeimgshost.com/fullsize/1vulqalily13j0xnx75.png) and [qa_hist](http://www.freeimgshost.com/fullsize/nnb1jq7exu8abc2y89v.png) ). Which file (version of changeset) will be used as "common ancestor"? – davidoff Jun 06 '12 at 20:13
  • @davidoff I expanded a bit on how the common ancestor is determined - I hope this is a bit more clear? – Edward Thomson Jun 06 '12 at 21:52
  • Edward, what you said is perfectly clear. Nevertheless it doesn’t explain my case. And I’m pretty much sure you can explain it once I share more details. I hid our real branch structure. In reality we have two development branches: HEAP and FP. They are child braches of QA branch. HEAP was created at change-set 30282 and FP at chanset 30279. (I'll go to another comment as max comment lenght is limited) – davidoff Jun 07 '12 at 02:48
  • We worked on FP branch and at some point reverse integrated new features from FP branch with QA branch (change-set 30560). Now I’m trying to pull changes on one particular file to my second dev branch (HEAP branch). And “common ancestor” should be the change-set that the file was branched at (since we never merged that file between HEAP and QA - If I look to the history of the file in HEAP branch I get only one entry with “branch” change type). But when I use 3-way merge tool it shows change-set 30560 as a base file. Can you explain why base is 30560 but not 30282? – davidoff Jun 07 '12 at 02:49
3

Looks like you've got a merge conflict on this screenshot right?

First, don't use the merge tool of visual studio/TFS, it's really not effective and not really intuitive. Use the free KDiff3 or Beyond Compare to get a better auto merge and a more user friendly interface.

Second, as far as I know there's no such thing as temporal coherence for merge in TFS (I may be wrong, but I've never heard about it). The two changeset you mentioned are not in the same branch so TFS doesn't care about which changeset happened after the other and what does it means.

Third, the direction of the merge doesn't change anything from branch1 to branch2 or branch2 to branch1. The only thing that changes is when both branches are directly related or not.

For me your issue is mainly a GUI based one, or maybe you used the auto-merge feature which is not smart at all in case of conflicts.

  1. Change your merge tool following James Manning's post
  2. Stay away from auto-merge
Nock
  • 6,561
  • 1
  • 28
  • 27
  • Thanks, Nock. I tried using KDiff3. It's little bit unusual for me so far, but I have to admit that KDiff3 does provide more information during merge operation and it is easier to follow the merging logic. – davidoff Jun 06 '12 at 20:44
  • After reading Edward's explanation and looking to a "base" file it's very obvious why result has "Guid" (not "int") as a param type. – davidoff Jun 06 '12 at 20:50
3

It seems TFS doesn't support indirect integrations, i.e. merging from one branch to another. You have to merge back to the parent, then merge to your other branch (DEV) See this post: http://social.msdn.microsoft.com/forums/en-US/tfsversioncontrol/thread/85c4ce74-59bc-4bec-a6e9-32b6e875a15a/

cpv
  • 39
  • 1
  • Indirect merging can be forced using the baseless option, but it ignores the merge history. As the original question was about indirect merging (QA -> DEV), and why TFS automatic merge was ignoring the history, the answer is still applies. – cpv Dec 13 '12 at 23:44