monotone-commits-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Monotone-commits-diffs] net.venge.monotone: 806715c5ce888b7dbbce7752a7f


From: code
Subject: [Monotone-commits-diffs] net.venge.monotone: 806715c5ce888b7dbbce7752a7ff35868ee3bcfa
Date: Sat, 4 Aug 2012 14:26:32 +0200 (CEST)

revision:            806715c5ce888b7dbbce7752a7ff35868ee3bcfa
date:                2012-07-23T09:14:16
author:              address@hidden
branch:              net.venge.monotone
changelog:
propagate from branch 'net.venge.monotone.issue-209' (head 
215eae872b62927248079d06e3dc23e2c2d31787)
            to branch 'net.venge.monotone' (head 
1f323fadb24c032518db433178c7cc757914a564)



manifest:
format_version "1"

new_manifest [b18e7ae7b45c52907489c015eef6fccf53eb7663]

old_revision [1f323fadb24c032518db433178c7cc757914a564]

rename "test/func/resolve_conflicts_dropped_modified"
    to "test/func/resolve_conflicts_dropped_modified_1"

rename "test/func/resolve_conflicts_dropped_modified_upstream_vs_local"
    to "test/func/resolve_conflicts_dropped_modified_upstream_vs_local_attr"

add_dir "test/func/resolve_conflicts_dropped_modified_3"

add_dir "test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2"

add_file "test/func/resolve_conflicts_dropped_modified_3/__driver__.lua"
 content [fd2a4dd0c38dff10bd3ff2f20e983f3f548b3d18]

add_file 
"test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2/__driver__.lua"
 content [80f056dd3773dab5d5019d78cbb0f7393d66bde3]

add_file 
"test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2/conflicts_2_3"
 content [1f257fa6e99abf93229af189109b9efe3cfae88f]

add_file 
"test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2/conflicts_2_3_resolved"
 content [d8b9a96fdaf210a4d1bc495c62c3dcf40114d39e]

add_file 
"test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2/conflicts_3_2"
 content [bb23f1fbf87e4f1dcb8aa7c4c3fcb90e1fc84fed]

add_file 
"test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2/conflicts_3_2_resolved"
 content [4482ad2e755308ce8a546191348cc648ad13d782]

patch "doc/monotone.texi"
 from [d1225486c4df3ec2023d0e303aac65b8ff10a54e]
   to [0f1e2230272b68614efad33648dbf436e0d6f729]

patch "src/cmd_conflicts.cc"
 from [e1b7feca5acd314d85498cabedf888f5293bf30e]
   to [28995825acadb97eaa4b5cda378c66e548e9ad16]

patch "src/merge_conflict.cc"
 from [34fbc891bc8455202d9c0d0a495beb690fd8a5a0]
   to [b47a70720f836809f06347360ee91ddf587b5721]

patch "src/merge_content.cc"
 from [662433acaf08fc1f3f3417389a9bc3b3d2bd9bf1]
   to [095c2d20ac8d8d495806f6463c311d253cb6a686]

patch "src/merge_roster.cc"
 from [5fbc50c114df22f4753f444aa137c06ea0f06195]
   to [c3eb70578a3b8b88c4d1b7af43b7685d6f636c23]

patch "src/merge_roster.hh"
 from [34d382b0ad333fa0f052e8640a88c1ece03caaa9]
   to [c782f7684e380f4583ed7ea88b0b2967f89d1d23]

patch "src/roster.cc"
 from [3f81121ce80b42565e6e5e4bbe3e6186b85e9b10]
   to [b0608d6ec3e3f2d23a5c97bb64c4354eeb01773a]

patch "test/func/resolve_conflicts_dropped_modified_1/__driver__.lua"
 from [e4f973e6cb8e3494c4b498f68f7cd4b2a6d20fa6]
   to [2c87a2a5810a34019c4ca4148eb2b9bede764267]

patch "test/func/resolve_conflicts_dropped_modified_1/conflicts-recreated"
 from [26f633aeeee82440eea41f8e1747f2c4262ce9bb]
   to [3f43d9d13d0149e3be0fa3a7a8695182dbc871ea]

patch 
"test/func/resolve_conflicts_dropped_modified_1/conflicts-recreated-resolved"
 from [5082532e55e68bc93610d1e8936fe790bb048d3d]
   to [7937832db9408aeadbbeed472d86f29ef3394a06]

patch "test/func/resolve_conflicts_dropped_modified_1/conflicts-resolved"
 from [dd892da237ef4f3a0ee30fa3989374a65d092d68]
   to [b2fe24f2d19a8d539c0c89c305a7b58a36ead63e]

patch "test/func/resolve_conflicts_dropped_modified_1/show_conflicts"
 from [74f0f311dee5ce970396bed354b6ab0fd97077f0]
   to [cce385ec522c8e4885cac7d3375896aaa507f0e0]

patch "test/func/resolve_conflicts_dropped_modified_1/show_conflicts-orphaned"
 from [fbe2c5cc2c59c6fec1121e3be9469d370b9ed5cb]
   to [e343653ac3989164acb2dbb54f3f97b035d28de6]

patch "test/func/resolve_conflicts_dropped_modified_2/__driver__.lua"
 from [e1bd2fed5032ea4cb911ffc1289b1830e2195e1c]
   to [81b11d9ab24ad470156b95e8be8efd9dc1645156]

patch 
"test/func/resolve_conflicts_dropped_modified_upstream_vs_local_attr/__driver__.lua"
 from [e7f37ed8b1a65325390ad4f1ade33904e7535381]
   to [abe2836cc2dfbae368820e3b5515ad587911c71b]

patch "test/func/resolve_conflicts_errors/__driver__.lua"
 from [9a130c83d4a7e3545f18d089542603c8d0bb72fa]
   to [0819ba223855d9814a7420c7412166b1109ed20f]

patch "test/func/resolve_conflicts_orphaned_file/__driver__.lua"
 from [fe6a4618be2793d18ee2f97adf94cd564763164f]
   to [beb41b901c1e08bbef25e87fe0b2ef36e74a2a8b]

old_revision [215eae872b62927248079d06e3dc23e2c2d31787]

patch "innosetup/README.txt"
 from [6f92ce9f01c75b83994b690bb8b00c549c368965]
   to [d306d15f4bba8011dd1fa01dd9522b93c8ce8920]

patch "src/automate.cc"
 from [919e3df52514d0877aeafc67791aae6a3faa33b4]
   to [30e9bb3567e320f83ce5c1b4817dc9ad90fe8423]
============================================================
--- doc/monotone.texi	d1225486c4df3ec2023d0e303aac65b8ff10a54e
+++ doc/monotone.texi	0f1e2230272b68614efad33648dbf436e0d6f729
@@ -1,4 +1,4 @@
-0\input texinfo   @c -*-texinfo-*-
+\input texinfo   @c -*-texinfo-*-
 @c %**start of header
 @setfilename monotone.info
 @settitle monotone documentation
@@ -5232,6 +5232,16 @@ @subheading Conflict resolutions
 @command{resolved_rename_right @var{filename}} conflict resolution in
 the conflicts file.
 
address@hidden user_rename @var{contents_file} @var{rename_file}
+The file contents are replaced by the contents of the specified file,
+and renamed.
+
+This inserts a @command{resolved_user_left @var{contents_file}} or
address@hidden @var{contents_file}} conflict resolution
+in the conflicts file, and a @command{resolved_rename_left
address@hidden or @command{resolved_rename_right
address@hidden conflict resolution.
+
 @item keep
 The file is kept in the merge.
 
============================================================
--- src/cmd_conflicts.cc	e1b7feca5acd314d85498cabedf888f5293bf30e
+++ src/cmd_conflicts.cc	28995825acadb97eaa4b5cda378c66e548e9ad16
@@ -50,6 +50,16 @@ static void
 typedef enum {first, remaining} show_conflicts_case_t;
 
 static void
+show_resolution(resolve_conflicts::file_resolution_t resolution, char const * const prefix)
+{
+
+  if (resolution.resolution != resolve_conflicts::none)
+    {
+      P(F(string(prefix).append(image(resolution)).c_str()));
+    }
+}
+
+static void
 show_conflicts(database & db, conflicts_t conflicts, show_conflicts_case_t show_case)
 {
   // Go thru the conflicts we know how to resolve in the same order
@@ -60,7 +70,7 @@ show_conflicts(database & db, conflicts_
     {
       orphaned_node_conflict & conflict = *i;
 
-      if (conflict.resolution.first == resolve_conflicts::none)
+      if (conflict.resolution.resolution == resolve_conflicts::none)
         {
           file_path name;
           if (conflicts.left_roster->has_node(conflict.nid))
@@ -90,83 +100,124 @@ show_conflicts(database & db, conflicts_
     {
       dropped_modified_conflict & conflict = *i;
 
-      if (conflict.resolution.first == resolve_conflicts::none)
+      if ((conflict.left_nid != the_null_node &&
+           conflict.left_resolution.resolution == resolve_conflicts::none) ||
+          (conflict.right_nid != the_null_node &&
+           conflict.right_resolution.resolution == resolve_conflicts::none))
         {
-          node_id nid;
           file_path modified_name;
 
-          if (conflict.left_nid == the_null_node)
+          switch (conflict.dropped_side)
             {
-              // left side dropped, right side modified
-              nid = conflict.right_nid;
+            case resolve_conflicts::left_side:
               conflicts.right_roster->get_name(conflict.right_nid, modified_name);
-            }
-          else
-            {
-              // left side modified, right side dropped
-              nid = conflict.left_nid;
+              break;
+
+            case resolve_conflicts::right_side:
               conflicts.left_roster->get_name(conflict.left_nid, modified_name);
+              break;
             }
 
           P(F("conflict: file '%s'") % modified_name);
           if (conflict.orphaned)
             {
-              if (conflict.left_nid == the_null_node)
+              switch (conflict.dropped_side)
                 {
+                case resolve_conflicts::left_side:
                   P(F("orphaned on the left"));
                   P(F("modified on the right"));
-                }
-              else
-                {
+                  break;
+
+                case resolve_conflicts::right_side:
                   P(F("modified on the left"));
                   P(F("orphaned on the right"));
                 }
             }
           else
             {
-              if (conflict.left_nid == the_null_node)
+              switch (conflict.dropped_side)
                 {
-                  if (conflict.recreated == the_null_node)
+                case resolve_conflicts::left_side:
+                  if (conflict.left_nid == the_null_node)
                     P(F("dropped on the left"));
                   else
-                    P(F("dropped and recreated on the left"));
+                    {
+                      // we can't distinguish duplicate name from recreated
+                      P(F("dropped and recreated on the left"));
+                    }
 
                   P(F("modified on the right"));
-                }
-              else
-                {
+                  break;
+
+                case resolve_conflicts::right_side:
                   P(F("modified on the left"));
 
-                  if (conflict.recreated == the_null_node)
+                  if (conflict.right_nid == the_null_node)
                     P(F("dropped on the right"));
                   else
-                    P(F("dropped and recreated on the right"));
+                    {
+                      P(F("dropped and recreated on the right"));
+                    }
                 }
             }
 
-          switch (show_case)
+          show_resolution(conflict.left_resolution, "left_");
+          show_resolution(conflict.right_resolution, "right_");
+
+          if (show_case == remaining) return;
+
+          if (conflict.left_nid == the_null_node || conflict.right_nid == the_null_node)
             {
-            case first:
+              // only one file involved; only need one resolution
               P(F("possible resolutions:"));
+              P(F("resolve_first drop"));
+              P(F("resolve_first rename"));
+              P(F("resolve_first user_rename \"new_content_name\" \"new_file_name\""));
 
-              if (conflict.recreated == the_null_node)
-                P(F("resolve_first drop"));
+              if (!conflict.orphaned)
+                {
+                  P(F("resolve_first keep"));
+                  P(F("resolve_first user \"name\""));
+                }
+              return;
+            }
+          else
+            {
+              // recreated or repeated duplicate name; need two resolutions
 
-              if (conflict.orphaned)
+              P(F("possible resolutions:"));
+
+              if (conflict.left_nid != the_null_node &&
+                  conflict.left_resolution.resolution == resolve_conflicts::none)
                 {
-                  P(F("resolve_first rename"));
-                  P(F("resolve_first user_rename \"new_content_name\" \"new_file_name\""));
-                  return;
+                  P(F("resolve_first_left drop"));
+                  P(F("resolve_first_left rename"));
+                  P(F("resolve_first_left user_rename \"new_content_name\" \"new_file_name\""));
+
+                  if (!conflict.orphaned &&
+                      conflict.right_resolution.resolution != resolve_conflicts::keep &&
+                      conflict.right_resolution.resolution != resolve_conflicts::content_user)
+                    {
+                      P(F("resolve_first_left keep"));
+                      P(F("resolve_first_left user \"name\""));
+                    }
                 }
-              else
+
+              if (conflict.right_nid != the_null_node &&
+                  conflict.right_resolution.resolution == resolve_conflicts::none)
                 {
-                  P(F("resolve_first keep"));
-                  P(F("resolve_first user \"name\""));
-                  return;
+                  P(F("resolve_first_right drop"));
+                  P(F("resolve_first_right rename"));
+                  P(F("resolve_first_right user_rename \"new_content_name\" \"new_file_name\""));
+                  if (!conflict.orphaned &&
+                      conflict.left_resolution.resolution != resolve_conflicts::keep &&
+                      conflict.left_resolution.resolution != resolve_conflicts::content_user)
+                    {
+                      P(F("resolve_first_right keep"));
+                      P(F("resolve_first_right user \"name\""));
+                    }
                 }
-
-            case remaining:
-              break;
+              return;
             }
         }
     }
@@ -177,8 +228,8 @@ show_conflicts(database & db, conflicts_
     {
       duplicate_name_conflict & conflict = *i;
 
-      if (conflict.left_resolution.first == resolve_conflicts::none ||
-          conflict.right_resolution.first == resolve_conflicts::none)
+      if (conflict.left_resolution.resolution == resolve_conflicts::none ||
+          conflict.right_resolution.resolution == resolve_conflicts::none)
         {
           file_path left_name;
           conflicts.left_roster->get_name(conflict.left_nid, left_name);
@@ -189,7 +240,7 @@ show_conflicts(database & db, conflicts_
             case first:
               P(F("possible resolutions:"));
 
-              if (conflict.left_resolution.first == resolve_conflicts::none)
+              if (conflict.left_resolution.resolution == resolve_conflicts::none)
                 {
                   P(F("resolve_first_left drop"));
                   P(F("resolve_first_left keep"));
@@ -197,7 +248,7 @@ show_conflicts(database & db, conflicts_
                   P(F("resolve_first_left user \"name\""));
                 }
 
-              if (conflict.right_resolution.first == resolve_conflicts::none)
+              if (conflict.right_resolution.resolution == resolve_conflicts::none)
                 {
                   P(F("resolve_first_right drop"));
                   P(F("resolve_first_right keep"));
@@ -218,7 +269,7 @@ show_conflicts(database & db, conflicts_
     {
       file_content_conflict & conflict = *i;
 
-      if (conflict.resolution.first == resolve_conflicts::none)
+      if (conflict.resolution.resolution == resolve_conflicts::none)
         {
           file_path name;
           conflicts.left_roster->get_name(conflict.nid, name);
@@ -330,48 +381,60 @@ static void
 } // do_interactive_merge
 
 static void
-set_duplicate_name_conflict(resolve_conflicts::file_resolution_t & resolution,
-                            resolve_conflicts::file_resolution_t const & other_resolution,
-                            args_vector const & args)
+set_resolution(resolve_conflicts::file_resolution_t &       resolution,
+               resolve_conflicts::file_resolution_t const & other_resolution,
+               args_vector const &                          args)
 {
   if ("drop" == idx(args, 0)())
     {
       E(args.size() == 1, origin::user, F("too many arguments"));
-      resolution.first = resolve_conflicts::drop;
+      resolution.resolution = resolve_conflicts::drop;
     }
   else if ("keep" == idx(args, 0)())
     {
       E(args.size() == 1, origin::user, F("too many arguments"));
-      E(other_resolution.first == resolve_conflicts::none ||
-        other_resolution.first == resolve_conflicts::drop ||
-        other_resolution.first == resolve_conflicts::rename,
+      E(other_resolution.resolution == resolve_conflicts::none ||
+        other_resolution.resolution == resolve_conflicts::drop ||
+        other_resolution.resolution == resolve_conflicts::rename ||
+        other_resolution.resolution == resolve_conflicts::content_user_rename,
         origin::user,
-        F("other resolution must be 'drop' or 'rename'"));
-      resolution.first = resolve_conflicts::keep;
+        F("other resolution is %s; specify 'drop', 'rename', or 'user_rename'") %
+        image(other_resolution.resolution));
+      resolution.resolution = resolve_conflicts::keep;
     }
   else if ("rename" == idx(args, 0)())
     {
       E(args.size() == 2, origin::user, F("wrong number of arguments"));
-      resolution.first  = resolve_conflicts::rename;
-      resolution.second = resolve_conflicts::new_file_path(idx(args,1)());
+      resolution.resolution  = resolve_conflicts::rename;
+      resolution.rename = file_path_external(idx(args,1));
     }
   else if ("user" == idx(args, 0)())
     {
       E(args.size() == 2, origin::user, F("wrong number of arguments"));
-      E(other_resolution.first == resolve_conflicts::none ||
-        other_resolution.first == resolve_conflicts::drop ||
-        other_resolution.first == resolve_conflicts::rename,
+      E(other_resolution.resolution == resolve_conflicts::none ||
+        other_resolution.resolution == resolve_conflicts::drop ||
+        other_resolution.resolution == resolve_conflicts::rename ||
+        other_resolution.resolution == resolve_conflicts::content_user_rename,
         origin::user,
-        F("other resolution must be 'drop' or 'rename'"));
+        F("other resolution is %s; specify 'drop', 'rename', or 'user_rename'") %
+        image(other_resolution.resolution));
 
-      resolution.first  = resolve_conflicts::content_user;
-      resolution.second = new_optimal_path(idx(args,1)(), false);
+      resolution.resolution  = resolve_conflicts::content_user;
+      resolution.content = new_optimal_path(idx(args,1)(), false);
     }
+  else if ("user_rename" == idx(args,0)())
+    {
+      E(args.size() == 3, origin::user, F("wrong number of arguments"));
+
+      resolution.resolution  = resolve_conflicts::content_user_rename;
+      resolution.content = new_optimal_path(idx(args,1)(), false);
+      resolution.rename = file_path_external(idx(args,2));
+    }
   else
     E(false, origin::user,
       F(conflict_resolution_not_supported_msg) % idx(args,0) % "duplicate_name");
 
-} //set_duplicate_name_conflict
+} // set_resolution
 
 static void
 set_first_conflict(database & db,
@@ -384,6 +447,48 @@ set_first_conflict(database & db,
 
   if (side != neither)
     {
+      for (std::vector<dropped_modified_conflict>::iterator i = conflicts.result.dropped_modified_conflicts.begin();
+           i != conflicts.result.dropped_modified_conflicts.end();
+           ++i)
+        {
+          dropped_modified_conflict & conflict = *i;
+
+          // here we only allow two resolutions; single resolutions are handled below
+
+          switch (side)
+            {
+            case left:
+              if (conflict.left_resolution.resolution == resolve_conflicts::none)
+                {
+                  E(conflict.left_nid != the_null_node, origin::user,
+                    F("must specify resolve_first (not _left or _right)"));
+
+                  if ("keep" == idx(args,0)())
+                    E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
+
+                  set_resolution(conflict.left_resolution, conflict.right_resolution, args);
+                  return;
+                }
+              break;
+            case right:
+              if (conflict.right_resolution.resolution == resolve_conflicts::none)
+                {
+                  E(conflict.right_nid != the_null_node, origin::user,
+                    F("must specify resolve_first (not _left or _right)"));
+
+                  if ("keep" == idx(args,0)())
+                    E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
+
+                  set_resolution(conflict.right_resolution, conflict.left_resolution, args);
+                  return;
+                }
+              break;
+            case neither:
+              // can't get here
+              break;
+            }
+        }
+
       for (std::vector<duplicate_name_conflict>::iterator i = conflicts.result.duplicate_name_conflicts.begin();
            i != conflicts.result.duplicate_name_conflicts.end();
            ++i)
@@ -393,17 +498,17 @@ set_first_conflict(database & db,
           switch (side)
             {
             case left:
-              if (conflict.left_resolution.first == resolve_conflicts::none)
+              if (conflict.left_resolution.resolution == resolve_conflicts::none)
                 {
-                  set_duplicate_name_conflict(conflict.left_resolution, conflict.right_resolution, args);
+                  set_resolution(conflict.left_resolution, conflict.right_resolution, args);
                   return;
                 }
               break;
 
             case right:
-              if (conflict.right_resolution.first == resolve_conflicts::none)
+              if (conflict.right_resolution.resolution == resolve_conflicts::none)
                 {
-                  set_duplicate_name_conflict(conflict.right_resolution, conflict.left_resolution, args);
+                  set_resolution(conflict.right_resolution, conflict.left_resolution, args);
                   return;
                 }
               break;
@@ -422,20 +527,20 @@ set_first_conflict(database & db,
         {
           orphaned_node_conflict & conflict = *i;
 
-          if (conflict.resolution.first == resolve_conflicts::none)
+          if (conflict.resolution.resolution == resolve_conflicts::none)
             {
               if ("drop" == idx(args,0)())
                 {
                   E(args.size() == 1, origin::user, F("wrong number of arguments"));
 
-                  conflict.resolution.first  = resolve_conflicts::drop;
+                  conflict.resolution.resolution  = resolve_conflicts::drop;
                 }
               else if ("rename" == idx(args,0)())
                 {
                   E(args.size() == 2, origin::user, F("wrong number of arguments"));
 
-                  conflict.resolution.first  = resolve_conflicts::rename;
-                  conflict.resolution.second = new_optimal_path(idx(args,1)(), false);
+                  conflict.resolution.resolution  = resolve_conflicts::rename;
+                  conflict.resolution.rename = file_path_external(idx(args,1));
                 }
               else
                 {
@@ -452,53 +557,113 @@ set_first_conflict(database & db,
         {
           dropped_modified_conflict & conflict = *i;
 
-          if (conflict.resolution.first == resolve_conflicts::none)
+          // Here we only allow single resolutions; two resolutions are handled above
+
+          switch (conflict.dropped_side)
             {
-              if ("drop" == idx(args,0)())
+            case resolve_conflicts::left_side:
+              E(conflict.left_nid == the_null_node, origin::user,
+                F("must specify 'resolve_first_left' or 'resolve_first_right' (not just 'resolve_first')"));
+
+              // the left side stays dropped; we either drop, keep or replace the right side
+              if (conflict.right_resolution.resolution == resolve_conflicts::none)
                 {
-                  E(args.size() == 1, origin::user, F("wrong number of arguments"));
-                  E(conflict.recreated == the_null_node, origin::user, F("recreated files may not be dropped"));
+                  if ("drop" == idx(args,0)())
+                    {
+                      E(args.size() == 1, origin::user, F("wrong number of arguments"));
 
-                  conflict.resolution.first  = resolve_conflicts::drop;
-                }
-              else if ("keep" == idx(args,0)())
-                {
-                  E(args.size() == 1, origin::user, F("wrong number of arguments"));
-                  E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
+                      conflict.right_resolution.resolution = resolve_conflicts::drop;
+                    }
+                  else if ("keep" == idx(args,0)())
+                    {
+                      E(args.size() == 1, origin::user, F("wrong number of arguments"));
+                      E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
 
-                  conflict.resolution.first  = resolve_conflicts::keep;
-                }
-              else if ("user" == idx(args,0)())
-                {
-                  E(args.size() == 2, origin::user, F("wrong number of arguments"));
-                  E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
+                      conflict.right_resolution.resolution = resolve_conflicts::keep;
+                    }
+                  else if ("user" == idx(args,0)())
+                    {
+                      E(args.size() == 2, origin::user, F("wrong number of arguments"));
+                      E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
 
-                  conflict.resolution.first  = resolve_conflicts::content_user;
-                  conflict.resolution.second = new_optimal_path(idx(args,1)(), false);
+                      conflict.right_resolution.resolution  = resolve_conflicts::content_user;
+                      conflict.right_resolution.content = new_optimal_path(idx(args,1)(), false);
+                    }
+                  else if ("rename" == idx(args,0)())
+                    {
+                      E(args.size() == 2, origin::user, F("wrong number of arguments"));
+
+                      conflict.right_resolution.resolution  = resolve_conflicts::rename;
+                      conflict.right_resolution.rename = file_path_external(idx(args,1));
+                    }
+                  else if ("user_rename" == idx(args,0)())
+                    {
+                      E(args.size() == 3, origin::user, F("wrong number of arguments"));
+
+                      conflict.right_resolution.resolution  = resolve_conflicts::content_user_rename;
+                      conflict.right_resolution.content = new_optimal_path(idx(args,1)(), false);
+                      conflict.right_resolution.rename = file_path_external(idx(args,2));
+                    }
+                  else
+                    {
+                      E(false, origin::user,
+                        F(conflict_resolution_not_supported_msg) % idx(args,0) % "dropped_modified");
+                    }
+                  return;
                 }
-              else if ("rename" == idx(args,0)())
+              break;
+
+            case resolve_conflicts::right_side:
+              E(conflict.right_nid == the_null_node, origin::user,
+                F("must specify 'resolve_first_left' or 'resolve_first_right' (not just 'resolve_first')"));
+
+              // the right side stays dropped; we either drop, keep or replace the left side
+              if (conflict.left_resolution.resolution == resolve_conflicts::none)
                 {
-                  E(args.size() == 2, origin::user, F("wrong number of arguments"));
-                  E(conflict.orphaned, origin::user, F("non-orphaned files cannot be renamed"));
+                  if ("drop" == idx(args,0)())
+                    {
+                      E(args.size() == 1, origin::user, F("wrong number of arguments"));
 
-                  conflict.resolution.first  = resolve_conflicts::rename;
-                  conflict.resolution.second = new_optimal_path(idx(args,1)(), false);
-                }
-              else if ("user_rename" == idx(args,0)())
-                {
-                  E(args.size() == 3, origin::user, F("wrong number of arguments"));
-                  E(conflict.orphaned, origin::user, F("non-orphaned files cannot be renamed"));
+                      conflict.left_resolution.resolution = resolve_conflicts::drop;
+                    }
+                  else if ("keep" == idx(args,0)())
+                    {
+                      E(args.size() == 1, origin::user, F("wrong number of arguments"));
+                      E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
 
-                  conflict.resolution.first  = resolve_conflicts::content_user_rename;
-                  conflict.resolution.second = new_optimal_path(idx(args,1)(), false);
-                  conflict.rename = file_path_external(utf8(idx(args,2)(), origin::user));
+                      conflict.left_resolution.resolution  = resolve_conflicts::keep;
+                    }
+                  else if ("user" == idx(args,0)())
+                    {
+                      E(args.size() == 2, origin::user, F("wrong number of arguments"));
+                      E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
+
+                      conflict.left_resolution.resolution  = resolve_conflicts::content_user;
+                      conflict.left_resolution.content = new_optimal_path(idx(args,1)(), false);
+                    }
+                  else if ("rename" == idx(args,0)())
+                    {
+                      E(args.size() == 2, origin::user, F("wrong number of arguments"));
+
+                      conflict.left_resolution.resolution  = resolve_conflicts::rename;
+                      conflict.left_resolution.rename = file_path_external(idx(args,1));
+                    }
+                  else if ("user_rename" == idx(args,0)())
+                    {
+                      E(args.size() == 3, origin::user, F("wrong number of arguments"));
+
+                      conflict.left_resolution.resolution  = resolve_conflicts::content_user_rename;
+                      conflict.left_resolution.content = new_optimal_path(idx(args,1)(), false);
+                      conflict.left_resolution.rename = file_path_external(idx(args,2));
+                    }
+                  else
+                    {
+                      E(false, origin::user,
+                        F(conflict_resolution_not_supported_msg) % idx(args,0) % "dropped_modified");
+                    }
+                  return;
                 }
-              else
-                {
-                  E(false, origin::user,
-                    F(conflict_resolution_not_supported_msg) % idx(args,0) % "dropped_modified");
-                }
-              return;
+              break;
             }
         }
 
@@ -508,7 +673,7 @@ set_first_conflict(database & db,
         {
           file_content_conflict & conflict = *i;
 
-          if (conflict.resolution.first == resolve_conflicts::none)
+          if (conflict.resolution.resolution == resolve_conflicts::none)
             {
               if ("interactive" == idx(args,0)())
                 {
@@ -543,8 +708,8 @@ set_first_conflict(database & db,
                   if (do_interactive_merge(db, lua, conflicts, conflict.nid,
                                            conflict.ancestor, conflict.left, conflict.right, result_path))
                     {
-                      conflict.resolution.first  = resolve_conflicts::content_user;
-                      conflict.resolution.second = boost::shared_ptr<any_path>(new bookkeeping_path(result_path));
+                      conflict.resolution.resolution  = resolve_conflicts::content_user;
+                      conflict.resolution.content = boost::shared_ptr<any_path>(new bookkeeping_path(result_path));
                       P(F("interactive merge result saved in '%s'") % result_path.as_internal());
                     }
                   else
@@ -554,8 +719,8 @@ set_first_conflict(database & db,
                 {
                   E(args.size() == 2, origin::user, F("wrong number of arguments"));
 
-                  conflict.resolution.first  = resolve_conflicts::content_user;
-                  conflict.resolution.second = new_optimal_path(idx(args,1)(), false);
+                  conflict.resolution.resolution  = resolve_conflicts::content_user;
+                  conflict.resolution.content = new_optimal_path(idx(args,1)(), false);
                 }
               else
                 {
============================================================
--- src/merge_conflict.cc	34fbc891bc8455202d9c0d0a495beb690fd8a5a0
+++ src/merge_conflict.cc	b47a70720f836809f06347360ee91ddf587b5721
@@ -1,4 +1,4 @@
-// Copyright (C) 2005 Nathaniel Smith <address@hidden>
+// Copyright (C) 2005, 2012 Nathaniel Smith <address@hidden>
 //               2008, 2009, 2012 Stephen Leake <address@hidden>
 //
 // This program is made available under the GNU GPL version 2.0 or
@@ -86,11 +86,9 @@ namespace resolve_conflicts
 
 namespace resolve_conflicts
 {
-  shared_ptr<any_path>
-  new_file_path(string path)
+  file_path file_path_external(string path)
   {
-    return shared_ptr<any_path>
-      (new file_path(file_path_external(utf8(path, origin::user))));
+    return file_path_external(utf8(path, origin::user));
   };
 }
 
@@ -396,11 +394,9 @@ put_attr_conflict (basic_io::stanza & st
     }
 }
 
-enum side_t {left_side, right_side};
-
 static void
-put_file_resolution(basic_io::stanza & st,
-                    side_t side,
+put_file_resolution(basic_io::stanza &                           st,
+                    resolve_conflicts::side_t                    side,
                     resolve_conflicts::file_resolution_t const & resolution)
 {
   // We output any resolution for any conflict; only valid resolutions
@@ -408,7 +404,7 @@ put_file_resolution(basic_io::stanza & s
   // resolutions from files we check that the resolution is valid for the
   // conflict. Hence there is no read_resolution.
 
-  switch (resolution.first)
+  switch (resolution.resolution)
     {
     case resolve_conflicts::none:
       break;
@@ -416,12 +412,12 @@ put_file_resolution(basic_io::stanza & s
     case resolve_conflicts::content_user:
       switch (side)
         {
-        case left_side:
-          st.push_str_pair(syms::resolved_user_left, resolution.second->as_external());
+        case resolve_conflicts::left_side:
+          st.push_str_pair(syms::resolved_user_left, resolution.content->as_external());
           break;
 
-        case right_side:
-          st.push_str_pair(syms::resolved_user_right, resolution.second->as_external());
+        case resolve_conflicts::right_side:
+          st.push_str_pair(syms::resolved_user_right, resolution.content->as_external());
           break;
         }
       break;
@@ -429,15 +425,16 @@ put_file_resolution(basic_io::stanza & s
     case resolve_conflicts::content_user_rename:
       switch (side)
         {
-        case left_side:
-          st.push_str_pair(syms::resolved_user_left, resolution.second->as_external());
+        case resolve_conflicts::left_side:
+          st.push_str_pair(syms::resolved_user_left, resolution.content->as_external());
+          st.push_str_pair(syms::resolved_rename_left, resolution.rename.as_external());
           break;
 
-        case right_side:
-          st.push_str_pair(syms::resolved_user_right, resolution.second->as_external());
+        case resolve_conflicts::right_side:
+          st.push_str_pair(syms::resolved_user_right, resolution.content->as_external());
+          st.push_str_pair(syms::resolved_rename_right, resolution.rename.as_external());
           break;
         }
-      // value for rename is put by caller
       break;
 
     case resolve_conflicts::content_internal:
@@ -447,12 +444,12 @@ put_file_resolution(basic_io::stanza & s
     case resolve_conflicts::rename:
       switch (side)
         {
-        case left_side:
-          st.push_str_pair(syms::resolved_rename_left, resolution.second->as_external());
+        case resolve_conflicts::left_side:
+          st.push_str_pair(syms::resolved_rename_left, resolution.rename.as_external());
           break;
 
-        case right_side:
-          st.push_str_pair(syms::resolved_rename_right, resolution.second->as_external());
+        case resolve_conflicts::right_side:
+          st.push_str_pair(syms::resolved_rename_right, resolution.rename.as_external());
           break;
         }
       break;
@@ -460,11 +457,11 @@ put_file_resolution(basic_io::stanza & s
     case resolve_conflicts::drop:
       switch (side)
         {
-        case left_side:
+        case resolve_conflicts::left_side:
           st.push_symbol(syms::resolved_drop_left);
           break;
 
-        case right_side:
+        case resolve_conflicts::right_side:
           st.push_symbol(syms::resolved_drop_right);
           break;
         }
@@ -473,11 +470,11 @@ put_file_resolution(basic_io::stanza & s
     case resolve_conflicts::keep:
       switch (side)
         {
-        case left_side:
+        case resolve_conflicts::left_side:
           st.push_symbol(syms::resolved_keep_left);
           break;
 
-        case right_side:
+        case resolve_conflicts::right_side:
           st.push_symbol(syms::resolved_keep_right);
           break;
         }
@@ -535,7 +532,7 @@ put_content_conflict (basic_io::stanza &
       st.push_file_pair(syms::left_name, left_name);
       st.push_file_pair(syms::right_name, right_name);
     }
-  put_file_resolution (st, left_side, conflict.resolution);
+  put_file_resolution (st, resolve_conflicts::left_side, conflict.resolution);
 }
 
 static void
@@ -1001,7 +998,7 @@ roster_merge_result::report_orphaned_nod
 
       if (basic_io)
         {
-          put_file_resolution (st, left_side, conflict.resolution);
+          put_file_resolution (st, resolve_conflicts::left_side, conflict.resolution);
           put_stanza (st, output);
         }
     }
@@ -1101,21 +1098,21 @@ roster_merge_result::report_dropped_modi
       node_id nid;
       file_path modified_name;
 
-      if (conflict.left_nid == the_null_node)
+      switch (conflict.dropped_side)
         {
-          // left side dropped, right side modified
+        case resolve_conflicts::left_side:
           I(!roster.is_attached(conflict.right_nid));
 
           nid = conflict.right_nid;
           right_roster.get_name(conflict.right_nid, modified_name);
-        }
-      else
-        {
-          // left side modified, right side dropped
+          break;
+
+        case resolve_conflicts::right_side:
           I(!roster.is_attached(conflict.left_nid));
 
           nid = conflict.left_nid;
           left_roster.get_name(conflict.left_nid, modified_name);
+          break;
         }
 
       shared_ptr<roster_t const> lca_roster;
@@ -1137,8 +1134,9 @@ roster_merge_result::report_dropped_modi
           db_adaptor.db.get_file_content (db_adaptor.lca, nid, fid);
           st.push_binary_pair(syms::ancestor_file_id, fid.inner());
 
-          if (conflict.left_nid == the_null_node)
+          switch (conflict.dropped_side)
             {
+            case resolve_conflicts::left_side:
               if (conflict.orphaned)
                 {
                    st.push_str_pair(syms::left_type, "orphaned file");
@@ -1147,7 +1145,7 @@ roster_merge_result::report_dropped_modi
                 }
               else
                 {
-                  if (conflict.recreated == the_null_node)
+                  if (conflict.left_nid == the_null_node)
                     {
                       st.push_str_pair(syms::left_type, "dropped file");
                       push_dropped_details(db_adaptor, syms::left_rev, syms::left_name, syms::left_file_id,
@@ -1157,21 +1155,23 @@ roster_merge_result::report_dropped_modi
                     {
                       st.push_str_pair(syms::left_type, "recreated file");
                       st.push_str_pair(syms::left_name, modified_name.as_external());
-                      db_adaptor.db.get_file_content (db_adaptor.left_rid, conflict.recreated, fid);
+                      db_adaptor.db.get_file_content (db_adaptor.left_rid, conflict.left_nid, fid);
                       st.push_binary_pair(syms::left_file_id, fid.inner());
                     }
                 }
-            }
-          else
-            {
+
+              st.push_str_pair(syms::right_type, "modified file");
+              st.push_str_pair(syms::right_name, modified_name.as_external());
+              db_adaptor.db.get_file_content (db_adaptor.right_rid, nid, fid);
+              st.push_binary_pair(syms::right_file_id, fid.inner());
+              break;
+
+            case resolve_conflicts::right_side:
               st.push_str_pair(syms::left_type, "modified file");
               st.push_str_pair(syms::left_name, modified_name.as_external());
               db_adaptor.db.get_file_content (db_adaptor.left_rid, nid, fid);
               st.push_binary_pair(syms::left_file_id, fid.inner());
-            }
 
-          if (conflict.right_nid == the_null_node)
-            {
               if (conflict.orphaned)
                 {
                   st.push_str_pair(syms::right_type, "orphaned file");
@@ -1180,7 +1180,7 @@ roster_merge_result::report_dropped_modi
                 }
               else
                 {
-                  if (conflict.recreated == the_null_node)
+                  if (conflict.right_nid == the_null_node)
                     {
                       st.push_str_pair(syms::right_type, "dropped file");
                       push_dropped_details(db_adaptor, syms::right_rev, syms::right_name, syms::right_file_id,
@@ -1190,59 +1190,39 @@ roster_merge_result::report_dropped_modi
                     {
                       st.push_str_pair(syms::right_type, "recreated file");
                       st.push_str_pair(syms::right_name, modified_name.as_external());
-                      db_adaptor.db.get_file_content (db_adaptor.right_rid, conflict.recreated, fid);
+                      db_adaptor.db.get_file_content (db_adaptor.right_rid, conflict.right_nid, fid);
                       st.push_binary_pair(syms::right_file_id, fid.inner());
                     }
                 }
+              break;
             }
-          else
-            {
-              st.push_str_pair(syms::right_type, "modified file");
-              st.push_str_pair(syms::right_name, modified_name.as_external());
-              db_adaptor.db.get_file_content (db_adaptor.right_rid, nid, fid);
-              st.push_binary_pair(syms::right_file_id, fid.inner());
-            }
 
-          put_file_resolution (st, left_side, conflict.resolution);
-          if (conflict.orphaned)
-            {
-              switch (conflict.resolution.first)
-                {
-                case resolve_conflicts::none:
-                case resolve_conflicts::rename:
-                case resolve_conflicts::drop:
-                  break;
+          put_file_resolution (st, resolve_conflicts::left_side, conflict.left_resolution);
+          put_file_resolution (st, resolve_conflicts::right_side, conflict.right_resolution);
 
-                case resolve_conflicts::content_user_rename:
-                  st.push_str_pair(syms::resolved_rename_left, conflict.rename.as_external());
-                  break;
-
-                default:
-                  I(false);
-                }
-            }
           put_stanza(st, output);
         }
       else
         {
-          P(F("conflict: file '%s' from revision %s") % ancestor_name % lca_rid);
-          if (conflict.left_nid == the_null_node)
+          P(F("conflict: file '%s'") % ancestor_name);
+          switch (conflict.dropped_side)
             {
+            case resolve_conflicts::left_side:
               if (conflict.orphaned)
                 {
                   P(F("orphaned on the left"));
                 }
               else
                 {
-                  if (conflict.recreated == the_null_node)
+                  if (conflict.left_nid == the_null_node)
                     P(F("dropped on the left"));
                   else
                     P(F("dropped and recreated on the left"));
                 }
               P(F("modified on the right, named %s") % modified_name);
-            }
-          else
-            {
+              break;
+
+            case resolve_conflicts::right_side:
               P(F("modified on the left, named %s") % modified_name);
               if (conflict.orphaned)
                 {
@@ -1250,43 +1230,49 @@ roster_merge_result::report_dropped_modi
                 }
               else
                 {
-                  if (conflict.recreated == the_null_node)
+                  if (conflict.right_nid == the_null_node)
                     P(F("dropped on the right"));
                   else
                     P(F("dropped and recreated on the right"));
                 }
+              break;
             }
 
-          // We can have a resolution from a mtn:resolve_conflict attribute.
-          switch (conflict.resolution.first)
+          // We can have a resolution from a mtn:resolve_conflict attribute
+          // (so far, that only supports drop).
+          switch (conflict.left_resolution.resolution)
             {
             case resolve_conflicts::none:
               break;
 
             case resolve_conflicts::content_user:
-              P(F("resolution: user file '%s'") % conflict.resolution.second->as_external());
-              break;
-
             case resolve_conflicts::content_user_rename:
-              P(F("resolution: user '%s' rename '%s'") %
-                conflict.resolution.second->as_external() %
-                conflict.rename.as_external());
-              break;
-
             case resolve_conflicts::rename:
-              P(F("resolution: rename '%s'") % conflict.resolution.second->as_external());
+            case resolve_conflicts::keep:
+            case resolve_conflicts::content_internal:
+              I(false);
               break;
 
             case resolve_conflicts::drop:
-              P(F("resolution: drop"));
+              P(F("left_resolution: drop"));
               break;
+            }
+          switch (conflict.right_resolution.resolution)
+            {
+            case resolve_conflicts::none:
+              break;
 
+            case resolve_conflicts::content_user:
+            case resolve_conflicts::content_user_rename:
+            case resolve_conflicts::rename:
             case resolve_conflicts::keep:
-              P(F("resolution: keep"));
+            case resolve_conflicts::content_internal:
+              I(false);
               break;
 
-            default:
-              I(false);
+            case resolve_conflicts::drop:
+              P(F("right_resolution: drop"));
+              break;
             }
         }
     }
@@ -1466,8 +1452,8 @@ roster_merge_result::report_duplicate_na
 
       if (basic_io)
         {
-          put_file_resolution (st, left_side, conflict.left_resolution);
-          put_file_resolution (st, right_side, conflict.right_resolution);
+          put_file_resolution (st, resolve_conflicts::left_side, conflict.left_resolution);
+          put_file_resolution (st, resolve_conflicts::right_side, conflict.right_resolution);
           put_stanza(st, output);
         }
     }
@@ -1643,9 +1629,9 @@ roster_merge_result::report_file_content
         {
           basic_io::stanza st;
 
-          if (conflict.resolution.first == resolve_conflicts::none)
+          if (conflict.resolution.resolution == resolve_conflicts::none)
             if (auto_merge_succeeds(lua, conflict, adaptor, left_roster, right_roster))
-              conflict.resolution.first = resolve_conflicts::content_internal;
+              conflict.resolution.resolution = resolve_conflicts::content_internal;
 
           st.push_str_pair(syms::conflict, syms::content);
           put_content_conflict (st, left_roster, right_roster, adaptor, conflict);
@@ -1727,6 +1713,7 @@ static char const * const conflict_resol
 
 static char const * const conflicts_mismatch_msg = N_("conflicts file does not match current conflicts");
 static char const * const conflict_resolution_not_supported_msg = N_("%s is not a supported conflict resolution for %s");
+static char const * const history_lost_msg = N_("history for '%s' from %s will be lost; see user manual Merge Conflicts section");
 static char const * const conflict_extra = N_("extra chars at end of conflict");
 
 static void
@@ -1886,14 +1873,14 @@ read_orphaned_node_conflict(basic_io::pa
     {
       if (pars.symp (syms::resolved_drop_left))
         {
-          conflict.resolution.first = resolve_conflicts::drop;
+          conflict.resolution.resolution = resolve_conflicts::drop;
           pars.sym();
         }
       else if (pars.symp (syms::resolved_rename_left))
         {
-          conflict.resolution.first = resolve_conflicts::rename;
+          conflict.resolution.resolution = resolve_conflicts::rename;
           pars.sym();
-          conflict.resolution.second = new_optimal_path(pars.token, true);
+          conflict.resolution.rename = resolve_conflicts::file_path_external(pars.token);
           pars.str();
         }
       else
@@ -1994,10 +1981,12 @@ static void
 } // read_multiple_name_conflicts
 
 static void
-read_dropped_modified_conflict(basic_io::parser & pars,
+read_dropped_modified_conflict(basic_io::parser &          pars,
                                dropped_modified_conflict & conflict,
-                               roster_t const & left_roster,
-                               roster_t const & right_roster)
+                               revision_id                 left_rid,
+                               roster_t const &            left_roster,
+                               revision_id                 right_rid,
+                               roster_t const &            right_roster)
 {
   string tmp;
 
@@ -2009,28 +1998,38 @@ read_dropped_modified_conflict(basic_io:
 
   if (tmp == "dropped file")
     {
-      pars.esym(syms::left_rev); pars.hex();
+      conflict.dropped_side = resolve_conflicts::left_side;
+
+      pars.esym(syms::left_rev); pars.hex(tmp);
+      conflict.left_rid = decode_hexenc_as<revision_id>(tmp, pars.tok.in.made_from);
       pars.esym(syms::left_name); pars.str();
       pars.esym(syms::left_file_id); pars.hex();
     }
   else if (tmp == "orphaned file")
     {
-      pars.esym(syms::left_rev); pars.hex();
+      conflict.dropped_side = resolve_conflicts::left_side;
+      conflict.orphaned = true;
+
+      pars.esym(syms::left_rev); pars.hex(tmp);
+      conflict.left_rid = decode_hexenc_as<revision_id>(tmp, pars.tok.in.made_from);
       pars.esym(syms::left_name); pars.str();
       pars.esym(syms::left_file_id); pars.hex();
-
-      conflict.orphaned = true;
     }
   else if (tmp == "recreated file")
     {
+      conflict.dropped_side = resolve_conflicts::left_side;
+      conflict.left_rid = left_rid;
+
       pars.esym(syms::left_name); pars.str(tmp);
-      conflict.recreated = left_roster.get_node(file_path_external(utf8(tmp, origin::internal)))->self;
+      conflict.left_nid = left_roster.get_node(resolve_conflicts::file_path_external(tmp))->self;
       pars.esym(syms::left_file_id); pars.hex();
     }
   else if (tmp == "modified file")
     {
+      conflict.left_rid = left_rid;
+
       pars.esym(syms::left_name); pars.str(tmp);
-      conflict.left_nid = left_roster.get_node(file_path_external(utf8(tmp, origin::internal)))->self;
+      conflict.left_nid = left_roster.get_node(resolve_conflicts::file_path_external(tmp))->self;
       pars.esym(syms::left_file_id); pars.hex();
     }
   else
@@ -2041,70 +2040,108 @@ read_dropped_modified_conflict(basic_io:
 
   if (tmp == "dropped file")
     {
-      pars.esym(syms::right_rev); pars.hex();
+      conflict.dropped_side = resolve_conflicts::right_side;
+
+      pars.esym(syms::right_rev); pars.hex(tmp);
+      conflict.right_rid = decode_hexenc_as<revision_id>(tmp, pars.tok.in.made_from);
       pars.esym(syms::right_name); pars.str();
       pars.esym(syms::right_file_id); pars.hex();
     }
   else if (tmp == "orphaned file")
     {
-      pars.esym(syms::right_rev); pars.hex();
+      conflict.dropped_side = resolve_conflicts::right_side;
+      conflict.orphaned = true;
+
+      pars.esym(syms::right_rev); pars.hex(tmp);
+      conflict.right_rid = decode_hexenc_as<revision_id>(tmp, pars.tok.in.made_from);
       pars.esym(syms::right_name); pars.str();
       pars.esym(syms::right_file_id); pars.hex();
-
-      conflict.orphaned = true;
     }
   else if (tmp == "recreated file")
     {
+      conflict.dropped_side = resolve_conflicts::right_side;
+      conflict.right_rid = right_rid;
+
       pars.esym(syms::right_name); pars.str(tmp);
-      conflict.recreated = right_roster.get_node(file_path_external(utf8(tmp, origin::internal)))->self;
+      conflict.right_nid = right_roster.get_node(resolve_conflicts::file_path_external(tmp))->self;
       pars.esym(syms::right_file_id); pars.hex();
     }
   else if (tmp == "modified file")
     {
+      conflict.right_rid = right_rid;
+
       pars.esym(syms::right_name); pars.str(tmp);
-      conflict.right_nid = right_roster.get_node(file_path_external(utf8(tmp, origin::internal)))->self;
+      conflict.right_nid = right_roster.get_node(resolve_conflicts::file_path_external(tmp))->self;
       pars.esym(syms::right_file_id); pars.hex();
     }
   else
     I(false);
 
-  // check for a resolution
-  if ((!pars.symp (syms::conflict)) && pars.tok.in.lookahead != EOF)
+  // check for resolutions
+  while ((!pars.symp (syms::conflict)) && pars.tok.in.lookahead != EOF)
     {
       if (pars.symp (syms::resolved_drop_left))
         {
-          conflict.resolution.first = resolve_conflicts::drop;
+          conflict.left_resolution.resolution = resolve_conflicts::drop;
           pars.sym();
         }
+      else if (pars.symp (syms::resolved_drop_right))
+        {
+          conflict.right_resolution.resolution = resolve_conflicts::drop;
+          pars.sym();
+        }
       else if (pars.symp (syms::resolved_keep_left))
         {
           E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
 
-          conflict.resolution.first = resolve_conflicts::keep;
+          conflict.left_resolution.resolution = resolve_conflicts::keep;
           pars.sym();
         }
+      else if (pars.symp (syms::resolved_keep_right))
+        {
+          E(!conflict.orphaned, origin::user, F("orphaned files must be renamed"));
+
+          conflict.right_resolution.resolution = resolve_conflicts::keep;
+          pars.sym();
+        }
+      else if (pars.symp (syms::resolved_rename_left))
+        {
+          if (conflict.left_resolution.resolution == resolve_conflicts::content_user)
+            conflict.left_resolution.resolution = resolve_conflicts::content_user_rename;
+          else
+            conflict.left_resolution.resolution = resolve_conflicts::rename;
+          pars.sym();
+          conflict.left_resolution.rename = resolve_conflicts::file_path_external(pars.token);
+          pars.str();
+        }
+      else if (pars.symp (syms::resolved_rename_right))
+        {
+          if (conflict.right_resolution.resolution == resolve_conflicts::content_user)
+            conflict.right_resolution.resolution = resolve_conflicts::content_user_rename;
+          else
+            conflict.right_resolution.resolution = resolve_conflicts::rename;
+          pars.sym();
+          conflict.right_resolution.rename = resolve_conflicts::file_path_external(pars.token);
+          pars.str();
+        }
       else if (pars.symp (syms::resolved_user_left))
         {
-          conflict.resolution.first = resolve_conflicts::content_user;
+          if (conflict.left_resolution.resolution == resolve_conflicts::rename)
+            conflict.left_resolution.resolution = resolve_conflicts::content_user_rename;
+          else
+            conflict.left_resolution.resolution = resolve_conflicts::content_user;
           pars.sym();
-          conflict.resolution.second = new_optimal_path(pars.token, false);
+          conflict.left_resolution.content = new_optimal_path(pars.token, false);
           pars.str();
-
-          if (conflict.orphaned)
-            {
-              pars.esym (syms::resolved_rename_left);
-              conflict.resolution.first = resolve_conflicts::content_user_rename;
-              pars.str(tmp);
-              conflict.rename = file_path_external_ws(utf8(tmp, origin::user));
-            }
         }
-      else if (pars.symp (syms::resolved_rename_left))
+      else if (pars.symp (syms::resolved_user_right))
         {
-          E(conflict.orphaned, origin::user, F("non-orphaned files cannot be renamed"));
-
-          conflict.resolution.first = resolve_conflicts::rename;
+          if (conflict.right_resolution.resolution == resolve_conflicts::rename)
+            conflict.right_resolution.resolution = resolve_conflicts::content_user_rename;
+          else
+            conflict.right_resolution.resolution = resolve_conflicts::content_user;
           pars.sym();
-          conflict.resolution.second = new_optimal_path(pars.token, false);
+          conflict.right_resolution.content = new_optimal_path(pars.token, false);
           pars.str();
         }
       else
@@ -2114,18 +2151,20 @@ static void
 } // read_dropped_modified_conflict
 
 static void
-read_dropped_modified_conflicts(basic_io::parser & pars,
-                             std::vector<dropped_modified_conflict> & conflicts,
-                             roster_t const & left_roster,
-                             roster_t const & right_roster)
+read_dropped_modified_conflicts(basic_io::parser &                       pars,
+                                std::vector<dropped_modified_conflict> & conflicts,
+                                revision_id                              left_rid,
+                                roster_t const &                         left_roster,
+                                revision_id                              right_rid,
+                                roster_t const &                         right_roster)
 {
   while (pars.tok.in.lookahead != EOF && pars.symp(syms::dropped_modified))
     {
-      dropped_modified_conflict conflict(the_null_node, the_null_node);
+      dropped_modified_conflict conflict;
 
       pars.sym();
 
-      read_dropped_modified_conflict(pars, conflict, left_roster, right_roster);
+      read_dropped_modified_conflict(pars, conflict, left_rid, left_roster, right_rid, right_roster);
 
       conflicts.push_back(conflict);
 
@@ -2133,11 +2172,14 @@ read_dropped_modified_conflicts(basic_io
         pars.esym (syms::conflict);
     }
 } // read_dropped_modified_conflicts
+
 static void
-validate_dropped_modified_conflicts(basic_io::parser & pars,
+validate_dropped_modified_conflicts(basic_io::parser &                       pars,
                                     std::vector<dropped_modified_conflict> & conflicts,
-                                    roster_t const & left_roster,
-                                    roster_t const & right_roster)
+                                    revision_id                              left_rid,
+                                    roster_t const &                         left_roster,
+                                    revision_id                              right_rid,
+                                    roster_t const &                         right_roster)
 {
   for (std::vector<dropped_modified_conflict>::iterator i = conflicts.begin();
        i != conflicts.end();
@@ -2148,17 +2190,19 @@ validate_dropped_modified_conflicts(basi
 
       pars.esym(syms::dropped_modified);
 
-      read_dropped_modified_conflict(pars, file_conflict, left_roster, right_roster);
+      read_dropped_modified_conflict(pars, file_conflict, left_rid, left_roster, right_rid, right_roster);
 
       // Note that we do not confirm the file ids.
-      E(merge_conflict.left_nid == file_conflict.left_nid &&
-        merge_conflict.right_nid == file_conflict.right_nid &&
-        merge_conflict.recreated == file_conflict.recreated,
+      E(merge_conflict.dropped_side == file_conflict.dropped_side &&
+        merge_conflict.left_nid == file_conflict.left_nid &&
+        merge_conflict.right_nid == file_conflict.right_nid,
         origin::user,
         F(conflicts_mismatch_msg));
 
-      merge_conflict.resolution = file_conflict.resolution;
-      merge_conflict.rename     = file_conflict.rename;
+      merge_conflict.left_rid         = file_conflict.left_rid;
+      merge_conflict.right_rid        = file_conflict.right_rid;
+      merge_conflict.left_resolution  = file_conflict.left_resolution;
+      merge_conflict.right_resolution = file_conflict.right_resolution;
 
       if (pars.tok.in.lookahead != EOF)
         pars.esym (syms::conflict);
@@ -2185,50 +2229,50 @@ read_duplicate_name_conflict(basic_io::p
     {
       if (pars.symp (syms::resolved_drop_left))
         {
-          conflict.left_resolution.first = resolve_conflicts::drop;
+          conflict.left_resolution.resolution = resolve_conflicts::drop;
           pars.sym();
         }
       else if (pars.symp (syms::resolved_drop_right))
         {
-          conflict.right_resolution.first = resolve_conflicts::drop;
+          conflict.right_resolution.resolution = resolve_conflicts::drop;
           pars.sym();
         }
       else if (pars.symp (syms::resolved_keep_left))
         {
-          conflict.left_resolution.first = resolve_conflicts::keep;
+          conflict.left_resolution.resolution = resolve_conflicts::keep;
           pars.sym();
         }
       else if (pars.symp (syms::resolved_keep_right))
         {
-          conflict.right_resolution.first = resolve_conflicts::keep;
+          conflict.right_resolution.resolution = resolve_conflicts::keep;
           pars.sym();
         }
       else if (pars.symp (syms::resolved_rename_left))
         {
-          conflict.left_resolution.first = resolve_conflicts::rename;
+          conflict.left_resolution.resolution = resolve_conflicts::rename;
           pars.sym();
-          conflict.left_resolution.second = resolve_conflicts::new_file_path(pars.token);
+          conflict.left_resolution.rename = resolve_conflicts::file_path_external(pars.token);
           pars.str();
         }
       else if (pars.symp (syms::resolved_rename_right))
         {
-          conflict.right_resolution.first = resolve_conflicts::rename;
+          conflict.right_resolution.resolution = resolve_conflicts::rename;
           pars.sym();
-          conflict.right_resolution.second = resolve_conflicts::new_file_path(pars.token);
+          conflict.right_resolution.rename = resolve_conflicts::file_path_external(pars.token);
           pars.str();
         }
       else if (pars.symp (syms::resolved_user_left))
         {
-          conflict.left_resolution.first = resolve_conflicts::content_user;
+          conflict.left_resolution.resolution = resolve_conflicts::content_user;
           pars.sym();
-          conflict.left_resolution.second = new_optimal_path(pars.token, true);
+          conflict.left_resolution.content = new_optimal_path(pars.token, true);
           pars.str();
         }
       else if (pars.symp (syms::resolved_user_right))
         {
-          conflict.right_resolution.first = resolve_conflicts::content_user;
+          conflict.right_resolution.resolution = resolve_conflicts::content_user;
           pars.sym();
-          conflict.right_resolution.second = new_optimal_path(pars.token, true);
+          conflict.right_resolution.content = new_optimal_path(pars.token, true);
           pars.str();
         }
       else
@@ -2433,14 +2477,14 @@ read_file_content_conflict(basic_io::par
     {
       if (pars.symp (syms::resolved_internal))
         {
-          conflict.resolution.first = resolve_conflicts::content_internal;
+          conflict.resolution.resolution = resolve_conflicts::content_internal;
           pars.sym();
         }
       else if (pars.symp (syms::resolved_user_left))
         {
-          conflict.resolution.first = resolve_conflicts::content_user;
+          conflict.resolution.resolution = resolve_conflicts::content_user;
           pars.sym();
-          conflict.resolution.second = new_optimal_path(pars.token, true);
+          conflict.resolution.content = new_optimal_path(pars.token, true);
           pars.str();
         }
       else
@@ -2506,7 +2550,9 @@ read_conflict_file_core(basic_io::parser
 
 static void
 read_conflict_file_core(basic_io::parser pars,
+                        revision_id left_rid,
                         roster_t const & left_roster,
+                        revision_id right_rid,
                         roster_t const & right_roster,
                         roster_merge_result & result,
                         bool validate)
@@ -2533,7 +2579,8 @@ read_conflict_file_core(basic_io::parser
       // order as non-validate, below.
 
       validate_orphaned_node_conflicts(pars, result.orphaned_node_conflicts, left_roster, right_roster);
-      validate_dropped_modified_conflicts(pars, result.dropped_modified_conflicts, left_roster, right_roster);
+      validate_dropped_modified_conflicts
+        (pars, result.dropped_modified_conflicts, left_rid, left_roster, right_rid, right_roster);
       validate_duplicate_name_conflicts(pars, result.duplicate_name_conflicts, left_roster, right_roster);
       validate_file_content_conflicts(pars, result.file_content_conflicts, left_roster, right_roster);
     }
@@ -2546,7 +2593,8 @@ read_conflict_file_core(basic_io::parser
       read_directory_loop_conflicts(pars, result.directory_loop_conflicts, left_roster, right_roster);
       read_orphaned_node_conflicts(pars, result.orphaned_node_conflicts, left_roster, right_roster);
       read_multiple_name_conflicts(pars, result.multiple_name_conflicts, left_roster, right_roster);
-      read_dropped_modified_conflicts(pars, result.dropped_modified_conflicts, left_roster, right_roster);
+      read_dropped_modified_conflicts
+        (pars, result.dropped_modified_conflicts, left_rid, left_roster, right_rid, right_roster);
       read_duplicate_name_conflicts(pars, result.duplicate_name_conflicts, left_roster, right_roster);
       read_attribute_conflicts(pars, result.attribute_conflicts, left_roster, right_roster);
       read_file_content_conflicts(pars, result.file_content_conflicts, left_roster, right_roster);
@@ -2596,7 +2644,7 @@ roster_merge_result::read_conflict_file(
       db.get_roster(left_rid, left_roster, left_marking);
       db.get_roster(right_rid, right_roster, right_marking);
 
-      read_conflict_file_core(pars, left_roster, right_roster, *this, false);
+      read_conflict_file_core(pars, left_rid, left_roster, right_rid, right_roster, *this, false);
     }
   // else no conflicts
 
@@ -2694,9 +2742,12 @@ parse_resolve_conflicts_opts (options co
           pars.sym();
           pars.hex(temp);
 
+          read_conflict_file_core (pars, left_rid, left_roster, right_rid, right_roster, result, true);
+        }
+      else
+        {
           // if there is no ancestor revision, then left is an ancestor of
           // right, or vice versa, and there can be no conflicts.
-          read_conflict_file_core (pars, left_roster, right_roster, result, true);
         }
     }
   else
@@ -2761,7 +2812,7 @@ roster_merge_result::resolve_orphaned_no
           right_roster.get_name(conflict.nid, name);
         }
 
-      switch (conflict.resolution.first)
+      switch (conflict.resolution.resolution)
         {
         case resolve_conflicts::drop:
           if (is_dir_t(roster.get_node(conflict.nid)))
@@ -2775,9 +2826,8 @@ roster_merge_result::resolve_orphaned_no
           break;
 
         case resolve_conflicts::rename:
-          P(F("renaming '%s' to '%s'") % name % *conflict.resolution.second);
-          attach_node
-            (lua, roster, conflict.nid, file_path_internal (conflict.resolution.second->as_internal()));
+          P(F("renaming '%s' to '%s'") % name % conflict.resolution.rename);
+          attach_node (lua, roster, conflict.nid, conflict.resolution.rename);
           break;
 
         case resolve_conflicts::none:
@@ -2794,194 +2844,313 @@ roster_merge_result::resolve_orphaned_no
   orphaned_node_conflicts.clear();
 }
 
+static node_id
+create_new_node(roster_t const &            parent_roster,
+                string const &              side_image,
+                node_id const &             parent_nid,
+                roster_t &                  result_roster,
+                boost::shared_ptr<any_path> new_content,
+                content_merge_adaptor &     adaptor,
+                temp_node_id_source &       nis)
+{
+  file_path parent_name;
+  file_id   parent_fid;
+  file_data parent_data;
+
+  parent_roster.get_file_details(parent_nid, parent_fid, parent_name);
+  adaptor.get_version(parent_fid, parent_data);
+
+  P(F("replacing content of '%s' from %s with '%s'") % parent_name % side_image % new_content->as_external());
+
+  P(F(history_lost_msg) % parent_name % side_image);
+
+  data result_raw_data;
+  read_data(*new_content, result_raw_data);
+
+  file_data result_data = file_data(result_raw_data);
+  file_id result_fid;
+  calculate_ident(result_data, result_fid);
+
+  // User could specify no changes in content
+  if (result_fid != parent_fid)
+    {
+      adaptor.record_file(parent_fid, result_fid, parent_data, result_data);
+    }
+
+  return result_roster.create_file_node(result_fid, nis);
+}
+
 static void
-resolve_dropped_modified_user(roster_t &                      roster,
-                              node_id   &                     nid,
-                              file_id                         modified_fid,
-                              dropped_modified_conflict const conflict,
-                              content_merge_adaptor &         adaptor,
-                              temp_node_id_source &           nis)
+replace_content(roster_t const &            parent_roster,
+                string const &              side_image,
+                node_id  const &            nid,
+                roster_t &                  result_roster,
+                boost::shared_ptr<any_path> new_content,
+                content_merge_adaptor &     adaptor)
 {
-  // See comments in keep below on why we drop first
-  roster.drop_detached_node(nid);
+  file_path parent_name;
+  file_id   parent_fid;
 
-  file_data result_data;
+  parent_roster.get_file_details(nid, parent_fid, parent_name);
+
+  P(F("replacing content of '%s' from %s with '%s'") % parent_name % side_image % new_content->as_external());
+
+  file_data parent_data;
+  adaptor.get_version(parent_fid, parent_data);
+
   data result_raw_data;
-  file_id result_fid;
-  read_data(*conflict.resolution.second, result_raw_data);
+  read_data(*new_content, result_raw_data);
 
-  result_data = file_data(result_raw_data);
+  file_data result_data = file_data(result_raw_data);
+  file_id result_fid;
   calculate_ident(result_data, result_fid);
 
-  nid = roster.create_file_node(result_fid, nis);
+  file_t result_node = downcast_to_file_t(result_roster.get_node_for_update(nid));
+  result_node->content = result_fid;
 
-  // User could specify no changes
-  if (result_fid != modified_fid)
+  // User could specify no changes in content
+  if (result_fid != parent_fid)
     {
-      adaptor.record_file(result_fid, result_data);
+      adaptor.record_file(parent_fid, result_fid, parent_data, result_data);
     }
 }
 
-void
-roster_merge_result::resolve_dropped_modified_conflicts(lua_hooks & lua,
-                                                        roster_t const & left_roster,
-                                                        roster_t const & right_roster,
-                                                        content_merge_adaptor & adaptor,
-                                                        temp_node_id_source & nis)
+static void
+resolve_dropped_modified_one(lua_hooks &                                  lua,
+                             string                                       side_image,
+                             bool                                         handling_dropped_side,
+                             resolve_conflicts::file_resolution_t const & resolution,
+                             resolve_conflicts::file_resolution_t const & other_resolution,
+                             roster_t const &                             side_roster,
+                             file_path const &                            name,
+                             file_id const &                              fid,
+                             node_id const                                nid,
+                             content_merge_database_adaptor &             adaptor,
+                             temp_node_id_source &                        nis,
+                             roster_t &                                   result_roster)
 {
-  MM(left_roster);
-  MM(right_roster);
-  MM(this->roster); // New roster
-
-  // Conflict node is absent in the new roster
-
-  for (std::vector<dropped_modified_conflict>::const_iterator i = dropped_modified_conflicts.begin();
-       i != dropped_modified_conflicts.end();
-       ++i)
+  if (nid == the_null_node)
     {
-      dropped_modified_conflict const & conflict = *i;
-      MM(conflict);
+      E(resolution.resolution == resolve_conflicts::none, origin::user,
+        F("extra %s_resolution provided for dropped_modified '%s'") % side_image % name);
+      return;
+    }
+  else
+    {
+      E(resolution.resolution != resolve_conflicts::none, origin::user,
+        (other_resolution.resolution == resolve_conflicts::none) ?
+        F("no resolution provided for dropped_modified '%s'") % name :
+        F("no %s_resolution provided for dropped_modified '%s'") % side_image % name);
+    }
 
-      node_id   nid;
-      file_path modified_name;
-      file_id   modified_fid;
-      file_path recreated_name;
-      file_id   recreated_fid;
+  switch (resolution.resolution)
+    {
+    case resolve_conflicts::none:
+      // handled above; can't get here
+      break;
 
-      if (conflict.left_nid == the_null_node)
+    case resolve_conflicts::content_user:
+      // FIXME: check other_resolution for consistency
+      if (handling_dropped_side)
         {
-          nid = conflict.right_nid;
-          right_roster.get_file_details(nid, modified_fid, modified_name);
-
-          if (conflict.recreated != the_null_node)
-            {
-              roster.get_file_details(conflict.recreated, recreated_fid, recreated_name);
-            }
+          // recreated; replace the contents of the recreated node
+          replace_content(side_roster, side_image, nid, result_roster, resolution.content, adaptor);
+          attach_node(lua, result_roster, nid, name);
         }
       else
         {
-          nid = conflict.left_nid;
-          left_roster.get_file_details(nid, modified_fid, modified_name);
+          // modified; drop and create a new node
+          // See comments in keep below on why we drop first
+          result_roster.drop_detached_node(nid);
 
-          if (conflict.recreated != the_null_node)
-            {
-              roster.get_file_details(conflict.recreated, recreated_fid, recreated_name);
-            }
+          node_id new_nid = create_new_node
+            (side_roster, side_image, nid, result_roster, resolution.content, adaptor, nis);
+
+          attach_node(lua, result_roster, new_nid, name);
         }
+      break;
 
-      switch (conflict.resolution.first)
+    case resolve_conflicts::content_internal:
+      // not valid for dropped_modified
+      I(false);
+
+    case resolve_conflicts::drop:
+      // The node is either modified, recreated or duplicate name; in
+      // any case, it is present but detached in the result roster, so drop it
+      P(F("dropping '%s' from %s") % name % side_image);
+      result_roster.drop_detached_node(nid);
+      break;
+
+    case resolve_conflicts::keep:
+      if (handling_dropped_side)
         {
-        case resolve_conflicts::none:
-          E(false, origin::user,
-            F("no resolution provided for dropped_modifed '%s'") % modified_name);
-          break;
+          // recreated; keep the recreated contents
+          P(F("keeping '%s' from %s") % name % side_image);
+          attach_node(lua, result_roster, nid, name);
+        }
+      else
+        {
+          // modified; keep the modified contents
 
-        case resolve_conflicts::content_user:
-          P(F("replacing content of '%s' with '%s'") %
-            modified_name % conflict.resolution.second->as_external());
-          P(F("history for '%s' will be lost; see user manual Merge Conflicts section") %
-            modified_name);
+          P(F("keeping '%s' from %s") % name % side_image);
+          P(F(history_lost_msg) % name % side_image);
 
-          if (conflict.recreated == the_null_node)
-            {
-              resolve_dropped_modified_user(roster, nid, modified_fid, conflict, adaptor, nis);
-              attach_node(lua, roster, nid, modified_name);
-            }
-          else
-            {
-              // See comments in keep below on why we drop first
-              roster.drop_detached_node(nid);
+          // We'd like to just attach_node here, but that violates a
+          // fundamental design principle of mtn; nodes are born once,
+          // and die once. If we attach here, the node is born, died,
+          // and then born again.
+          //
+          // So we have to drop the old node, and create a new node with
+          // the same contents. That loses history; 'mtn log <path>'
+          // will end here, not showing the history of the original
+          // node.
+          result_roster.drop_detached_node(nid);
+          node_id nid = result_roster.create_file_node(fid, nis);
+          attach_node (lua, result_roster, nid, name);
+        }
+      break;
 
-              file_id result_fid;
-              file_data parent_data, result_data;
-              data result_raw_data;
-              adaptor.get_version(recreated_fid, parent_data);
+    case resolve_conflicts::rename:
+      if (handling_dropped_side)
+        {
+          // recreated; rename the recreated contents
+          P(F("renaming '%s' from %s to '%s'") % name % side_image % resolution.rename.as_external());
+          attach_node(lua, result_roster, nid, resolution.rename);
+        }
+      else
+        {
+          // modified; drop, create new node with the modified contents, rename
+          // See comment in keep above on why we drop first.
+          result_roster.drop_detached_node(nid);
 
-              read_data(*conflict.resolution.second, result_raw_data);
+          P(F("renaming '%s' from %s to '%s'") % name % side_image % resolution.rename.as_external());
+          P(F(history_lost_msg) % name % side_image);
 
-              result_data = file_data(result_raw_data);
-              calculate_ident(result_data, result_fid);
+          node_id new_nid = result_roster.create_file_node(fid, nis);
+          attach_node (lua, result_roster, new_nid, resolution.rename);
+        }
+      break;
 
-              file_t result_node = downcast_to_file_t(roster.get_node_for_update(conflict.recreated));
-              result_node->content = result_fid;
+    case resolve_conflicts::content_user_rename:
+      if (handling_dropped_side)
+        {
+          // recreated; rename and replace the recreated contents
+          replace_content(side_roster, side_image, nid, result_roster, resolution.content, adaptor);
 
-              adaptor.record_file(recreated_fid, result_fid, parent_data, result_data);
-            }
-          break;
+          P(F("renaming '%s' from %s to '%s'") % name % side_image % resolution.rename.as_external());
 
-        case resolve_conflicts::content_user_rename:
-          I(conflict.rename.as_external().length() != 0);
-          P(F("replacing content of '%s' (renamed to '%s') with '%s'") %
-            modified_name % conflict.rename.as_external() % conflict.resolution.second->as_external());
-          P(F("history for '%s' will be lost; see user manual Merge Conflicts section") %
-            modified_name);
+          attach_node (lua, result_roster, nid, resolution.rename);
+        }
+      else
+        {
+          // modified; drop, rename and replace the modified contents
+          result_roster.drop_detached_node(nid);
 
-          resolve_dropped_modified_user(roster, nid, modified_fid, conflict, adaptor, nis);
-          attach_node(lua, roster, nid, file_path_internal (conflict.rename.as_internal()));
-          break;
+          node_id new_nid = create_new_node
+            (side_roster, side_image, nid, result_roster, resolution.content, adaptor, nis);
 
-        case resolve_conflicts::drop:
-          P(F("dropping '%s'") % modified_name);
+          P(F("renaming '%s' from %s to '%s'") % name % side_image % resolution.rename.as_external());
 
-          roster.drop_detached_node(nid);
-          break;
+          attach_node(lua, result_roster, new_nid, resolution.rename);
+        }
+      break;
+    }
+} // resolve_dropped_modified_one
 
-        case resolve_conflicts::rename:
-          P(F("renaming '%s' to '%s'") % modified_name % conflict.resolution.second->as_external());
-          P(F("history for '%s' will be lost; see user manual Merge Conflicts section") %
-            modified_name);
+void
+roster_merge_result::resolve_dropped_modified_conflicts(lua_hooks & lua,
+                                                        roster_t const & left_roster,
+                                                        roster_t const & right_roster,
+                                                        content_merge_database_adaptor & adaptor,
+                                                        temp_node_id_source & nis)
+{
+  MM(left_roster);
+  MM(right_roster);
+  MM(this->roster); // New roster
 
-          // See comment in keep below on why we drop first.
-          roster.drop_detached_node(nid);
-          nid = roster.create_file_node(modified_fid, nis);
-          attach_node (lua, roster, nid, file_path_internal (conflict.resolution.second->as_internal()));
-          break;
+  for (std::vector<dropped_modified_conflict>::iterator i = dropped_modified_conflicts.begin();
+       i != dropped_modified_conflicts.end();
+       ++i)
+    {
+      dropped_modified_conflict & conflict = *i;
+      MM(conflict);
 
-        case resolve_conflicts::keep:
-          if (conflict.recreated == the_null_node)
+      file_path left_name;
+      file_id   left_fid;
+      file_path right_name;
+      file_id   right_fid;
+
+      if (conflict.left_nid != the_null_node)
+        {
+          if (conflict.left_rid == adaptor.left_rid)
             {
-              P(F("keeping '%s'") % modified_name);
-              P(F("history for '%s' will be lost; see user manual Merge Conflicts section") %
-                modified_name);
+              left_roster.get_file_details(conflict.left_nid, left_fid, left_name);
+            }
+          else
+            {
+              if (null_id(conflict.left_rid))
+                {
+                  // attr mtn::resolve_conflict drop does not set rid; find it now
+                  adaptor.get_dropped_details
+                    (adaptor.left_rid, conflict.left_nid, conflict.left_rid, left_name, left_fid);
+                }
+              else
+                {
+                  roster_t tmp;
+                  adaptor.db.get_roster(conflict.left_rid, tmp);
+                  tmp.get_file_details(conflict.left_nid, left_fid, left_name);
+                }
+            }
+        }
 
-              // We'd like to just attach_node here, but that violates a
-              // fundamental design principle of mtn; nodes are born once,
-              // and die once. If we attach here, the node is born, died,
-              // and then born again.
-              //
-              // So we have to drop the old node, and create a new node with
-              // the same contents. That loses history; 'mtn log <path>'
-              // will end here, not showing the history of the original
-              // node.
-              roster.drop_detached_node(nid);
-              nid = roster.create_file_node(modified_fid, nis);
-              attach_node (lua, roster, nid, modified_name);
+      if (conflict.right_nid != the_null_node)
+        {
+          if (conflict.right_rid == adaptor.right_rid)
+            {
+              right_roster.get_file_details(conflict.right_nid, right_fid, right_name);
             }
           else
             {
-              P(F("keeping '%s' from %s") % modified_name % ((conflict.left_nid == the_null_node) ? "right" : "left"));
-              P(F("history for '%s' will be lost; see user manual Merge Conflicts section") %
-                modified_name);
+              if (null_id(conflict.left_rid))
+                {
+                  adaptor.get_dropped_details
+                    (adaptor.right_rid, conflict.right_nid, conflict.right_rid, right_name, right_fid);
+                }
+              else
+                {
+                  roster_t tmp;
+                  adaptor.db.get_roster(conflict.right_rid, tmp);
+                  tmp.get_file_details(conflict.right_nid, right_fid, right_name);
+                }
+            }
+        }
 
-              roster.drop_detached_node(nid);
+      resolve_dropped_modified_one (lua,
+                                    string("left"),
+                                    conflict.dropped_side == resolve_conflicts::left_side,
+                                    conflict.left_resolution,
+                                    conflict.right_resolution,
+                                    left_roster,
+                                    left_name,
+                                    left_fid,
+                                    conflict.left_nid,
+                                    adaptor,
+                                    nis,
+                                    roster);
 
-              // keep the modified content, not the recreated content
-              file_data parent_data, result_data;
-              adaptor.get_version(recreated_fid, parent_data);
+      resolve_dropped_modified_one (lua,
+                                    string("right"),
+                                    conflict.dropped_side == resolve_conflicts::right_side,
+                                    conflict.right_resolution,
+                                    conflict.left_resolution,
+                                    right_roster,
+                                    right_name,
+                                    right_fid,
+                                    conflict.right_nid,
+                                    adaptor,
+                                    nis,
+                                    roster);
 
-              adaptor.get_version(modified_fid, result_data);
-
-              file_t result_node = downcast_to_file_t(roster.get_node_for_update(conflict.recreated));
-              result_node->content = modified_fid;
-
-              adaptor.record_file(recreated_fid, modified_fid, parent_data, result_data);
-            }
-          break;
-
-        default:
-          I(false);
-        }
-
     } // end for
 
   dropped_modified_conflicts.clear();
@@ -2997,23 +3166,23 @@ resolve_duplicate_name_one_side(lua_hook
                                 content_merge_adaptor & adaptor,
                                 roster_t & result_roster)
 {
-  switch (resolution.first)
+  switch (resolution.resolution)
     {
     case resolve_conflicts::content_user:
       {
-        E(other_resolution.first == resolve_conflicts::drop ||
-          other_resolution.first == resolve_conflicts::rename,
+        E(other_resolution.resolution == resolve_conflicts::drop ||
+          other_resolution.resolution == resolve_conflicts::rename,
           origin::user,
           F("inconsistent left/right resolutions for '%s'") % name);
 
-        P(F("replacing content of '%s' with '%s'") % name % resolution.second->as_external());
+        P(F("replacing content of '%s' with '%s'") % name % resolution.content->as_external());
 
         file_id result_fid;
         file_data parent_data, result_data;
         data result_raw_data;
         adaptor.get_version(fid, parent_data);
 
-        read_data(*resolution.second, result_raw_data);
+        read_data(*resolution.content, result_raw_data);
 
         result_data = file_data(result_raw_data);
         calculate_ident(result_data, result_fid);
@@ -3039,8 +3208,8 @@ resolve_duplicate_name_one_side(lua_hook
       break;
 
     case resolve_conflicts::keep:
-      E(other_resolution.first == resolve_conflicts::drop ||
-        other_resolution.first == resolve_conflicts::rename,
+      E(other_resolution.resolution == resolve_conflicts::drop ||
+        other_resolution.resolution == resolve_conflicts::rename,
         origin::user,
         F("inconsistent left/right resolutions for '%s'") % name);
 
@@ -3049,9 +3218,8 @@ resolve_duplicate_name_one_side(lua_hook
       break;
 
     case resolve_conflicts::rename:
-      P(F("renaming '%s' to '%s'") % name % *resolution.second);
-      attach_node
-        (lua, result_roster, nid, file_path_internal (resolution.second->as_internal()));
+      P(F("renaming '%s' to '%s'") % name % resolution.rename);
+      attach_node (lua, result_roster, nid, resolution.rename);
       break;
 
     case resolve_conflicts::none:
@@ -3146,7 +3314,7 @@ roster_merge_result::resolve_file_conten
       left_roster.get_name(conflict.nid, left_name);
       right_roster.get_name(conflict.nid, right_name);
 
-      switch (conflict.resolution.first)
+      switch (conflict.resolution.resolution)
         {
           case resolve_conflicts::content_internal:
           case resolve_conflicts::none:
@@ -3168,7 +3336,7 @@ roster_merge_result::resolve_file_conten
           case resolve_conflicts::content_user:
             {
               P(F("replacing content of '%s', '%s' with '%s'") %
-                left_name % right_name % conflict.resolution.second->as_external());
+                left_name % right_name % conflict.resolution.content->as_external());
 
               file_id result_id;
               file_data left_data, right_data, result_data;
@@ -3176,7 +3344,7 @@ roster_merge_result::resolve_file_conten
               adaptor.get_version(conflict.left, left_data);
               adaptor.get_version(conflict.right, right_data);
 
-              read_data(*conflict.resolution.second, result_raw_data);
+              read_data(*conflict.resolution.content, result_raw_data);
 
               result_data = file_data(result_raw_data);
               calculate_ident(result_data, result_id);
============================================================
--- src/merge_content.cc	662433acaf08fc1f3f3417389a9bc3b3d2bd9bf1
+++ src/merge_content.cc	095c2d20ac8d8d495806f6463c311d253cb6a686
@@ -189,8 +189,9 @@ content_merge_database_adaptor::get_drop
   set<revision_id> parents;
   db.get_revision_parents(rev_id, parents);
 
-  for (set<revision_id>::iterator i = parents.begin(); i != parents.end(); i++)
+  while (parents.begin() != parents.end())
     {
+      set<revision_id>::iterator i = parents.begin();
       roster_t roster;
       marking_map marking_map;
 
@@ -203,6 +204,7 @@ content_merge_database_adaptor::get_drop
         }
       else
         {
+          parents.erase (i);
           set<revision_id> more_parents;
           db.get_revision_parents(*i, more_parents);
           parents.insert(more_parents.begin(), more_parents.end());
@@ -745,7 +747,8 @@ resolve_merge_conflicts(lua_hooks & lua,
           // Resolve the ones we can, if they have resolutions specified. Each
           // conflict list is deleted once all are resolved.
           result.resolve_orphaned_node_conflicts(lua, left_roster, right_roster, adaptor);
-          result.resolve_dropped_modified_conflicts(lua, left_roster, right_roster, adaptor, nis);
+          result.resolve_dropped_modified_conflicts(lua, left_roster, right_roster,
+                                                    dynamic_cast <content_merge_database_adaptor&>(adaptor), nis);
           result.resolve_duplicate_name_conflicts(lua, left_roster, right_roster, adaptor);
 
           result.resolve_file_content_conflicts (lua, left_roster, right_roster, adaptor);
============================================================
--- src/merge_roster.cc	5fbc50c114df22f4753f444aa137c06ea0f06195
+++ src/merge_roster.cc	c3eb70578a3b8b88c4d1b7af43b7685d6f636c23
@@ -26,27 +26,61 @@ enum side_t {left_side, right_side};
 
 enum side_t {left_side, right_side};
 
-static char const *
-image(resolve_conflicts::resolution_t resolution)
+namespace resolve_conflicts
 {
-  switch (resolution)
-    {
-    case resolve_conflicts::none:
-      return "none";
-    case resolve_conflicts::content_user:
-      return "content_user";
-    case resolve_conflicts::content_internal:
-      return "content_internal";
-    case resolve_conflicts::drop:
-      return "drop";
-    case resolve_conflicts::keep:
-      return "keep";
-    case resolve_conflicts::rename:
-      return "rename";
-    case resolve_conflicts::content_user_rename:
-      return "content_user_rename";
-    }
-  I(false); // keep compiler happy
+  char const *
+  image(side_t item)
+  {
+    switch (item)
+      {
+      case resolve_conflicts::left_side:
+        return "left_side";
+      case resolve_conflicts::right_side:
+        return "right_side";
+      }
+    I(false); // keep compiler happy
+  }
+
+  char const *
+  image(resolution_t resolution)
+  {
+    switch (resolution)
+      {
+      case resolve_conflicts::none:
+        return "none";
+      case resolve_conflicts::content_user:
+        return "content_user";
+      case resolve_conflicts::content_internal:
+        return "content_internal";
+      case resolve_conflicts::drop:
+        return "drop";
+      case resolve_conflicts::keep:
+        return "keep";
+      case resolve_conflicts::rename:
+        return "rename";
+      case resolve_conflicts::content_user_rename:
+        return "content_user_rename";
+      }
+    I(false); // keep compiler happy
+  }
+
+  string
+  image(file_resolution_t res)
+  {
+    if (res.resolution == resolve_conflicts::none)
+      return string("\n");
+    else
+      {
+        ostringstream oss;
+        oss << "resolution: " << image(res.resolution);
+        if (res.content != 0)
+          oss << ", content: '" << res.content->as_external() << "'";
+        if (res.rename.as_internal().length()>0)
+          oss << ", rename: '" << res.rename.as_external() << "'";
+        oss << "\n";
+        return oss.str();
+      }
+  }
 }
 
 template <> void
@@ -95,16 +129,15 @@ dump(dropped_modified_conflict const & c
 dump(dropped_modified_conflict const & conflict, string & out)
 {
   ostringstream oss;
-  oss << "dropped_modified_conflict on node: " <<
-    conflict.left_nid == the_null_node ? conflict.right_nid : conflict.left_nid;
-  oss << " orphaned: " << conflict.orphaned;
-  if (conflict.resolution.first != resolve_conflicts::none)
-    {
-      oss << " resolution: " << image(conflict.resolution.first);
-      oss << " new_content_name: " << conflict.resolution.second;
-      oss << " rename: " << conflict.rename;
-    }
-  oss << "\n";
+  oss << "dropped_modified_conflict -\n";
+  oss << " dropped_side    : " << image(conflict.dropped_side) << "\n";
+  oss << " left_nid        : " << conflict.left_nid << "\n";
+  oss << " right_nid       : " << conflict.right_nid << "\n";
+  oss << " orphaned        : " << conflict.orphaned << "\n";
+  oss << " left_rid        : " << conflict.left_rid << "\n";
+  oss << " right_rid       : " << conflict.right_rid << "\n";
+  oss << " left_resolution : " << image(conflict.left_resolution);
+  oss << " right_resolution: " << image(conflict.right_resolution);
   out = oss.str();
 }
 
@@ -115,19 +148,10 @@ dump(duplicate_name_conflict const & con
   oss << "duplicate_name_conflict between left node: " << conflict.left_nid << " "
       << "and right node: " << conflict.right_nid << " "
       << "parent: " << conflict.parent_name.first << " "
-      << "basename: " << conflict.parent_name.second;
-
-  if (conflict.left_resolution.first != resolve_conflicts::none)
-    {
-      oss << " left_resolution: " << image(conflict.left_resolution.first);
-      oss << " left_name: " << conflict.left_resolution.second;
-    }
-  if (conflict.right_resolution.first != resolve_conflicts::none)
-    {
-      oss << " right_resolution: " << image(conflict.right_resolution.first);
-      oss << " right_name: " << conflict.right_resolution.second;
-    }
-  oss << "\n";
+      << "basename: " << conflict.parent_name.second << " "
+      << "left_resolution: " << image(conflict.left_resolution)
+      << "right_resolution: " << image(conflict.right_resolution)
+      << "\n";
   out = oss.str();
 }
 
@@ -147,12 +171,7 @@ dump(file_content_conflict const & confl
 {
   ostringstream oss;
   oss << "file_content_conflict on node: " << conflict.nid;
-
-  if (conflict.resolution.first != resolve_conflicts::none)
-    {
-      oss << " resolution: " << image(conflict.resolution.first);
-      oss << " name: " << conflict.resolution.second;
-    }
+  oss << " resolution: " << image(conflict.resolution);
   oss << "\n";
   out = oss.str();
 }
@@ -374,7 +393,7 @@ namespace
                 switch (present_in)
                   {
                   case left_side:
-                      conflict = dropped_modified_conflict(n->self, the_null_node);
+                    conflict = dropped_modified_conflict(n->self, the_null_node);
                     break;
                   case right_side:
                     conflict = dropped_modified_conflict(the_null_node, n->self);
@@ -385,7 +404,15 @@ namespace
                   {
                     if (i->second.second == typecast_vocab<attr_value>(utf8("drop")))
                       {
-                        conflict.resolution.first = resolve_conflicts::drop;
+                        switch (present_in)
+                          {
+                          case left_side:
+                            conflict.left_resolution.resolution = resolve_conflicts::drop;
+                            break;
+                          case right_side:
+                            conflict.right_resolution.resolution = resolve_conflicts::drop;
+                            break;
+                          }
                       }
                     else
                       {
@@ -791,26 +818,83 @@ roster_merge(roster_t const & left_paren
     I(new_i == result.roster.all_nodes().end());
   }
 
-  // now we can look for dropped_modified conflicts with recreated nodes
+  // now we can look for dropped_modified conflicts with recreated nodes or
+  // duplicate names
   for (size_t i = 0; i < result.dropped_modified_conflicts.size(); ++i)
     {
       dropped_modified_conflict & conflict = result.dropped_modified_conflicts[i];
 
+      // If the file name was recreated, it is present in the result with
+      // the modified_name but different node id; find that and unattach it.
+      // Or, it may now subject to a duplicate_name conflict (see
+      // test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2).
+      // In which case modified_name will be present in the other parent,
+      // but not in the result.
+
       file_path modified_name;
-      if (conflict.left_nid == the_null_node)
+      node_id   nid;
+      bool duplicate_name = false;
+
+      switch (conflict.dropped_side)
         {
+        case resolve_conflicts::left_side:
           right_parent.get_name(conflict.right_nid, modified_name);
-        }
-      else
-        {
+
+          if (result.roster.has_node(modified_name))
+            {
+              // recreated; we need to detach the node for the conflict
+              // resolution process. We'd like to just do:
+              // result.roster.detach_node(modified_name);
+              // but that doesn't erase result.roster.old_locations properly
+
+              const_node_t const & left_node      = left_parent.get_node(modified_name);
+              node_id              parent         = left_node->parent;
+              path_component       component_name = left_node->name;
+              dir_t                p              = downcast_to_dir_t(result.roster.get_node_for_update(parent));
+              conflict.left_nid                   = left_node->self;
+              p->detach_child(component_name);
+            }
+          else if (left_parent.has_node (modified_name))
+            {
+              conflict.left_nid = left_parent.get_node(modified_name)->self;
+              nid               = conflict.left_nid;
+              duplicate_name    = true;
+            }
+          break;
+
+        case resolve_conflicts::right_side:
           left_parent.get_name(conflict.left_nid, modified_name);
+
+          if (result.roster.has_node(modified_name))
+            {
+              // recreated; see comment in left_side above
+              const_node_t const & right_node     = right_parent.get_node(modified_name);
+              node_id              parent         = right_node->parent;
+              path_component       component_name = right_node->name;
+              dir_t                p              = downcast_to_dir_t(result.roster.get_node_for_update(parent));
+              conflict.right_nid                  = right_node->self;
+              p->detach_child(component_name);
+            }
+          else if (right_parent.has_node (modified_name))
+            {
+              conflict.right_nid = right_parent.get_node(modified_name)->self;
+              nid                = conflict.right_nid;
+              duplicate_name     = true;
+            }
+          break;
         }
 
-      if (result.roster.has_node(modified_name))
+      if (duplicate_name)
         {
-          conflict.recreated = result.roster.get_node(modified_name)->self;
+          // delete the duplicate name conflict; it will be handled by dropped_modified.
+          std::vector<duplicate_name_conflict>::iterator i =
+            find(result.duplicate_name_conflicts.begin(),
+                 result.duplicate_name_conflicts.end(),
+                 nid);
+
+          result.duplicate_name_conflicts.erase(i);
         }
-    }
+    } // end dropped_modified loop
 
   // now check for the possible global problems
   if (!result.roster.has_root())
============================================================
--- src/merge_roster.hh	34d382b0ad333fa0f052e8640a88c1ece03caaa9
+++ src/merge_roster.hh	c782f7684e380f4583ed7ea88b0b2967f89d1d23
@@ -31,10 +31,31 @@ namespace resolve_conflicts
 {
   enum resolution_t {none, content_user, content_internal, drop, keep, rename, content_user_rename};
 
-  typedef std::pair<resolve_conflicts::resolution_t, boost::shared_ptr<any_path> > file_resolution_t;
+  char const * image(resolution_t item);
 
-  boost::shared_ptr<any_path> new_file_path(std::string path);
+  enum side_t {left_side, right_side};
 
+  char const * image(side_t item);
+
+  struct file_resolution_t
+  {
+    resolution_t resolution;
+    boost::shared_ptr<any_path> content;
+    file_path rename;
+
+    file_resolution_t() :
+      resolution(none),
+      content(),
+      rename()
+      {}
+  };
+
+  std::string image(file_resolution_t res);
+
+  // For filename read from conflicts file; converts path to utf8. basic_io
+  // parser should return utf8 in the first place.
+  file_path file_path_external(std::string path);
+
 }
 
 // renaming the root dir allows these:
@@ -94,34 +115,54 @@ struct dropped_modified_conflict
 // roster, with null parent and name fields.
 struct dropped_modified_conflict
 {
-  node_id left_nid, right_nid; // the dropped side is the null node, modified is valid.
+  // A dropped_modified conflict can be the result of a repeated
+  // duplicate_name conflict (see
+  // ../test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2)
+  //
+  // If the user has recreated the dropped node, that also looks like a
+  // duplicate name conflict.
+  //
+  // In either case both nids are non-null.
+  //
+  // Dropped_modified can also be due to a dropped directory, in which case
+  // this looks like an orphaned_node conflict.
+  //
+  // If the resolution for the dropped node is 'keep', we need the revision
+  // that contains the node id, which is an ancestor of the merge parent.
+  // That information is also useful for an external front end, that wants to
+  // retreive the file contents for a merge tool.
 
-  bool orphaned; // if true, the dropped side is due to a dropped parent directory
+  resolve_conflicts::side_t dropped_side;
 
-  node_id recreated; // by user, or in a previous drop/modified resolution
+ // If nid is set, it is in corresponding merge parent.
+  node_id left_nid, right_nid;
 
-  resolve_conflicts::file_resolution_t resolution;
-  file_path rename;
-  // if orphaned is true, the resolutions are 'drop' and 'user rename'; the
-  // latter requires two paths; content in resolution->second, filename in
-  // rename.
+  bool orphaned; // if true, the dropped side is due to a dropped parent directory
 
+  revision_id                          left_rid, right_rid;
+  resolve_conflicts::file_resolution_t left_resolution, right_resolution;
+
   dropped_modified_conflict(node_id left_nid, node_id right_nid) :
     left_nid(left_nid),
     right_nid(right_nid),
     orphaned(false),
-    recreated(the_null_node)
-    // rename is implicitly null
-  {resolution.first = resolve_conflicts::none;}
+    left_rid(),
+    right_rid(),
+    left_resolution(),
+    right_resolution()
+  {dropped_side = (left_nid == the_null_node ? resolve_conflicts::left_side : resolve_conflicts::right_side);}
 
   dropped_modified_conflict() :
     left_nid(the_null_node),
     right_nid(the_null_node),
     orphaned(false),
-    recreated(the_null_node)
-    // rename is implicitly null
-  {resolution.first = resolve_conflicts::none;}
+    left_rid(),
+    right_rid(),
+    left_resolution(),
+    right_resolution()
+  {}
 
+  // for find
   bool operator==(node_id n) {return left_nid == n || right_nid == n;}
 };
 
@@ -147,9 +188,10 @@ struct duplicate_name_conflict
   // it may be a bookkeeping or system path if resolution is 'user'.
   resolve_conflicts::file_resolution_t left_resolution, right_resolution;
 
-  duplicate_name_conflict ()
-  {left_resolution.first = resolve_conflicts::none;
-    right_resolution.first = resolve_conflicts::none;};
+  duplicate_name_conflict() {};
+
+  // for find
+  bool operator==(node_id n) {return left_nid == n || right_nid == n;}
 };
 
 // nodes with attribute conflicts are left attached in the resulting tree (unless
@@ -174,12 +216,15 @@ struct file_content_conflict
   file_id ancestor, left, right; // ancestor is set only when reading in a conflicts file
   resolve_conflicts::file_resolution_t resolution;
 
-  file_content_conflict () :
-    nid(the_null_node)
-    {resolution.first = resolve_conflicts::none;};
+  file_content_conflict() :
+    nid(the_null_node),
+    resolution()
+  {};
 
   file_content_conflict(node_id nid) :
-    nid(nid) {resolution.first = resolve_conflicts::none;};
+    nid(nid),
+    resolution()
+  {};
 };
 
 template <> void dump(invalid_name_conflict const & conflict, std::string & out);
@@ -269,7 +314,7 @@ struct roster_merge_result
   void resolve_dropped_modified_conflicts(lua_hooks & lua,
                                           roster_t const & left_roster,
                                           roster_t const & right_roster,
-                                          content_merge_adaptor & adaptor,
+                                          content_merge_database_adaptor & adaptor,
                                           temp_node_id_source & nis);
 
   void report_duplicate_name_conflicts(roster_t const & left,
============================================================
--- src/roster.cc	3f81121ce80b42565e6e5e4bbe3e6186b85e9b10
+++ src/roster.cc	b0608d6ec3e3f2d23a5c97bb64c4354eeb01773a
@@ -1188,14 +1188,30 @@ dump(roster_t const & val, string & out)
   out = oss.str();
 }
 
+template <> void
+dump(std::map<node_id, std::pair<node_id, path_component> > const & val, string & out)
+{
+  ostringstream oss;
+  for (std::map<node_id, std::pair<node_id, path_component> >::const_iterator i = val.begin();
+       i != val.end();
+       ++i)
+    {
+      oss << "Node " << i->first;
+      oss << " node " << i->second.first;
+      oss << " path " << i->second.second << "\n";
+    }
+  out = oss.str();
+}
+
 void
 roster_t::check_sane(bool temp_nodes_ok) const
 {
   MM(*this);
+  MM(old_locations);
 
   node_id parent_id(the_null_node);
   const_dir_t parent_dir;
-  I(old_locations.empty());
+  I(old_locations.empty()); // if fail, some renamed node is still present and detached
   I(has_root());
   size_t maxdepth = nodes.size();
   bool is_first = true;
@@ -1235,7 +1251,7 @@ roster_t::check_sane(bool temp_nodes_ok)
       I(n == get_node(nid));
       I(maxdepth-- > 0);
     }
-  I(maxdepth == 0);
+  I(maxdepth == 0); // if fails, some newly created node is not attached
 }
 
 void
============================================================
--- test/func/resolve_conflicts_dropped_modified/__driver__.lua	e4f973e6cb8e3494c4b498f68f7cd4b2a6d20fa6
+++ test/func/resolve_conflicts_dropped_modified_1/__driver__.lua	2c87a2a5810a34019c4ca4148eb2b9bede764267
@@ -1,25 +1,55 @@
 -- Test reporting and resolving drop/modified conflicts
 --
+-- tests for the restrictions imposed by orphan, resoltion consistency
+-- are in resolved_dropped_modified_3
+-- 
 -- other resolve_conflicts_dropped_modified_* tests validate resolving
 -- in extended use cases.
 
+-- Parent nodes can be in several states: dropped, modified,
+-- recreated. Modified nodes can also be renamed, but that is
+-- orthogonal to resolutions; the parent name is used to detect
+-- recreate or duplicate name, and to name the result node if no
+-- rename resolution is specified. Dropped files can also be orphaned,
+-- but that just restricts the allowed resolutions (no keep).
+-- 
+-- We need to test all combinations of left/right, parent node state,
+-- and resolution:
+--
+-- state    resolution      left file       right file
+-- dropped  drop            file_3 etc      file_2 etc
+--          keep            (not supported)
+--          rename          (not supported)
+--          user            (not supported)
+--          user_rename     (not supported)
+-- 
+-- modified drop            file_2      file_3 etc
+--          keep            file_4      file_5
+--          rename          file_14     file_9
+--          user            file_6      file_7
+--          user_rename     file_15     file_10
+-- 
+-- recreated drop           file_16     file_12
+--           keep           file_12     file_14
+--           rename         file_17     file_15
+--           user           file_13     file_16
+--           user_rename    file_18     file_13
+--
+
 mtn_setup()
 
--- Create conflicts; modify and rename file in one head, drop in
--- other.
+-- Create conflicts with single resolutions; modify and/or rename file in
+-- one parent, drop in the other.
 -- 
--- Six conflicts to test the three possible resolutions, with drop on
--- both left and right. Number in file name is the node number (helps
--- in debugging; node 1 is the root directory).
---
--- The case of a modified file in a dropped directory is tested below.
+-- Six conflicts to test three possible resolutions, with drop on
+-- both left and right. 
 
-addfile("file_2", "file_2 base") -- modify/rename left, drop right; drop
-addfile("file_3", "file_3 base") -- drop left, modify/rename right; drop
-addfile("file_4", "file_4 base") -- modify left; modify, rename, and drop right; keep
-addfile("file_5", "file_5 base") -- modify, rename, and drop left; modify right; keep
-addfile("file_6", "file_6 base") -- modify/rename left, drop right; user
-addfile("file_7", "file_7 base") -- drop left, modify/rename right; user
+addfile("file_2", "file_2 base") -- modify/rename left: drop right: drop left, drop right
+addfile("file_3", "file_3 base") -- drop left; modify/rename right: drop left, drop right
+addfile("file_4", "file_4 base") -- modify left; modify, rename, and drop right; keep left, drop right
+addfile("file_5", "file_5 base") -- modify, rename, and drop left; modify right; drop left, keep right
+addfile("file_6", "file_6 base") -- modify/rename left, drop right; user left, drop right
+addfile("file_7", "file_7 base") -- drop left, modify/rename right; drop left, user right
 commit("testbranch", "base")
 base = base_revision()
 
@@ -92,6 +122,8 @@ check(samelines("stderr",
  "mtn: dropped on the right",
  "mtn: possible resolutions:",
  "mtn: resolve_first drop",
+ "mtn: resolve_first rename",
+ "mtn: resolve_first user_rename \"new_content_name\" \"new_file_name\"",
  "mtn: resolve_first keep",
  "mtn: resolve_first user \"name\""}))
 
@@ -100,7 +132,7 @@ check(mtn("explicit_merge", "--resolve-c
 -- check for nice error message if not all dropped_modified conflicts are resolved
 -- we have to use explicit_merge to get left/right to match 'conflicts store'
 check(mtn("explicit_merge", "--resolve-conflicts", left_1, right_1, "testbranch"), 1, nil, true)
-check(qgrep("no resolution provided for", "stderr"))
+check(qgrep("no resolution provided for dropped_modified 'file_3_renamed'", "stderr"))
              
 check(mtn("conflicts", "show_first"), 0, nil, true)
 check(samelines("stderr",
@@ -109,6 +141,8 @@ check(samelines("stderr",
  "mtn: modified on the right",
  "mtn: possible resolutions:",
  "mtn: resolve_first drop",
+ "mtn: resolve_first rename",
+ "mtn: resolve_first user_rename \"new_content_name\" \"new_file_name\"",
  "mtn: resolve_first keep",
  "mtn: resolve_first user \"name\""}))
 
@@ -121,6 +155,8 @@ check(samelines("stderr",
  "mtn: dropped on the right",
  "mtn: possible resolutions:",
  "mtn: resolve_first drop",
+ "mtn: resolve_first rename",
+ "mtn: resolve_first user_rename \"new_content_name\" \"new_file_name\"",
  "mtn: resolve_first keep",
  "mtn: resolve_first user \"name\""}))
 
@@ -133,6 +169,8 @@ check(samelines("stderr",
  "mtn: modified on the right",
  "mtn: possible resolutions:",
  "mtn: resolve_first drop",
+ "mtn: resolve_first rename",
+ "mtn: resolve_first user_rename \"new_content_name\" \"new_file_name\"",
  "mtn: resolve_first keep",
  "mtn: resolve_first user \"name\""}))
 
@@ -145,6 +183,8 @@ check(samelines("stderr",
  "mtn: dropped on the right",
  "mtn: possible resolutions:",
  "mtn: resolve_first drop",
+ "mtn: resolve_first rename",
+ "mtn: resolve_first user_rename \"new_content_name\" \"new_file_name\"",
  "mtn: resolve_first keep",
  "mtn: resolve_first user \"name\""}))
 
@@ -159,10 +199,11 @@ check(samelines("stderr",
  "mtn: modified on the right",
  "mtn: possible resolutions:",
  "mtn: resolve_first drop",
+ "mtn: resolve_first rename",
+ "mtn: resolve_first user_rename \"new_content_name\" \"new_file_name\"",
  "mtn: resolve_first keep",
  "mtn: resolve_first user \"name\""}))
 
-mkdir("_MTN/resolutions")
 writefile("_MTN/resolutions/file_7_resolved", "file_7 resolved")
 check(mtn("conflicts", "resolve_first", "user", "_MTN/resolutions/file_7_resolved"), 0, nil, true)
 
@@ -171,16 +212,23 @@ check(mtn("explicit_merge", "--resolve-c
 
 -- we have to use explicit_merge to get left/right to match 'conflicts store'
 check(mtn("explicit_merge", "--resolve-conflicts", left_1, right_1, "testbranch"), 0, nil, true)
-check(qgrep("dropping 'file_2_renamed'", "stderr"))
-check(qgrep("dropping 'file_3_renamed'", "stderr"))
-check(qgrep("keeping 'file_4'", "stderr"))
-check(qgrep("keeping 'file_5'", "stderr"))
-check(qgrep("replacing content of 'file_6_renamed' with '_MTN/resolutions/file_6_resolved", "stderr"))
-check(qgrep("replacing content of 'file_7_renamed' with '_MTN/resolutions/file_7_resolved", "stderr"))
-check(not qgrep("warning", "stderr"))
+check(samelines("stderr",
+{"mtn: [left]  7b2ef4343b0717bcd122498a1a0b7ff7acffb64c",
+ "mtn: [right] ca7922b510f9daf5c4b28c6788315ee82eb9a7f0",
+ "mtn: dropping 'file_2_renamed' from left",
+ "mtn: dropping 'file_3_renamed' from right",
+ "mtn: keeping 'file_4' from left",
+ "mtn: history for 'file_4' from left will be lost; see user manual Merge Conflicts section",
+ "mtn: keeping 'file_5' from right",
+ "mtn: history for 'file_5' from right will be lost; see user manual Merge Conflicts section",
+ "mtn: replacing content of 'file_6_renamed' from left with '_MTN/resolutions/file_6_resolved'",
+ "mtn: history for 'file_6_renamed' from left will be lost; see user manual Merge Conflicts section",
+ "mtn: replacing content of 'file_7_renamed' from right with '_MTN/resolutions/file_7_resolved'",
+ "mtn: history for 'file_7_renamed' from right will be lost; see user manual Merge Conflicts section",
+ "mtn: [merged] 57bf835ef0434411189dc3eca1650a6bba513c14"}))
 
 -- If a file is renamed (without other change) and dropped,
--- the change is ignored:
+-- the rename is ignored:
 
 addfile("file_8", "file_8 base") -- rename left, drop right
 commit("testbranch", "base 2")
@@ -208,13 +256,13 @@ check(qgrep("0 conflicts", "stderr"))
 -- make sense). This used to be the test
 -- "(imp)_merge((patch_foo_a),_(delete_foo_))"
 --
--- We create three potential conflicts; one ignored, three with different resolutions:
+-- We create four potential conflicts; one ignored, three with different resolutions:
 
-adddir("dir1") -- empty, dropped and renamed (not a conflict; just dropped)
-mkdir("dir2")  -- not empty, dropped, contents modified
-addfile("dir2/file_9", "file_9 base") -- resolved by rename
-addfile("dir2/file_10", "file_10 base") -- resolved by user_rename
-addfile("dir2/file_11", "file_11 base") -- resolved by drop
+adddir("dir1") -- empty - drop left; rename right (not a conflict; just dropped)
+mkdir("dir2")  -- not empty - modified left; drop right
+addfile("dir2/file_9", "file_9 base") -- resolution: rename left, drop right
+addfile("dir2/file_10", "file_10 base") -- resolution: user_rename left, drop right
+addfile("dir2/file_11", "file_11 base") -- resolution: drop left, drop right
 commit("testbranch", "base 3")
 base_3 = base_revision()
 
@@ -260,6 +308,15 @@ check(mtn("conflicts", "resolve_first", 
 writefile("_MTN/resolutions/file_10", "file_10 user")
 check(mtn("conflicts", "resolve_first", "user_rename", "_MTN/resolutions/file_10", "file_10"), 0, nil, true)
 
+-- Test error message from invalid resolution
+check(mtn("conflicts", "resolve_first", "keep"), 1, nil, true)
+check(samelines("stderr",
+{"mtn: misuse: orphaned files must be renamed"}))
+
+check(mtn("conflicts", "resolve_first", "user", "foo"), 1, nil, true)
+check(samelines("stderr",
+{"mtn: misuse: orphaned files must be renamed"}))
+
 check(mtn("conflicts", "resolve_first", "drop"), 0, nil, nil)
 
 check(mtn("conflicts", "resolve_first", "rename", "file_9"), 0, nil, nil)
@@ -270,97 +327,188 @@ check(samelines("stderr",
 check(samelines("stderr",
 {"mtn: [left]  4228fbd8003cdd89e7eea51fcef10c3f91d78f69",
  "mtn: [right] 6cb6438a490a1ad4c69ff6cac23c75a903cd9cfd",
- "mtn: replacing content of 'dir2/file_10' (renamed to 'file_10') with '_MTN/resolutions/file_10'",
- "mtn: history for 'dir2/file_10' will be lost; see user manual Merge Conflicts section",
- "mtn: dropping 'dir2/file_11'",
- "mtn: renaming 'dir2/file_9' to 'file_9'",
- "mtn: history for 'dir2/file_9' will be lost; see user manual Merge Conflicts section",
+ "mtn: replacing content of 'dir2/file_10' from left with '_MTN/resolutions/file_10'",
+ "mtn: history for 'dir2/file_10' from left will be lost; see user manual Merge Conflicts section",
+ "mtn: renaming 'dir2/file_10' from left to 'file_10'",
+ "mtn: dropping 'dir2/file_11' from left",
+ "mtn: renaming 'dir2/file_9' from left to 'file_9'",
+ "mtn: history for 'dir2/file_9' from left will be lost; see user manual Merge Conflicts section",
  "mtn: [merged] 5cafe5405ed31c81f9061be62e38f25aeaaea9c5"}))
- 
--- A special case; drop then re-add vs modify. This used to be the test
+
+check(mtn("update"), 0, nil, true)
+
+-- Test recreated; drop then re-add vs modify. This used to be the test
 -- "merge((patch_a),_(drop_a,_add_a))"
-addfile("file_10", "file_10 base") -- modify in left; drop, add in right
-addfile("file_11", "file_11 base") -- drop, add in left; modify in right
+addfile("file_12", "file_12 base") -- modify in left; drop, add in right: keep left, drop right
+addfile("file_13", "file_13 base") -- drop, add in left; modify in right: user left, user_rename right
+
+-- Other cases not covered above
+addfile("file_14", "file_14 base") -- modify in left; recreated in right: rename left, keep right
+addfile("file_15", "file_15 base") -- modify in left; recreated in right: user_rename left, rename right
+addfile("file_16", "file_16 base") -- recreated in left; modify in right: drop left, rename right
+addfile("file_17", "file_17 base") -- recreated in left; modify in right: rename left, drop right
+addfile("file_18", "file_18 base") -- recreated in left; modify in right: user_rename left, drop right
 commit("testbranch", "base 4")
 base_4 = base_revision()
 
-writefile("file_10", "file_10 left")
+writefile("file_12", "file_12 left")
+check(mtn("drop", "file_13"), 0, false, false)
 
-check(mtn("drop", "file_11"), 0, false, false)
+writefile("file_14", "file_14 left")
+writefile("file_15", "file_15 left")
+
+check(mtn("drop", "file_16"), 0, false, false)
+check(mtn("drop", "file_17"), 0, false, false)
+check(mtn("drop", "file_18"), 0, false, false)
 commit("testbranch", "left 4a")
 
-addfile("file_11", "file_11 left re-add")
+addfile("file_13", "file_13 left re-add")
+addfile("file_16", "file_16 left re-add")
+addfile("file_17", "file_17 left re-add")
+addfile("file_18", "file_18 left re-add")
 commit("testbranch", "left 4b")
 left_4 = base_revision()
 
 revert_to(base_4)
 
-check(mtn("drop", "file_10"), 0, false, false)
-writefile("file_11", "file_11 right")
+check(mtn("drop", "file_12"), 0, false, false)
+writefile("file_13", "file_13 right")
+check(mtn("drop", "file_14"), 0, false, false)
+check(mtn("drop", "file_15"), 0, false, false)
+writefile("file_16", "file_16 right")
+writefile("file_17", "file_17 right")
+writefile("file_18", "file_18 right")
 commit("testbranch", "right 4a")
 
-addfile("file_10", "file_10 right re-add")
+addfile("file_12", "file_12 right re-add")
+addfile("file_14", "file_14 right re-add")
+addfile("file_15", "file_15 right re-add")
 commit("testbranch", "right 4b")
 right_4 = base_revision()
 
 check(mtn("show_conflicts", left_4, right_4), 0, nil, true)
 check(samelines("stderr",
-{"mtn: [left]     9485fe891d5e23d6dc30140228cd02840ee719e9",
- "mtn: [right]    9a8192d3bf263cbd5782791e823b837d42af6902",
- "mtn: [ancestor] 209e4118bda3960b2f83e48b2368e981ab748ee5",
- "mtn: conflict: file 'file_10' from revision 209e4118bda3960b2f83e48b2368e981ab748ee5",
- "mtn: modified on the left, named file_10",
+{"mtn: [left]     " .. left_4,
+ "mtn: [right]    " .. right_4,
+ "mtn: [ancestor] " .. base_4,
+ "mtn: conflict: file 'file_12'",
+ "mtn: modified on the left, named file_12",
  "mtn: dropped and recreated on the right",
- "mtn: conflict: file 'file_11' from revision 209e4118bda3960b2f83e48b2368e981ab748ee5",
+ "mtn: conflict: file 'file_13'",
  "mtn: dropped and recreated on the left",
- "mtn: modified on the right, named file_11",
- "mtn: 2 conflicts with supported resolutions."}))
+ "mtn: modified on the right, named file_13",
+ "mtn: conflict: file 'file_14'",
+ "mtn: modified on the left, named file_14",
+ "mtn: dropped and recreated on the right",
+ "mtn: conflict: file 'file_15'",
+ "mtn: modified on the left, named file_15",
+ "mtn: dropped and recreated on the right",
+ "mtn: conflict: file 'file_16'",
+ "mtn: dropped and recreated on the left",
+ "mtn: modified on the right, named file_16",
+ "mtn: conflict: file 'file_17'",
+ "mtn: dropped and recreated on the left",
+ "mtn: modified on the right, named file_17",
+ "mtn: conflict: file 'file_18'",
+ "mtn: dropped and recreated on the left",
+ "mtn: modified on the right, named file_18",
+ "mtn: 7 conflicts with supported resolutions."}))
 
 check(mtn("conflicts", "store", left_4, right_4), 0, nil, true)
 check(samefilestd("conflicts-recreated", "_MTN/conflicts"))
 
--- drop is not a valid resolution in this case
 check(mtn("conflicts", "show_first"), 0, nil, true)
 check(samelines("stderr",
-{"mtn: conflict: file 'file_10'",
+{"mtn: conflict: file 'file_12'",
  "mtn: modified on the left",
  "mtn: dropped and recreated on the right",
  "mtn: possible resolutions:",
- "mtn: resolve_first keep",
- "mtn: resolve_first user \"name\""}))
+ "mtn: resolve_first_left drop",
+ "mtn: resolve_first_left rename",
+ "mtn: resolve_first_left user_rename \"new_content_name\" \"new_file_name\"",
+ "mtn: resolve_first_left keep",
+ "mtn: resolve_first_left user \"name\"",
+ "mtn: resolve_first_right drop",
+ "mtn: resolve_first_right rename",
+ "mtn: resolve_first_right user_rename \"new_content_name\" \"new_file_name\"",
+ "mtn: resolve_first_right keep",
+ "mtn: resolve_first_right user \"name\""}))
 
+-- need to specify both left and right resolutions
 check(mtn("conflicts", "resolve_first", "drop"), 1, nil, true)
-check(samelines("stderr", {"mtn: misuse: recreated files may not be dropped"}))
+check(samelines("stderr",
+{"mtn: misuse: must specify 'resolve_first_left' or 'resolve_first_right' (not just 'resolve_first')"}))
 
-check(mtn("conflicts", "resolve_first", "keep"), 0, nil, nil)
+check(mtn("conflicts", "resolve_first_left", "keep"), 0, nil, nil)
+check(mtn("conflicts", "resolve_first_right", "drop"), 0, nil, nil)
 
 check(mtn("conflicts", "show_first"), 0, nil, true)
-check(samelines("stderr",
-{"mtn: conflict: file 'file_11'",
- "mtn: dropped and recreated on the left",
- "mtn: modified on the right",
- "mtn: possible resolutions:",
- "mtn: resolve_first keep",
- "mtn: resolve_first user \"name\""}))
+qgrep("mtn: conflict: file 'file_13'", "stderr")
+qgrep("mtn: dropped and recreated on the left", "stderr")
+qgrep("mtn: modified on the right", "stderr")
 
 mkdir("_MTN")
 mkdir("_MTN/resolutions")
-writefile("_MTN/resolutions/file_11", "file_11 user")
-check(mtn("conflicts", "resolve_first", "user", "_MTN/resolutions/file_11"), 0, nil, nil)
+writefile("_MTN/resolutions/file_13", "file_13 user")
+check(mtn("conflicts", "resolve_first_left", "user", "_MTN/resolutions/file_13"), 0, nil, nil)
+check(mtn("conflicts", "resolve_first_right", "drop"), 0, nil, nil)
 
+check(mtn("conflicts", "resolve_first_left", "rename", "file_14_renamed"), 0, nil, nil)
+check(mtn("conflicts", "resolve_first_right", "keep"), 0, nil, nil)
+
+writefile("_MTN/resolutions/file_15_left", "file_15 user left")
+check(mtn("conflicts", "resolve_first_left", "user_rename", "_MTN/resolutions/file_15_left", "file_15_renamed_left"), 0, nil, nil)
+check(mtn("conflicts", "resolve_first_right", "rename", "file_15_renamed_right"), 0, nil, nil)
+
+check(mtn("conflicts", "resolve_first_left", "drop"), 0, nil, nil)
+check(mtn("conflicts", "resolve_first_right", "rename", "file_16_renamed"), 0, nil, nil)
+
+check(mtn("conflicts", "resolve_first_left", "rename", "file_17_renamed"), 0, nil, nil)
+check(mtn("conflicts", "resolve_first_right", "drop"), 0, nil, nil)
+
+mkdir("_MTN")
+mkdir("_MTN/resolutions")
+writefile("_MTN/resolutions/file_18", "file_18 user")
+check(mtn("conflicts", "resolve_first_left", "user_rename", "_MTN/resolutions/file_18", "file_18_renamed"), 0, nil, nil)
+check(mtn("conflicts", "resolve_first_right", "drop"), 0, nil, nil)
+
 check(samefilestd("conflicts-recreated-resolved", "_MTN/conflicts"))
 
 check(mtn("explicit_merge", "--resolve-conflicts", left_4, right_4, "testbranch"), 0, nil, true)
 check(samelines("stderr",
-{"mtn: [left]  9485fe891d5e23d6dc30140228cd02840ee719e9",
- "mtn: [right] 9a8192d3bf263cbd5782791e823b837d42af6902",
- "mtn: keeping 'file_10' from left",
- "mtn: history for 'file_10' will be lost; see user manual Merge Conflicts section",
- "mtn: replacing content of 'file_11' with '_MTN/resolutions/file_11'",
- "mtn: history for 'file_11' will be lost; see user manual Merge Conflicts section",
- "mtn: [merged] 306eb31064512a8a2f4d316ff7a7ec32a1f64f4c"}))
+{"mtn: [left]  " .. left_4,
+ "mtn: [right] " .. right_4,
+ "mtn: keeping 'file_12' from left",
+ "mtn: history for 'file_12' from left will be lost; see user manual Merge Conflicts section",
+ "mtn: dropping 'file_12' from right",
+ "mtn: replacing content of 'file_13' from left with '_MTN/resolutions/file_13'",
+ "mtn: dropping 'file_13' from right",
+ "mtn: renaming 'file_14' from left to 'file_14_renamed'",
+ "mtn: history for 'file_14' from left will be lost; see user manual Merge Conflicts section",
+ "mtn: keeping 'file_14' from right",
+ "mtn: replacing content of 'file_15' from left with '_MTN/resolutions/file_15_left'",
+ "mtn: history for 'file_15' from left will be lost; see user manual Merge Conflicts section",
+ "mtn: renaming 'file_15' from left to 'file_15_renamed_left'",
+ "mtn: renaming 'file_15' from right to 'file_15_renamed_right'",
+ "mtn: dropping 'file_16' from left",
+ "mtn: renaming 'file_16' from right to 'file_16_renamed'",
+ "mtn: history for 'file_16' from right will be lost; see user manual Merge Conflicts section",
+ "mtn: renaming 'file_17' from left to 'file_17_renamed'",
+ "mtn: dropping 'file_17' from right",
+ "mtn: replacing content of 'file_18' from left with '_MTN/resolutions/file_18'",
+ "mtn: renaming 'file_18' from left to 'file_18_renamed'",
+ "mtn: dropping 'file_18' from right",
+ "mtn: [merged] c6d6dba528110b6aa32572f6939982a1d56b17e0"}))
 
 check(mtn("update"), 0, nil, true)
-check(samelines("file_10", {"file_10 left"}))
+check(samelines("file_12", {"file_12 left"}))
+check(samelines("file_13", {"file_13 user"}))
+check(samelines("file_14_renamed", {"file_14 left"}))
+check(samelines("file_14", {"file_14 right re-add"}))
+check(samelines("file_15_renamed_left", {"file_15 user left"}))
+check(samelines("file_15_renamed_right", {"file_15 right re-add"}))
+check(samelines("file_16_renamed", {"file_16 right"}))
+check(samelines("file_17_renamed", {"file_17 left re-add"}))
+check(samelines("file_18_renamed", {"file_18 user"}))
 
 -- end of file
============================================================
--- test/func/resolve_conflicts_dropped_modified/conflicts-recreated	26f633aeeee82440eea41f8e1747f2c4262ce9bb
+++ test/func/resolve_conflicts_dropped_modified_1/conflicts-recreated	3f43d9d13d0149e3be0fa3a7a8695182dbc871ea
@@ -1,23 +1,73 @@
-    left [9485fe891d5e23d6dc30140228cd02840ee719e9]
-   right [9a8192d3bf263cbd5782791e823b837d42af6902]
-ancestor [209e4118bda3960b2f83e48b2368e981ab748ee5]
+    left [3721cc38b4f98b571d92bd38b2e269f438ed0af8]
+   right [ad9ca7cb1f77fe829269d4ac18763bfb9683a4b0]
+ancestor [a2b0bdd41b1b94e6e580c81c9901af128d299a7f]
 
         conflict dropped_modified
-   ancestor_name "file_10"
-ancestor_file_id [7368a4340573dca149c05db6f49638fafee766d0]
+   ancestor_name "file_12"
+ancestor_file_id [ec21b6df4e9613a1de985ab44f073a78b1f0b0c1]
        left_type "modified file"
-       left_name "file_10"
-    left_file_id [080c590e6e671b1b9ca0e752e1bc468c5167e2a9]
+       left_name "file_12"
+    left_file_id [831efe7eb30ab2dfecc3b3d9e5b68f4d8d8978ca]
       right_type "recreated file"
-      right_name "file_10"
-   right_file_id [59db7ed2afabb782b5a0215d825a86271eb96b8d]
+      right_name "file_12"
+   right_file_id [33f2071587733c13912824370e1ae0e3aa2a296c]
 
         conflict dropped_modified
-   ancestor_name "file_11"
-ancestor_file_id [498b49fddbd0418f62eb19d2096de816f3e34116]
+   ancestor_name "file_13"
+ancestor_file_id [833044cd7f5458fe436ddb0058dd9cde0e715715]
        left_type "recreated file"
-       left_name "file_11"
-    left_file_id [bbf158a696465c2feb9ea22fac35ff7088f07ba0]
+       left_name "file_13"
+    left_file_id [c43d02936229a95d852d699ffb14ee8f02ad5311]
       right_type "modified file"
-      right_name "file_11"
-   right_file_id [935f9a7af1da88e7fe541690076c48bac2108052]
+      right_name "file_13"
+   right_file_id [6aaa91015c6fc3b0dc8eeccf2aa22c52fe8dba2a]
+
+        conflict dropped_modified
+   ancestor_name "file_14"
+ancestor_file_id [0e462cd7a936807265ce7c01ce040b79aa2fa4d1]
+       left_type "modified file"
+       left_name "file_14"
+    left_file_id [442ddb4cae7d5cde84983dd60597c966e87645ea]
+      right_type "recreated file"
+      right_name "file_14"
+   right_file_id [963b47ce0f243cbd38b1ae288e6c6d559006ba6b]
+
+        conflict dropped_modified
+   ancestor_name "file_15"
+ancestor_file_id [13afe431eae6774a3b14ce8a2a6c2583ba375383]
+       left_type "modified file"
+       left_name "file_15"
+    left_file_id [8c1962f213226e85231b7b91b1a368f8e2ad8bf4]
+      right_type "recreated file"
+      right_name "file_15"
+   right_file_id [4d28d9df37261e18c1c23108c2ab531b8e535193]
+
+        conflict dropped_modified
+   ancestor_name "file_16"
+ancestor_file_id [b7de9cb0feb57657abcc6a991fdc7d034bfe2e66]
+       left_type "recreated file"
+       left_name "file_16"
+    left_file_id [7a8bca4a66301d480279b187927dcacba4da9f39]
+      right_type "modified file"
+      right_name "file_16"
+   right_file_id [e9d80dfb12b60107455fdf42fcc6beecb33fa7bd]
+
+        conflict dropped_modified
+   ancestor_name "file_17"
+ancestor_file_id [36158b32ea4ca5cbeba77374c9f407adeee91930]
+       left_type "recreated file"
+       left_name "file_17"
+    left_file_id [b5143f8996e5383113d406f263896a75f211e208]
+      right_type "modified file"
+      right_name "file_17"
+   right_file_id [5b5069994101a5bd4f35c9a9eaaf78abdf8e52ee]
+
+        conflict dropped_modified
+   ancestor_name "file_18"
+ancestor_file_id [4d70c0b068124556ad0e2d4f17b2d5994eaf01f5]
+       left_type "recreated file"
+       left_name "file_18"
+    left_file_id [3c2034c53b1882fd16fd943c80a93074932fc220]
+      right_type "modified file"
+      right_name "file_18"
+   right_file_id [c7ef23c4c1ad7b8c977a1caeb9a5830fd1fa1ae7]
============================================================
--- test/func/resolve_conflicts_dropped_modified/conflicts-recreated-resolved	5082532e55e68bc93610d1e8936fe790bb048d3d
+++ test/func/resolve_conflicts_dropped_modified_1/conflicts-recreated-resolved	7937832db9408aeadbbeed472d86f29ef3394a06
@@ -1,25 +1,89 @@
-    left [9485fe891d5e23d6dc30140228cd02840ee719e9]
-   right [9a8192d3bf263cbd5782791e823b837d42af6902]
-ancestor [209e4118bda3960b2f83e48b2368e981ab748ee5]
+    left [3721cc38b4f98b571d92bd38b2e269f438ed0af8]
+   right [ad9ca7cb1f77fe829269d4ac18763bfb9683a4b0]
+ancestor [a2b0bdd41b1b94e6e580c81c9901af128d299a7f]
 
-          conflict dropped_modified
-     ancestor_name "file_10"
-  ancestor_file_id [7368a4340573dca149c05db6f49638fafee766d0]
-         left_type "modified file"
-         left_name "file_10"
-      left_file_id [080c590e6e671b1b9ca0e752e1bc468c5167e2a9]
-        right_type "recreated file"
-        right_name "file_10"
-     right_file_id [59db7ed2afabb782b5a0215d825a86271eb96b8d]
-resolved_keep_left 
+           conflict dropped_modified
+      ancestor_name "file_12"
+   ancestor_file_id [ec21b6df4e9613a1de985ab44f073a78b1f0b0c1]
+          left_type "modified file"
+          left_name "file_12"
+       left_file_id [831efe7eb30ab2dfecc3b3d9e5b68f4d8d8978ca]
+         right_type "recreated file"
+         right_name "file_12"
+      right_file_id [33f2071587733c13912824370e1ae0e3aa2a296c]
+ resolved_keep_left 
+resolved_drop_right 
 
-          conflict dropped_modified
-     ancestor_name "file_11"
-  ancestor_file_id [498b49fddbd0418f62eb19d2096de816f3e34116]
-         left_type "recreated file"
-         left_name "file_11"
-      left_file_id [bbf158a696465c2feb9ea22fac35ff7088f07ba0]
-        right_type "modified file"
-        right_name "file_11"
-     right_file_id [935f9a7af1da88e7fe541690076c48bac2108052]
-resolved_user_left "_MTN/resolutions/file_11"
+           conflict dropped_modified
+      ancestor_name "file_13"
+   ancestor_file_id [833044cd7f5458fe436ddb0058dd9cde0e715715]
+          left_type "recreated file"
+          left_name "file_13"
+       left_file_id [c43d02936229a95d852d699ffb14ee8f02ad5311]
+         right_type "modified file"
+         right_name "file_13"
+      right_file_id [6aaa91015c6fc3b0dc8eeccf2aa22c52fe8dba2a]
+ resolved_user_left "_MTN/resolutions/file_13"
+resolved_drop_right 
+
+            conflict dropped_modified
+       ancestor_name "file_14"
+    ancestor_file_id [0e462cd7a936807265ce7c01ce040b79aa2fa4d1]
+           left_type "modified file"
+           left_name "file_14"
+        left_file_id [442ddb4cae7d5cde84983dd60597c966e87645ea]
+          right_type "recreated file"
+          right_name "file_14"
+       right_file_id [963b47ce0f243cbd38b1ae288e6c6d559006ba6b]
+resolved_rename_left "file_14_renamed"
+ resolved_keep_right 
+
+             conflict dropped_modified
+        ancestor_name "file_15"
+     ancestor_file_id [13afe431eae6774a3b14ce8a2a6c2583ba375383]
+            left_type "modified file"
+            left_name "file_15"
+         left_file_id [8c1962f213226e85231b7b91b1a368f8e2ad8bf4]
+           right_type "recreated file"
+           right_name "file_15"
+        right_file_id [4d28d9df37261e18c1c23108c2ab531b8e535193]
+   resolved_user_left "_MTN/resolutions/file_15_left"
+ resolved_rename_left "file_15_renamed_left"
+resolved_rename_right "file_15_renamed_right"
+
+             conflict dropped_modified
+        ancestor_name "file_16"
+     ancestor_file_id [b7de9cb0feb57657abcc6a991fdc7d034bfe2e66]
+            left_type "recreated file"
+            left_name "file_16"
+         left_file_id [7a8bca4a66301d480279b187927dcacba4da9f39]
+           right_type "modified file"
+           right_name "file_16"
+        right_file_id [e9d80dfb12b60107455fdf42fcc6beecb33fa7bd]
+   resolved_drop_left 
+resolved_rename_right "file_16_renamed"
+
+            conflict dropped_modified
+       ancestor_name "file_17"
+    ancestor_file_id [36158b32ea4ca5cbeba77374c9f407adeee91930]
+           left_type "recreated file"
+           left_name "file_17"
+        left_file_id [b5143f8996e5383113d406f263896a75f211e208]
+          right_type "modified file"
+          right_name "file_17"
+       right_file_id [5b5069994101a5bd4f35c9a9eaaf78abdf8e52ee]
+resolved_rename_left "file_17_renamed"
+ resolved_drop_right 
+
+            conflict dropped_modified
+       ancestor_name "file_18"
+    ancestor_file_id [4d70c0b068124556ad0e2d4f17b2d5994eaf01f5]
+           left_type "recreated file"
+           left_name "file_18"
+        left_file_id [3c2034c53b1882fd16fd943c80a93074932fc220]
+          right_type "modified file"
+          right_name "file_18"
+       right_file_id [c7ef23c4c1ad7b8c977a1caeb9a5830fd1fa1ae7]
+  resolved_user_left "_MTN/resolutions/file_18"
+resolved_rename_left "file_18_renamed"
+ resolved_drop_right 
============================================================
--- test/func/resolve_conflicts_dropped_modified/conflicts-resolved	dd892da237ef4f3a0ee30fa3989374a65d092d68
+++ test/func/resolve_conflicts_dropped_modified_1/conflicts-resolved	b2fe24f2d19a8d539c0c89c305a7b58a36ead63e
@@ -14,17 +14,17 @@ resolved_drop_left 
      right_file_id [4fd0fa24812427ee6c13a839d2a90bc0c6fc0091]
 resolved_drop_left 
 
-          conflict dropped_modified
-     ancestor_name "file_3"
-  ancestor_file_id [311aac8e6f1fb6fca84da5153aa6d5a1c6faff79]
-         left_type "dropped file"
-          left_rev [c2fe3623ce72d248154425dc7db2ddcc397c9aca]
-         left_name "file_3"
-      left_file_id [311aac8e6f1fb6fca84da5153aa6d5a1c6faff79]
-        right_type "modified file"
-        right_name "file_3_renamed"
-     right_file_id [da7ea65160c9c92f4ed120568229342fe7daa924]
-resolved_drop_left 
+           conflict dropped_modified
+      ancestor_name "file_3"
+   ancestor_file_id [311aac8e6f1fb6fca84da5153aa6d5a1c6faff79]
+          left_type "dropped file"
+           left_rev [c2fe3623ce72d248154425dc7db2ddcc397c9aca]
+          left_name "file_3"
+       left_file_id [311aac8e6f1fb6fca84da5153aa6d5a1c6faff79]
+         right_type "modified file"
+         right_name "file_3_renamed"
+      right_file_id [da7ea65160c9c92f4ed120568229342fe7daa924]
+resolved_drop_right 
 
           conflict dropped_modified
      ancestor_name "file_4"
@@ -38,17 +38,17 @@ resolved_keep_left 
      right_file_id [259dbd8291bd18ba3fdb9adb3776eb26f94b1230]
 resolved_keep_left 
 
-          conflict dropped_modified
-     ancestor_name "file_5"
-  ancestor_file_id [d141bda733292622ebce4c231cbb0da44ac59f40]
-         left_type "dropped file"
-          left_rev [b0d6953684d49dd6bd345c312d6a0c8fed3078ce]
-         left_name "file_5_renamed"
-      left_file_id [420cde699a422f7c3d2c8951c46ddfd546db66c0]
-        right_type "modified file"
-        right_name "file_5"
-     right_file_id [e7eb31ab48c2e42126f44ef78ffdb27f388333b0]
-resolved_keep_left 
+           conflict dropped_modified
+      ancestor_name "file_5"
+   ancestor_file_id [d141bda733292622ebce4c231cbb0da44ac59f40]
+          left_type "dropped file"
+           left_rev [b0d6953684d49dd6bd345c312d6a0c8fed3078ce]
+          left_name "file_5_renamed"
+       left_file_id [420cde699a422f7c3d2c8951c46ddfd546db66c0]
+         right_type "modified file"
+         right_name "file_5"
+      right_file_id [e7eb31ab48c2e42126f44ef78ffdb27f388333b0]
+resolved_keep_right 
 
           conflict dropped_modified
      ancestor_name "file_6"
@@ -62,14 +62,14 @@ resolved_user_left "_MTN/resolutions/fil
      right_file_id [d5531643d3b5aee3e10eceabbdfecf167148a2d9]
 resolved_user_left "_MTN/resolutions/file_6_resolved"
 
-          conflict dropped_modified
-     ancestor_name "file_7"
-  ancestor_file_id [1a9d3059360fd5f04d0cec05875c8e376da0eaef]
-         left_type "dropped file"
-          left_rev [c2fe3623ce72d248154425dc7db2ddcc397c9aca]
-         left_name "file_7"
-      left_file_id [1a9d3059360fd5f04d0cec05875c8e376da0eaef]
-        right_type "modified file"
-        right_name "file_7_renamed"
-     right_file_id [9b362e2754ea1f943497d5a31de3899271ee5a8b]
-resolved_user_left "_MTN/resolutions/file_7_resolved"
+           conflict dropped_modified
+      ancestor_name "file_7"
+   ancestor_file_id [1a9d3059360fd5f04d0cec05875c8e376da0eaef]
+          left_type "dropped file"
+           left_rev [c2fe3623ce72d248154425dc7db2ddcc397c9aca]
+          left_name "file_7"
+       left_file_id [1a9d3059360fd5f04d0cec05875c8e376da0eaef]
+         right_type "modified file"
+         right_name "file_7_renamed"
+      right_file_id [9b362e2754ea1f943497d5a31de3899271ee5a8b]
+resolved_user_right "_MTN/resolutions/file_7_resolved"
============================================================
--- test/func/resolve_conflicts_dropped_modified/show_conflicts	74f0f311dee5ce970396bed354b6ab0fd97077f0
+++ test/func/resolve_conflicts_dropped_modified_1/show_conflicts	cce385ec522c8e4885cac7d3375896aaa507f0e0
@@ -1,22 +1,22 @@ mtn: [ancestor] c2fe3623ce72d248154425dc
 mtn: [left]     7b2ef4343b0717bcd122498a1a0b7ff7acffb64c
 mtn: [right]    ca7922b510f9daf5c4b28c6788315ee82eb9a7f0
 mtn: [ancestor] c2fe3623ce72d248154425dc7db2ddcc397c9aca
-mtn: conflict: file 'file_2' from revision c2fe3623ce72d248154425dc7db2ddcc397c9aca
+mtn: conflict: file 'file_2'
 mtn: modified on the left, named file_2_renamed
 mtn: dropped on the right
-mtn: conflict: file 'file_3' from revision c2fe3623ce72d248154425dc7db2ddcc397c9aca
+mtn: conflict: file 'file_3'
 mtn: dropped on the left
 mtn: modified on the right, named file_3_renamed
-mtn: conflict: file 'file_4' from revision c2fe3623ce72d248154425dc7db2ddcc397c9aca
+mtn: conflict: file 'file_4'
 mtn: modified on the left, named file_4
 mtn: dropped on the right
-mtn: conflict: file 'file_5' from revision c2fe3623ce72d248154425dc7db2ddcc397c9aca
+mtn: conflict: file 'file_5'
 mtn: dropped on the left
 mtn: modified on the right, named file_5
-mtn: conflict: file 'file_6' from revision c2fe3623ce72d248154425dc7db2ddcc397c9aca
+mtn: conflict: file 'file_6'
 mtn: modified on the left, named file_6_renamed
 mtn: dropped on the right
-mtn: conflict: file 'file_7' from revision c2fe3623ce72d248154425dc7db2ddcc397c9aca
+mtn: conflict: file 'file_7'
 mtn: dropped on the left
 mtn: modified on the right, named file_7_renamed
 mtn: 6 conflicts with supported resolutions.
============================================================
--- test/func/resolve_conflicts_dropped_modified/show_conflicts-orphaned	fbe2c5cc2c59c6fec1121e3be9469d370b9ed5cb
+++ test/func/resolve_conflicts_dropped_modified_1/show_conflicts-orphaned	e343653ac3989164acb2dbb54f3f97b035d28de6
@@ -1,13 +1,13 @@ mtn: [ancestor] 44c4d408ecf65b6b45fa2c6f
 mtn: [left]     4228fbd8003cdd89e7eea51fcef10c3f91d78f69
 mtn: [right]    6cb6438a490a1ad4c69ff6cac23c75a903cd9cfd
 mtn: [ancestor] 44c4d408ecf65b6b45fa2c6fa2a51e5b7485d8e1
-mtn: conflict: file 'dir2/file_10' from revision 44c4d408ecf65b6b45fa2c6fa2a51e5b7485d8e1
+mtn: conflict: file 'dir2/file_10'
 mtn: modified on the left, named dir2/file_10
 mtn: orphaned on the right
-mtn: conflict: file 'dir2/file_11' from revision 44c4d408ecf65b6b45fa2c6fa2a51e5b7485d8e1
+mtn: conflict: file 'dir2/file_11'
 mtn: modified on the left, named dir2/file_11
 mtn: orphaned on the right
-mtn: conflict: file 'dir2/file_9' from revision 44c4d408ecf65b6b45fa2c6fa2a51e5b7485d8e1
+mtn: conflict: file 'dir2/file_9'
 mtn: modified on the left, named dir2/file_9
 mtn: orphaned on the right
 mtn: 3 conflicts with supported resolutions.
============================================================
--- test/func/resolve_conflicts_dropped_modified_2/__driver__.lua	e1bd2fed5032ea4cb911ffc1289b1830e2195e1c
+++ test/func/resolve_conflicts_dropped_modified_2/__driver__.lua	81b11d9ab24ad470156b95e8be8efd9dc1645156
@@ -34,7 +34,7 @@ check(samelines("stderr",
  {"mtn: [left]     506d8ed51b06c0080e8bb307155a88637045b532",
   "mtn: [right]    a2889488ed1801a904d0219ec9939dfc2e9be033",
   "mtn: [ancestor] f80ff103551d0313647d6c84990bc9db6b158dac",
-  "mtn: conflict: file 'file_2' from revision f80ff103551d0313647d6c84990bc9db6b158dac",
+  "mtn: conflict: file 'file_2'",
   "mtn: modified on the left, named file_2",
   "mtn: dropped on the right",
   "mtn: 1 conflict with supported resolutions."}))
@@ -47,8 +47,8 @@ check(samelines("stderr",
 check(samelines("stderr",
  {"mtn: [left]  506d8ed51b06c0080e8bb307155a88637045b532",
   "mtn: [right] a2889488ed1801a904d0219ec9939dfc2e9be033",
-  "mtn: keeping 'file_2'",
-  "mtn: history for 'file_2' will be lost; see user manual Merge Conflicts section",
+  "mtn: keeping 'file_2' from left",
+  "mtn: history for 'file_2' from left will be lost; see user manual Merge Conflicts section",
   "mtn: [merged] 3df3126220588440def7b08f488ca35eaa94f1b6"}))
 
 check(mtn("update"), 0, nil, true)
@@ -69,17 +69,19 @@ check(samelines("stderr",
  {"mtn: [left]     5a144a43f03692e389f3ddd4c510a4d9754061d5",
   "mtn: [right]    3df3126220588440def7b08f488ca35eaa94f1b6",
   "mtn: [ancestor] 506d8ed51b06c0080e8bb307155a88637045b532",
-  "mtn: conflict: file 'file_2' from revision 506d8ed51b06c0080e8bb307155a88637045b532",
+  "mtn: conflict: file 'file_2'",
   "mtn: modified on the left, named file_2",
   "mtn: dropped and recreated on the right",
   "mtn: 1 conflict with supported resolutions."}))
 
 check(mtn("conflicts", "store", left_2, right_2), 0, nil, true)
 
-check(mtn("conflicts", "resolve_first", "keep"), 0, nil, true)
+check(mtn("conflicts", "resolve_first_left", "keep"), 0, nil, true)
+check(mtn("conflicts", "resolve_first_right", "drop"), 0, nil, true)
 
 check(mtn("explicit_merge", "--resolve-conflicts", left_2, right_2, "testbranch"), 0, nil, true)
 check(qgrep("mtn: keeping 'file_2' from left", "stderr"))
+check(qgrep("mtn: dropping 'file_2' from right", "stderr"))
 
 check(mtn("update"), 0, nil, true)
 check(samelines("file_2", {"file_2 left 2"}))
============================================================
--- /dev/null	
+++ test/func/resolve_conflicts_dropped_modified_3/__driver__.lua	fd2a4dd0c38dff10bd3ff2f20e983f3f548b3d18
@@ -0,0 +1,276 @@
+-- Test enforcement of consistent resolutions for drop/modified conflicts.
+--
+-- Only invalid combinations are tested here (we verify a good error
+-- message); valid combinations are tested in
+-- resolve_conflicts_dropped_modified_1.
+--
+-- The left and right conflicts chosen by the user must be consistent;
+-- they must give different names for the two sides. 
+--
+-- When one file is in the dropped state, only one resolution can be
+-- specified; that of the modified file.
+--
+-- Rename on both sides is valid, unless the user specifies the same
+-- new name for both; that is tested only once here.
+--
+-- The only inconsistent cases are between modified and recreated
+-- files. A recreated file is detected by having the same name as the
+-- modified file; if the modified file has also been renamed, the
+-- recreated file must have the same name as the renamed file. Thus we
+-- do not need to consider a renamed modified file as a separate case.
+--
+-- Orphaned file resolution cannot be keep or user; those error
+-- messages are tested in resolve_conflicts_dropped_modified_1.
+--
+-- We need to test all invalid combinations of left/right resolution:
+--
+--      left                    right
+-- state    resolution      state       resolution  file    case
+-----------------------------------------------------------
+-- dropped  -               dropped     -           (not a conflict)
+--          -               modified    keep        (valid)
+--          -               modified    rename      (valid)
+--          -               modified    user        (valid)
+--          -               modified    user_rename (valid)
+--          -               recreated   -           (not a conflict)
+--
+-- modified drop            dropped     -           (valid)
+--          keep            dropped     -           (valid)
+--          rename          dropped     -           (valid)
+--          user            dropped     -           (valid)
+--          user_rename     dropped     -           (valid)
+--
+-- modified -               modified    -           (file content conflict)
+--          drop            recreated   drop        (valid)
+--          drop            recreated   keep        (valid)
+--          drop            recreated   rename      (valid)
+--          drop            recreated   user        (valid)
+--          drop            recreated   user_rename (valid)
+--          keep            recreated   drop        (valid)
+--          keep            recreated   keep        file_2  1
+--          keep            recreated   rename      (valid)
+--          keep            recreated   user        file_2  2
+--          keep            recreated   user_rename (valid)
+--          rename          recreated   drop        (valid)
+--          rename          recreated   keep        (valid)
+--          rename          recreated   rename      (valid)
+--          rename          recreated   user        (valid)
+--          rename          recreated   user_rename (valid)
+--          user            recreated   drop        (valid)
+--          user            recreated   keep        file_4  3
+--          user            recreated   rename      (valid) 
+--          user            recreated   user        file_4  4
+--          user            recreated   user_rename (valid)
+--          user_rename     recreated   drop        (valid)
+--          user_rename     recreated   keep        (valid)
+--          user_rename     recreated   rename      (valid)
+--          user_rename     recreated   user        (valid)
+--          user_rename     recreated   user_rename (valid)
+-- 
+-- recreated drop           dropped     -           (valid)
+--           keep           dropped     -           (valid)
+--           rename         dropped     -           (valid)
+--           user           dropped     -           (valid)
+--           user_rename    dropped     -           (valid)
+--           drop           modified    drop        (valid)
+--           drop           modified    keep        (valid)
+--           drop           modified    rename      (valid)
+--           drop           modified    user        (valid)
+--           drop           modified    user_rename (valid)
+--           keep           modified    drop        (valid)
+--           keep           modified    keep        file_3  5
+--           keep           modified    rename      (valid)
+--           keep           modified    user        file_3  6
+--           keep           modified    user_rename (valid)
+--           rename         modified    drop        (valid)
+--           rename         modified    keep        (valid)
+--           rename         modified    rename      (valid)
+--           rename         modified    user        (valid)
+--           rename         modified    user_rename (valid)
+--           user           modified    drop        (valid)
+--           user           modified    keep        file_3  7
+--           user           modified    rename      (valid)
+--           user           modified    user        file_3  8 
+--           user           modified    user_rename (valid)
+--           user_rename    modified    drop        (valid)
+--           user_rename    modified    keep        (valid)
+--           user_rename    modified    rename      (valid)
+--           user_rename    modified    user        (valid)
+--           user_rename    modified    user_rename (valid)
+
+mtn_setup()
+
+-- Create the test files
+
+addfile("file_2", "file_2 base") -- modified left, recreated right
+addfile("file_3", "file_3 base") -- recreated left, modified right
+commit("testbranch", "base")
+base = base_revision()
+
+writefile("file_2", "file_2 left")
+check(mtn("drop", "file_3"), 0, false, false)
+commit("testbranch", "left 1a")
+
+addfile("file_3", "file_3 left recreated")
+commit("testbranch", "left 1b")
+left_1 = base_revision()
+
+revert_to(base)
+
+check(mtn("drop", "file_2"), 0, false, false)
+writefile("file_3", "file_3 right")
+commit("testbranch", "right 1a")
+
+addfile("file_2", "file_2 right recreated")
+commit("testbranch", "right 1b")
+right_1 = base_revision()
+
+-- Store and show inconsistency error messages
+check(mtn("conflicts", "store", left_1, right_1), 0, nil, true)
+check(samelines("stderr",
+{"mtn: 2 conflicts with supported resolutions.",
+ "mtn: stored in '_MTN/conflicts'"}))
+
+check(mtn("conflicts", "show_first"), 0, nil, true)
+check(samelines("stderr",
+{"mtn: conflict: file 'file_2'",
+ "mtn: modified on the left",
+ "mtn: dropped and recreated on the right",
+ "mtn: possible resolutions:",
+ "mtn: resolve_first_left drop",
+ "mtn: resolve_first_left rename",
+ "mtn: resolve_first_left user_rename \"new_content_name\" \"new_file_name\"",
+ "mtn: resolve_first_left keep",
+ "mtn: resolve_first_left user \"name\"",
+ "mtn: resolve_first_right drop",
+ "mtn: resolve_first_right rename",
+ "mtn: resolve_first_right user_rename \"new_content_name\" \"new_file_name\"",
+ "mtn: resolve_first_right keep",
+ "mtn: resolve_first_right user \"name\""}))
+
+-- case 1, 2; keep *
+check(mtn("conflicts", "resolve_first_left", "keep"), 0, nil, false)
+
+-- check that inconsistent resolutions for right are not displayed
+check(mtn("conflicts", "show_first"), 0, nil, true)
+check(samelines("stderr",
+{"mtn: conflict: file 'file_2'",
+ "mtn: modified on the left",
+ "mtn: dropped and recreated on the right",
+ "mtn: left_resolution: keep",
+ "mtn: possible resolutions:",
+ "mtn: resolve_first_right drop",
+ "mtn: resolve_first_right rename",
+ "mtn: resolve_first_right user_rename \"new_content_name\" \"new_file_name\""}))
+
+-- check for errors from inconsistent resolutions
+
+-- case 1: keep, keep
+check(mtn("conflicts", "resolve_first_right", "keep"), 1, nil, true)
+check(samelines("stderr",
+{"mtn: misuse: other resolution is keep; specify 'drop', 'rename', or 'user_rename'"}))
+
+-- case 2: keep, user
+check(mtn("conflicts", "resolve_first_right", "user", "_MTN/resolutions/file_2"), 1, nil, true)
+check(samelines("stderr",
+{"mtn: misuse: other resolution is keep; specify 'drop', 'rename', or 'user_rename'"}))
+
+-- case 1, 2, but specify right resolution first
+check(mtn("conflicts", "store", left_1, right_1), 0, nil, true)
+check(mtn("conflicts", "resolve_first_right", "keep"), 0, nil, false)
+check(mtn("conflicts", "show_first"), 0, nil, true)
+check(samelines("stderr",
+{"mtn: conflict: file 'file_2'",
+ "mtn: modified on the left",
+ "mtn: dropped and recreated on the right",
+ "mtn: right_resolution: keep",
+ "mtn: possible resolutions:",
+ "mtn: resolve_first_left drop",
+ "mtn: resolve_first_left rename",
+ "mtn: resolve_first_left user_rename \"new_content_name\" \"new_file_name\""}))
+check(mtn("conflicts", "resolve_first_left", "keep"), 1, nil, true)
+check(samelines("stderr",
+{"mtn: misuse: other resolution is keep; specify 'drop', 'rename', or 'user_rename'"}))
+
+-- No error if specify right again, but it actually sets file_3 right resolution. so we have to reset
+check(mtn("conflicts", "store", left_1, right_1), 0, nil, true)
+check(mtn("conflicts", "resolve_first_right", "user", "_MTN/resolutions/file_2"), 0, nil, false)
+check(mtn("conflicts", "show_first"), 0, nil, true)
+check(qgrep("right_resolution: content_user, content: '_MTN/resolutions/file_2'", "stderr"))
+
+check(mtn("conflicts", "resolve_first_left", "keep"), 1, nil, true)
+check(samelines("stderr",
+{"mtn: misuse: other resolution is content_user; specify 'drop', 'rename', or 'user_rename'"}))
+
+-- provide a valid resolution for file_2 so file_3 is first
+check(mtn("conflicts", "resolve_first_left", "drop"), 0, nil, nil)
+
+-- case 3, 4; user *
+check(mtn("conflicts", "resolve_first_left", "user", "_MTN/resolutions/file_3"), 0, nil, false)
+
+check(mtn("conflicts", "show_first"), 0, nil, true)
+check(samelines("stderr",
+{"mtn: conflict: file 'file_3'",
+ "mtn: dropped and recreated on the left",
+ "mtn: modified on the right",
+ "mtn: left_resolution: content_user, content: '_MTN/resolutions/file_3'",
+ "mtn: possible resolutions:",
+ "mtn: resolve_first_right drop",
+ "mtn: resolve_first_right rename",
+ "mtn: resolve_first_right user_rename \"new_content_name\" \"new_file_name\""}))
+
+-- case 3: user, keep
+check(mtn("conflicts", "resolve_first_right", "keep"), 1, nil, true)
+check(samelines("stderr",
+{"mtn: misuse: other resolution is content_user; specify 'drop', 'rename', or 'user_rename'"}))
+
+-- case 4: user, user
+check(mtn("conflicts", "resolve_first_right", "user", "_MTN/resolutions/file_3"), 1, nil, true)
+check(samelines("stderr",
+{"mtn: misuse: other resolution is content_user; specify 'drop', 'rename', or 'user_rename'"}))
+
+-- specify right first
+check(mtn("conflicts", "store", left_1, right_1), 0, nil, true)
+-- resolve file_2
+check(mtn("conflicts", "resolve_first_left", "keep"), 0, nil, nil) 
+check(mtn("conflicts", "resolve_first_right", "drop"), 0, nil, nil)
+
+-- file_3
+check(mtn("conflicts", "resolve_first_right", "keep"), 0, nil, true)
+
+check(mtn("conflicts", "show_first"), 0, nil, true)
+check(samelines("stderr",
+{"mtn: conflict: file 'file_3'",
+ "mtn: dropped and recreated on the left",
+ "mtn: modified on the right",
+ "mtn: right_resolution: keep",
+ "mtn: possible resolutions:",
+ "mtn: resolve_first_left drop",
+ "mtn: resolve_first_left rename",
+ "mtn: resolve_first_left user_rename \"new_content_name\" \"new_file_name\""}))
+check(mtn("conflicts", "resolve_first_left", "user", "_MTN/resolutions/file_3"), 1, nil, true)
+check(samelines("stderr",
+{"mtn: misuse: other resolution is keep; specify 'drop', 'rename', or 'user_rename'"}))
+
+-- reset for case 4 reversed
+check(mtn("conflicts", "store", left_1, right_1), 0, nil, true)
+-- resolve file_2
+check(mtn("conflicts", "resolve_first_left", "keep"), 0, nil, nil) 
+check(mtn("conflicts", "resolve_first_right", "drop"), 0, nil, nil)
+
+check(mtn("conflicts", "resolve_first_right", "user", "_MTN/resolutions/file_3"), 0, nil, nil)
+check(mtn("conflicts", "resolve_first_left", "user", "_MTN/resolutions/file_3"), 1, nil, true)
+check(samelines("stderr",
+{"mtn: misuse: other resolution is content_user; specify 'drop', 'rename', or 'user_rename'"}))
+
+-- Test error from user rename both sides to same new name. The error is at merge time.
+check(mtn("conflicts", "store", left_1, right_1), 0, nil, true)
+check(mtn("conflicts", "resolve_first_left", "rename", "file_2_renamed"), 0, nil, nil) 
+check(mtn("conflicts", "resolve_first_right", "rename", "file_2_renamed"), 0, nil, nil) 
+-- file_3
+check(mtn("conflicts", "resolve_first_left", "drop"), 0, nil, nil)
+check(mtn("conflicts", "resolve_first_right", "drop"), 0, nil, nil)
+check(mtn("explicit_merge", "--resolve-conflicts", left_1, right_1, "testbranch"), 1, nil, true)
+check(qgrep("'file_2_renamed' already exists", "stderr"))
+
+-- end of file
============================================================
--- /dev/null	
+++ test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2/__driver__.lua	80f056dd3773dab5d5019d78cbb0f7393d66bde3
@@ -0,0 +1,170 @@
+-- Show a problematic use case involving a dropped_modified conflict,
+-- and how it can be resolved with the 'mtn:resolve_conflict'
+-- attribute.
+--
+-- There is an upstream branch, and a local branch. The local branch
+-- adds a file that the upstream branch adopts. The next merge from
+-- upstream to local encounters a duplicate_name conflict; if we
+-- resolve that the wrong way (keep local instead of upstream), on the
+-- next merge we get a dropped_modified conflict.
+--
+-- In the meantime, we've edited the file locally, illustrating that
+-- the dropped_modified conflict code needs to search thru history for
+-- the rev last containing the dropped node id (this was a bug in an
+-- earlier implementation).
+
+mtn_setup()
+
+addfile("file_1", "file_1 base")
+commit("testbranch", "base")
+base = base_revision()
+
+writefile("file_1", "file_1 upstream 1")
+
+commit("testbranch", "upstream 1")
+upstream_1 = base_revision()
+
+revert_to(base)
+
+addfile("file_2", "file_2 local")
+
+commit("testbranch", "local 1")
+local_1 = base_revision()
+
+revert_to(upstream_1)
+
+addfile("file_2", "file_2 upstream 1")
+
+commit("testbranch", "upstream 2")
+upstream_2 = base_revision()
+
+check(mtn("show_conflicts", upstream_2, local_1), 0, nil, true)
+check(samelines("stderr",
+ {"mtn: [left]     27d41ae9f2b3cb73b130d9845d77574a11021b17",
+  "mtn: [right]    3cae692a68fa6710b1db5e73e3e876994c175925",
+  "mtn: [ancestor] 736498437fa91538540dc5fbad750cbc1472d793",
+  "mtn: conflict: duplicate name 'file_2' for the directory ''",
+  "mtn: added as a new file on the left",
+  "mtn: added as a new file on the right",
+  "mtn: 1 conflict with supported resolutions."}))
+
+check(mtn("conflicts", "store", upstream_2, local_1), 0, nil, true)
+
+-- We should keep upstream, and drop local, but we get it backwards
+check(mtn("conflicts", "resolve_first_left", "drop"), 0, nil, true)
+check(mtn("conflicts", "resolve_first_right", "keep"), 0, nil, true)
+
+check(mtn("explicit_merge", "--resolve-conflicts", upstream_2, local_1, "testbranch"), 0, nil, true)
+check(samelines("stderr",
+ {"mtn: [left]  27d41ae9f2b3cb73b130d9845d77574a11021b17",
+  "mtn: [right] 3cae692a68fa6710b1db5e73e3e876994c175925",
+  "mtn: dropping 'file_2'",
+  "mtn: keeping 'file_2'",
+  "mtn: [merged] fce2fe3c327a294209ad695e0658275f104fe12a"}))
+
+check(mtn("update"), 0, nil, true)
+
+-- One more local mod
+writefile("file_2", "file_2 local 2")
+commit("testbranch", "local 2")
+local_2 = base_revision()
+
+-- round 2; upstream modifies the file again, and we try to merge
+revert_to(upstream_2)
+
+writefile("file_2", "file_2 upstream 2")
+
+commit("testbranch", "upstream 3")
+upstream_3 = base_revision()
+
+check(mtn("show_conflicts", upstream_3, local_2), 0, nil, true)
+check(samelines("stderr",
+ {"mtn: [left]     48b18ebc7b70733133539384e49a2eedb82e32b2",
+  "mtn: [right]    650057e8a81bd41991dc5ff10b2d60343f1032ae",
+  "mtn: [ancestor] 27d41ae9f2b3cb73b130d9845d77574a11021b17",
+  "mtn: conflict: file 'file_2'",
+  "mtn: modified on the left, named file_2",
+  "mtn: dropped and recreated on the right",
+  "mtn: 1 conflict with supported resolutions."}))
+
+--  There are two nodes with filename 'file_2'; node 4 in upstream,
+--  node 3 in local. At this point, node 4 is modified in upstream and
+--  dropped in local; node 3 is unborn in upstream and modified in
+--  local. Therefore this is a combination of dropped_modified and
+--  duplicate_name conflicts, which we handle as a dropped_modified
+--  conflict.
+check(mtn("conflicts", "store", upstream_3, local_2), 0, nil, true)
+check(samefilestd("conflicts_3_2", "_MTN/conflicts"))
+
+-- since we have a duplicate name conflict, we need to specify both
+-- right and left resolutions, so 'resolve_first' is wrong here
+check(mtn("conflicts", "resolve_first", "keep"), 1, nil, true)
+check(qgrep("must specify 'resolve_first_left' or 'resolve_first_right'", "stderr"))
+
+check(mtn("conflicts", "show_first"), 0, nil, true)
+check(samelines("stderr",
+ {"mtn: conflict: file 'file_2'",
+  "mtn: modified on the left",
+  "mtn: dropped and recreated on the right",
+  "mtn: possible resolutions:",
+  "mtn: resolve_first_left drop",
+  "mtn: resolve_first_left rename",
+  "mtn: resolve_first_left user_rename \"new_content_name\" \"new_file_name\"",
+  "mtn: resolve_first_left keep",
+  "mtn: resolve_first_left user \"name\"",
+  "mtn: resolve_first_right drop",
+  "mtn: resolve_first_right rename",
+  "mtn: resolve_first_right user_rename \"new_content_name\" \"new_file_name\"",
+  "mtn: resolve_first_right keep",
+  "mtn: resolve_first_right user \"name\""}))   
+
+-- We want to keep the upstream node to avoid future conflicts
+check(mtn("conflicts", "resolve_first_left", "keep"), 0, nil, true)
+
+check(mtn("conflicts", "show_first"), 0, nil, true)
+check(samelines("stderr",
+ {"mtn: conflict: file 'file_2'",
+  "mtn: modified on the left",
+  "mtn: dropped and recreated on the right",
+  "mtn: left_resolution: keep",
+  "mtn: possible resolutions:",
+  "mtn: resolve_first_right drop",
+  "mtn: resolve_first_right rename",
+  "mtn: resolve_first_right user_rename \"new_content_name\" \"new_file_name\""}))   
+
+check(mtn("conflicts", "resolve_first_right", "drop"), 0, nil, true)
+check(samefilestd("conflicts_3_2_resolved", "_MTN/conflicts"))
+
+check(mtn("explicit_merge", "--resolve-conflicts", upstream_3, local_2, "testbranch"), 0, nil, true)
+check(qgrep("mtn: dropping 'file_2'", "stderr"))
+check(qgrep("mtn: \\[merged\\] 864bfab34bcd301828a985f000c6f8ada712b0ca", "stderr")) -- for comparing with below
+
+check(mtn("update"), 0, nil, true)
+check(samelines("file_2", {"file_2 upstream 2"}))
+
+-- Repeat merge with left, right swapped, to test symmetry in code.
+check(mtn("conflicts", "store", local_2, upstream_3), 0, nil, true)
+check(samefilestd("conflicts_2_3", "_MTN/conflicts"))
+
+check(mtn("conflicts", "resolve_first_right", "keep"), 0, nil, true)
+
+check(mtn("conflicts", "show_first"), 0, nil, true)
+check(samelines("stderr",
+ {"mtn: conflict: file 'file_2'",
+  "mtn: dropped and recreated on the left",
+  "mtn: modified on the right",
+  "mtn: right_resolution: keep",
+  "mtn: possible resolutions:",
+  "mtn: resolve_first_left drop",
+  "mtn: resolve_first_left rename",
+  "mtn: resolve_first_left user_rename \"new_content_name\" \"new_file_name\""}))   
+
+check(mtn("conflicts", "resolve_first_left", "drop"), 0, nil, true)
+check(samefilestd("conflicts_2_3_resolved", "_MTN/conflicts"))
+
+check(mtn("explicit_merge", "--resolve-conflicts", local_2, upstream_3, "testbranch"), 0, nil, true)
+check(qgrep("mtn: dropping 'file_2'", "stderr"))
+check(qgrep("mtn: \\[merged\\] 864bfab34bcd301828a985f000c6f8ada712b0ca", "stderr"))
+-- same revision as merge in other order
+
+-- end of file
============================================================
--- /dev/null	
+++ test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2/conflicts_2_3	1f257fa6e99abf93229af189109b9efe3cfae88f
@@ -0,0 +1,13 @@
+    left [650057e8a81bd41991dc5ff10b2d60343f1032ae]
+   right [48b18ebc7b70733133539384e49a2eedb82e32b2]
+ancestor [27d41ae9f2b3cb73b130d9845d77574a11021b17]
+
+        conflict dropped_modified
+   ancestor_name "file_2"
+ancestor_file_id [7fc990de4797bd6534a5c1deb344e11964f6b353]
+       left_type "recreated file"
+       left_name "file_2"
+    left_file_id [6e49d17f382dc2f03d495181490e7653f1a14ad9]
+      right_type "modified file"
+      right_name "file_2"
+   right_file_id [b7e3240a78dc6afce4507f5a18ab516963e72022]
============================================================
--- /dev/null	
+++ test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2/conflicts_2_3_resolved	d8b9a96fdaf210a4d1bc495c62c3dcf40114d39e
@@ -0,0 +1,15 @@
+    left [650057e8a81bd41991dc5ff10b2d60343f1032ae]
+   right [48b18ebc7b70733133539384e49a2eedb82e32b2]
+ancestor [27d41ae9f2b3cb73b130d9845d77574a11021b17]
+
+           conflict dropped_modified
+      ancestor_name "file_2"
+   ancestor_file_id [7fc990de4797bd6534a5c1deb344e11964f6b353]
+          left_type "recreated file"
+          left_name "file_2"
+       left_file_id [6e49d17f382dc2f03d495181490e7653f1a14ad9]
+         right_type "modified file"
+         right_name "file_2"
+      right_file_id [b7e3240a78dc6afce4507f5a18ab516963e72022]
+ resolved_drop_left 
+resolved_keep_right 
============================================================
--- /dev/null	
+++ test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2/conflicts_3_2	bb23f1fbf87e4f1dcb8aa7c4c3fcb90e1fc84fed
@@ -0,0 +1,13 @@
+    left [48b18ebc7b70733133539384e49a2eedb82e32b2]
+   right [650057e8a81bd41991dc5ff10b2d60343f1032ae]
+ancestor [27d41ae9f2b3cb73b130d9845d77574a11021b17]
+
+        conflict dropped_modified
+   ancestor_name "file_2"
+ancestor_file_id [7fc990de4797bd6534a5c1deb344e11964f6b353]
+       left_type "modified file"
+       left_name "file_2"
+    left_file_id [b7e3240a78dc6afce4507f5a18ab516963e72022]
+      right_type "recreated file"
+      right_name "file_2"
+   right_file_id [6e49d17f382dc2f03d495181490e7653f1a14ad9]
============================================================
--- /dev/null	
+++ test/func/resolve_conflicts_dropped_modified_upstream_vs_local_2/conflicts_3_2_resolved	4482ad2e755308ce8a546191348cc648ad13d782
@@ -0,0 +1,15 @@
+    left [48b18ebc7b70733133539384e49a2eedb82e32b2]
+   right [650057e8a81bd41991dc5ff10b2d60343f1032ae]
+ancestor [27d41ae9f2b3cb73b130d9845d77574a11021b17]
+
+           conflict dropped_modified
+      ancestor_name "file_2"
+   ancestor_file_id [7fc990de4797bd6534a5c1deb344e11964f6b353]
+          left_type "modified file"
+          left_name "file_2"
+       left_file_id [b7e3240a78dc6afce4507f5a18ab516963e72022]
+         right_type "recreated file"
+         right_name "file_2"
+      right_file_id [6e49d17f382dc2f03d495181490e7653f1a14ad9]
+ resolved_keep_left 
+resolved_drop_right 
============================================================
--- test/func/resolve_conflicts_dropped_modified_upstream_vs_local/__driver__.lua	e7f37ed8b1a65325390ad4f1ade33904e7535381
+++ test/func/resolve_conflicts_dropped_modified_upstream_vs_local_attr/__driver__.lua	abe2836cc2dfbae368820e3b5515ad587911c71b
@@ -34,7 +34,7 @@ check(samelines("stderr",
  {"mtn: [left]     1e700864de7a2cbb1cf85c26f5e1e4ca335d2bc2",
   "mtn: [right]    a2889488ed1801a904d0219ec9939dfc2e9be033",
   "mtn: [ancestor] f80ff103551d0313647d6c84990bc9db6b158dac",
-  "mtn: conflict: file 'file_2' from revision f80ff103551d0313647d6c84990bc9db6b158dac",
+  "mtn: conflict: file 'file_2'",
   "mtn: modified on the left, named file_2",
   "mtn: dropped on the right",
   "mtn: 1 conflict with supported resolutions."}))
@@ -47,7 +47,7 @@ check(samelines("stderr",
 check(samelines("stderr",
  {"mtn: [left]  1e700864de7a2cbb1cf85c26f5e1e4ca335d2bc2",
   "mtn: [right] a2889488ed1801a904d0219ec9939dfc2e9be033",
-  "mtn: dropping 'file_2'",
+  "mtn: dropping 'file_2' from left",
   "mtn: [merged] dd1ba606b52fddb4431da3760ff65b65f6509a48"}))
 
 check(mtn("update"), 0, nil, true)
@@ -75,7 +75,7 @@ check(mtn("merge", "--resolve-conflicts"
 check(qgrep("mtn: misuse: merge failed due to unresolved conflicts", "stderr"))
 
 check(mtn("merge", "--resolve-conflicts"), 0, nil, true)
-check(qgrep("mtn: dropping 'file_2'", "stderr"))
+check(qgrep("mtn: dropping 'file_2' from left", "stderr"))
 
 check(mtn("update"), 0, nil, true)
 check(not exists("file_2"))
@@ -86,10 +86,10 @@ check(samelines("stderr",
  {"mtn: [left]     c0ed8c29ffad149af1c948969e8e80d270999b13",
   "mtn: [right]    dd1ba606b52fddb4431da3760ff65b65f6509a48",
   "mtn: [ancestor] 1e700864de7a2cbb1cf85c26f5e1e4ca335d2bc2",
-  "mtn: conflict: file 'file_2' from revision 1e700864de7a2cbb1cf85c26f5e1e4ca335d2bc2",
+  "mtn: conflict: file 'file_2'",
   "mtn: modified on the left, named file_2",
   "mtn: dropped on the right",
-  "mtn: resolution: drop",
+  "mtn: left_resolution: drop",
   "mtn: 1 conflict with supported resolutions."}))
 
 -- 'conflicts store' is not needed unless there are other conflicts,
@@ -98,4 +98,11 @@ check(samefilestd("conflicts", "_MTN/con
 check(mtn("conflicts", "store", upstream_2, local_2), 0, nil, true)
 check(samefilestd("conflicts", "_MTN/conflicts"))
 
+-- repeat merge with left, right swapped to verify symmetry in code
+remove("_MTN/conflicts")
+check(mtn("explicit_merge", "--resolve-conflicts", local_2, upstream_2, "testbranch"), 0, nil, true)
+check(qgrep("mtn: dropping 'file_2' from right", "stderr"))
+check(mtn("update"), 0, nil, true)
+check(not exists("file_2"))
+
 -- end of file
============================================================
--- test/func/resolve_conflicts_errors/__driver__.lua	9a130c83d4a7e3545f18d089542603c8d0bb72fa
+++ test/func/resolve_conflicts_errors/__driver__.lua	0819ba223855d9814a7420c7412166b1109ed20f
@@ -107,7 +107,8 @@ canonicalize("stdout")
 check(mtn("conflicts", "resolve_first_right", "user", "checkout.sh"), 1, nil, true)
 check(grep("-v", "detected at", "stderr"), 0, true)
 canonicalize("stdout")
-check("mtn: misuse: other resolution must be 'drop' or 'rename'\n" == readfile("stdout"))
+check(samelines("stdout",
+{"mtn: misuse: other resolution is content_user; specify 'drop', 'rename', or 'user_rename'"}))
 
 -- not in workspace; report nice error; conflicts file must be under
 -- _MTN, so need workspace. Fixes bug 30473
============================================================
--- test/func/resolve_conflicts_orphaned_file/__driver__.lua	fe6a4618be2793d18ee2f97adf94cd564763164f
+++ test/func/resolve_conflicts_orphaned_file/__driver__.lua	beb41b901c1e08bbef25e87fe0b2ef36e74a2a8b
@@ -1,5 +1,4 @@
 -- Test resolving orphaned_file and orphaned_directory conflicts
-
 mtn_setup()
 
 addfile("foo", "foo base")
============================================================
--- innosetup/README.txt	6f92ce9f01c75b83994b690bb8b00c549c368965
+++ innosetup/README.txt	d306d15f4bba8011dd1fa01dd9522b93c8ce8920
@@ -19,6 +19,10 @@
 Check out the release version of monotone:
 mtn -d /path/to/monotone.db checkout -r t:monotone-<version> --branch net.venge.monotone.monotone-<version> monotone-<version>
 
+Do not use the source tarball; if you do, the resulting monotone
+executable will not know the base revision, which is needed for good
+bug reports.
+
 Build the release. See the last instruction in ../INSTALL_windows_native.txt
 Then build the installer:
 
============================================================
--- src/automate.cc	919e3df52514d0877aeafc67791aae6a3faa33b4
+++ src/automate.cc	30e9bb3567e320f83ce5c1b4817dc9ad90fe8423
@@ -1352,15 +1352,15 @@ CMD_AUTOMATE(get_base_revision_id, "",
   E(args.size() == 0, origin::user,
     F("no arguments needed"));
 
-  database db(app);
   workspace work(app);
+  revision_t rev;
 
-  parent_map parents;
-  work.get_parent_rosters(db, parents);
-  E(parents.size() == 1, origin::user,
+  work.get_work_rev(rev);
+
+  E(rev.edges.size() == 1, origin::user,
     F("this command can only be used in a single-parent workspace"));
 
-  output << parent_id(parents.begin()) << '\n';
+  output << rev.edges.begin()->first << '\n';
 }
 
 // Name: get_current_revision_id

reply via email to

[Prev in Thread] Current Thread [Next in Thread]