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.issue-209: bf033095aaea2a927


From: code
Subject: [Monotone-commits-diffs] net.venge.monotone.issue-209: bf033095aaea2a9278e5f869efb4dc3116ff5a12
Date: Sun, 3 Jun 2012 17:47:18 +0200 (CEST)

revision:            bf033095aaea2a9278e5f869efb4dc3116ff5a12
date:                2012-05-16T19:16:04
author:              address@hidden
branch:              net.venge.monotone.issue-209
changelog:
add support for dropped/modified conflict; first test passes

* doc/monotone.texi: add dropped/modified

* src/merge_roster.hh:
* src/merge_roster.cc:
* src/merge_content.cc (resolve_merge_conflicts):
* src/merge_conflict.cc:
* src/cmd_merging.cc (show_conflicts_core):
* src/cmd_conflicts.cc (show_conflicts, set_first_conflict): add 
dropped/modified

* test/func/resolve_conflicts_dropped_modified: New directory.

manifest:
format_version "1"

new_manifest [927e3f56a044a768857efa1392e262e4d780a131]

old_revision [53e02eaa302bc05e96a18e3882b0e9843b53cf9a]

add_dir "test/func/resolve_conflicts_dropped_modified"

add_file "test/func/resolve_conflicts_dropped_modified/__driver__.lua"
 content [5cc8d92ebd6d5590401e902cfaccf8d8252c996a]

add_file "test/func/resolve_conflicts_dropped_modified/conflicts-1"
 content [f67001a294d492e4e3c0a2196e37c2a23077e27a]

add_file "test/func/resolve_conflicts_dropped_modified/conflicts-1-resolved"
 content [ecb345496807dd3a509bc7f4ef751abf55c8233a]

patch "doc/monotone.texi"
 from [d1293584f8a36c591fec36bc00427f77512c9876]
   to [c4b06dbfa4640e9358dbbfadf36c78de7d9a2f64]

patch "src/cmd_conflicts.cc"
 from [86bf4eb2e0608a8b9814cef8b416626b4aa0ca90]
   to [c88107b417e03c5d6b8b916830ba1e9c898f47c8]

patch "src/cmd_merging.cc"
 from [84b177469ecb60664be23c42c38cc649d23899a7]
   to [7050a2b91ad9a15d6b8066c2b5e54bb96c87265c]

patch "src/merge_conflict.cc"
 from [1c09dc4a20532ce040c429d7fb5ed2a25a199856]
   to [9921b266cef59c6e2b3d3fd5fda16cf2c48fced1]

patch "src/merge_content.cc"
 from [76d5a0997d9217b309d75806f8059d8f56f2ca49]
   to [f3b9e82e6c46b53d17147068607cec03d581957b]

patch "src/merge_roster.cc"
 from [98297d6264f77d540fc8e1578b1ebc5b2f36ec38]
   to [8f846a5a40e36dba86f99f8ed48b3579c3bbe494]

patch "src/merge_roster.hh"
 from [cd2da3b06f595187a27622a98580b0636aabea4b]
   to [605b6f3f2a3e674e8542864523660ca86e797693]
============================================================
--- doc/monotone.texi	d1293584f8a36c591fec36bc00427f77512c9876
+++ doc/monotone.texi	c4b06dbfa4640e9358dbbfadf36c78de7d9a2f64
@@ -3560,6 +3560,15 @@ @subheading Multiple Name Conflict
 In earlier versions of monotone (those before version 0.39) this type
 of conflict was referred to as a @emph{name conflict}.
 
address@hidden Dropped/Modified Conflict
+
+A dropped/modified conflict occurs when a single file has
+been dropped in one merge parent, and modified in the other.
+
address@hidden supports resolving this conflict; the possible
+resolutions are to drop the file in the result, keep the modified
+version, or keep a user supplied version.
+
 @subheading Attribute Conflict
 
 An attribute conflict occurs when a versioned attribute on a file or
@@ -5125,11 +5134,11 @@ @subheading Commands
 @item mtn conflicts show_remaining address@hidden
 Show remaining unresolved conflicts in the conflicts file.
 
address@hidden mtn conflicts resolve_first address@hidden @var{resolution}
address@hidden conflicts address@hidden mtn conflicts resolve_first address@hidden @var{resolution}
 Specify a resolution for the first conflict in the conflicts file; it
 must be a single file conflict. The conflicts file is updated.
 
address@hidden conflicts address@hidden mtn conflicts resolve_first_left address@hidden @var{resolution}
address@hidden mtn conflicts resolve_first_left address@hidden @var{resolution}
 @itemx mtn conflicts resolve_first_right address@hidden @var{resolution}
 Specify a resolution for one of the files in the first conflict in the
 conflicts file; it must be a two file conflict. The conflicts file is
@@ -10214,6 +10223,8 @@ @section Automation
 
 @itemize
 @item
+FIXME: -- Add reporting and resolution for dropped/modified conflicts.
address@hidden
 11.0 -- Add resolution for orphaned node conflicts. Deleted
 @code{resolved_user} conflict resolution; use @code{resolved_*_left}
 for single file conflicts. Add @code{resolved_keep_left,
@@ -10426,6 +10437,17 @@ @section Automation
    right_file_id [e80910e54d0bdea1b6d295ada320b87aaf9fdc23]
 @end verbatim
 
+File dropped and modified (and possibly renamed):
address@hidden
+        conflict drop_modified
+   ancestor_name "foo"
+ancestor_file_id [e80910e54d0bdea1b6d295ada320b87aaf9fdc23]
+       left_type "dropped file"
+      right_type "modified file"
+      right_name "baz"
+   right_file_id [fe6d523f607e2f2fc0f0defad3bda0351a95a337]
address@hidden verbatim
+
 Invalid file name (@file{_MTN} in root directory):
 @verbatim
      conflict invalid_name
============================================================
--- src/cmd_conflicts.cc	86bf4eb2e0608a8b9814cef8b416626b4aa0ca90
+++ src/cmd_conflicts.cc	c88107b417e03c5d6b8b916830ba1e9c898f47c8
@@ -84,6 +84,57 @@ show_conflicts(database & db, conflicts_
         }
     }
 
+  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;
+
+      if (conflict.resolution.first == resolve_conflicts::none)
+        {
+          node_id nid;
+          file_path modified_name;
+
+          if (conflict.left_nid == the_null_node)
+            {
+              // left side dropped, right side modified
+              nid = conflict.right_nid;
+              conflicts.right_roster->get_name(conflict.right_nid, modified_name);
+            }
+          else
+            {
+              // left side modified, right side dropped
+              nid = conflict.left_nid;
+              conflicts.left_roster->get_name(conflict.left_nid, modified_name);
+            }
+
+          P(F("conflict: file '%s'") % modified_name);
+          if (conflict.left_nid == the_null_node)
+            {
+              P(F("dropped on the left"));
+              P(F("modified on the right"));
+            }
+          else
+            {
+              P(F("modified on the left"));
+              P(F("dropped on the right"));
+            }
+
+          switch (show_case)
+            {
+            case first:
+              P(F("possible resolutions:"));
+              P(F("resolve_first drop"));
+              P(F("resolve_first keep"));
+              P(F("resolve_first user \"name\""));
+              return;
+
+            case remaining:
+              break;
+            }
+        }
+    }
+
   for (std::vector<duplicate_name_conflict>::iterator i = conflicts.result.duplicate_name_conflicts.begin();
        i != conflicts.result.duplicate_name_conflicts.end();
        ++i)
@@ -187,6 +238,8 @@ show_conflicts(database & db, conflicts_
               (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout);
             conflicts.result.report_multiple_name_conflicts
               (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout);
+            conflicts.result.report_dropped_modified_conflicts
+              (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout);
             conflicts.result.report_attribute_conflicts
               (*conflicts.left_roster, *conflicts.right_roster, adaptor, false, std::cout);
           }
@@ -357,6 +410,42 @@ set_first_conflict(database & db,
             }
         }
 
+      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;
+
+          if (conflict.resolution.first == 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;
+                }
+              else if ("keep" == idx(args,0)())
+                {
+                  E(args.size() == 1, origin::user, F("wrong number of arguments"));
+
+                  conflict.resolution.first  = resolve_conflicts::keep;
+                }
+              else if ("user" == idx(args,0)())
+                {
+                  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);
+                }
+              else
+                {
+                  E(false, origin::user,
+                    F(conflict_resolution_not_supported_msg) % idx(args,0) % "dropped_modified");
+                }
+              return;
+            }
+        }
+
       for (std::vector<file_content_conflict>::iterator i = conflicts.result.file_content_conflicts.begin();
            i != conflicts.result.file_content_conflicts.end();
            ++i)
============================================================
--- src/cmd_merging.cc	84b177469ecb60664be23c42c38cc649d23899a7
+++ src/cmd_merging.cc	7050a2b91ad9a15d6b8066c2b5e54bb96c87265c
@@ -1,5 +1,5 @@
 // Copyright (C) 2002 Graydon Hoare <address@hidden>
-//               2008, 2010 Stephen Leake <address@hidden>
+//               2008, 2010, 2012 Stephen Leake <address@hidden>
 //
 // This program is made available under the GNU GPL version 2.0 or
 // greater. See the accompanying file COPYING for details.
@@ -1064,6 +1064,7 @@ show_conflicts_core (database & db,
 
       result.report_orphaned_node_conflicts(*l_roster, *r_roster, adaptor, basic_io, output);
       result.report_multiple_name_conflicts(*l_roster, *r_roster, adaptor, basic_io, output);
+      result.report_dropped_modified_conflicts(*l_roster, *r_roster, adaptor, basic_io, output);
       result.report_duplicate_name_conflicts(*l_roster, *r_roster, adaptor, basic_io, output);
 
       result.report_attribute_conflicts(*l_roster, *r_roster, adaptor, basic_io, output);
============================================================
--- src/merge_conflict.cc	1c09dc4a20532ce040c429d7fb5ed2a25a199856
+++ src/merge_conflict.cc	9921b266cef59c6e2b3d3fd5fda16cf2c48fced1
@@ -1,5 +1,5 @@
 // Copyright (C) 2005 Nathaniel Smith <address@hidden>
-//               2008, 2009 Stephen Leake <address@hidden>
+//               2008, 2009, 2012 Stephen Leake <address@hidden>
 //
 // This program is made available under the GNU GPL version 2.0 or
 // greater. See the accompanying file COPYING for details.
@@ -50,6 +50,7 @@ namespace
     symbol const conflict("conflict");
     symbol const content("content");
     symbol const directory_loop("directory_loop");
+    symbol const dropped_modified("dropped_modified");
     symbol const duplicate_name("duplicate_name");
     symbol const invalid_name("invalid_name");
     symbol const left("left");
@@ -1048,6 +1049,104 @@ void
 }
 
 void
+roster_merge_result::report_dropped_modified_conflicts(roster_t const & left_roster,
+                                                       roster_t const & right_roster,
+                                                       content_merge_adaptor & adaptor,
+                                                       const bool basic_io,
+                                                       std::ostream & output) const
+{
+  MM(left_roster);
+  MM(right_roster);
+
+  for (size_t i = 0; i < dropped_modified_conflicts.size(); ++i)
+    {
+      dropped_modified_conflict const & conflict = dropped_modified_conflicts[i];
+      MM(conflict);
+
+      node_id nid;
+      file_path modified_name;
+
+      if (conflict.left_nid == the_null_node)
+        {
+          // left side dropped, right side modified
+          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
+          I(!roster.is_attached(conflict.left_nid));
+
+          nid = conflict.left_nid;
+          left_roster.get_name(conflict.left_nid, modified_name);
+        }
+
+      shared_ptr<roster_t const> lca_roster;
+      revision_id lca_rid;
+      file_path ancestor_name;
+
+      adaptor.get_ancestral_roster(nid, lca_rid, lca_roster);
+      lca_roster->get_name(nid, ancestor_name);
+
+      if (basic_io)
+        {
+          basic_io::stanza st;
+
+          content_merge_database_adaptor & db_adaptor (dynamic_cast<content_merge_database_adaptor &>(adaptor));
+          file_id fid;
+
+          st.push_str_pair(syms::conflict, syms::dropped_modified);
+          st.push_str_pair(syms::ancestor_name, ancestor_name.as_external());
+          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)
+            {
+              st.push_str_pair(syms::left_type, "dropped file");
+            }
+          else
+            {
+              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)
+            {
+              st.push_str_pair(syms::right_type, "dropped file");
+            }
+          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_resolution (st, left_side, conflict.resolution);
+          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("dropped on the left"));
+              P(F("modified on the right, named %s") % modified_name);
+            }
+          else
+            {
+              P(F("modified on the left, named %s") % modified_name);
+              P(F("dropped on the right"));
+            }
+        }
+    }
+}
+
+void
 roster_merge_result::report_duplicate_name_conflicts(roster_t const & left_roster,
                                                      roster_t const & right_roster,
                                                      content_merge_adaptor & adaptor,
@@ -1749,6 +1848,124 @@ static void
 } // read_multiple_name_conflicts
 
 static void
+read_dropped_modified_conflict(basic_io::parser & pars,
+                               dropped_modified_conflict & conflict,
+                               roster_t const & left_roster,
+                               roster_t const & right_roster)
+{
+  string tmp;
+
+  pars.esym(syms::ancestor_name); pars.str();
+  pars.esym(syms::ancestor_file_id); pars.hex();
+
+  pars.esym(syms::left_type);
+  pars.str(tmp);
+
+  if (tmp == "dropped file")
+    {
+      // no more data for left
+    }
+  else
+    {
+      // modified file
+      pars.esym(syms::left_name); pars.str(tmp);
+      conflict.left_nid = left_roster.get_node(file_path_external(utf8(tmp, origin::internal)))->self;
+      pars.esym(syms::left_file_id); pars.hex();
+    }
+
+  pars.esym(syms::right_type);
+  pars.str(tmp);
+
+  if (tmp == "dropped file")
+    {
+      // no more data for right
+    }
+  else
+    {
+      // modified file
+      pars.esym(syms::right_name); pars.str(tmp);
+      conflict.right_nid = right_roster.get_node(file_path_external(utf8(tmp, origin::internal)))->self;
+      pars.esym(syms::right_file_id); pars.hex();
+    }
+
+  // check for a resolution
+  if ((!pars.symp (syms::conflict)) && pars.tok.in.lookahead != EOF)
+    {
+      if (pars.symp (syms::resolved_drop_left))
+        {
+          conflict.resolution.first = resolve_conflicts::drop;
+          pars.sym();
+        }
+      else if (pars.symp (syms::resolved_rename_left))
+        {
+          conflict.resolution.first = resolve_conflicts::rename;
+          pars.sym();
+          conflict.resolution.second = new_optimal_path(pars.token, true);
+          pars.str();
+        }
+      else
+        E(false, origin::user,
+          F(conflict_resolution_not_supported_msg) % pars.token % "dropped_modified");
+    }
+} // 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)
+{
+  while (pars.tok.in.lookahead != EOF && pars.symp(syms::dropped_modified))
+    {
+      dropped_modified_conflict conflict(the_null_node, the_null_node);
+
+      pars.sym();
+
+      read_dropped_modified_conflict(pars, conflict, left_roster, right_roster);
+
+      conflicts.push_back(conflict);
+
+      if (pars.tok.in.lookahead != EOF)
+        pars.esym (syms::conflict);
+    }
+} // read_dropped_modified_conflicts
+static void
+validate_dropped_modified_conflicts(basic_io::parser & pars,
+                                    std::vector<dropped_modified_conflict> & conflicts,
+                                    roster_t const & left_roster,
+                                    roster_t const & right_roster)
+{
+  for (std::vector<dropped_modified_conflict>::iterator i = conflicts.begin();
+       i != conflicts.end();
+       ++i)
+    {
+      dropped_modified_conflict & merge_conflict = *i;
+      dropped_modified_conflict file_conflict (the_null_node, the_null_node);
+
+      pars.esym(syms::dropped_modified);
+
+      read_dropped_modified_conflict(pars, file_conflict, left_roster, 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,
+        origin::user,
+        F(conflicts_mismatch_msg));
+
+      merge_conflict.resolution = file_conflict.resolution;
+
+      if (pars.tok.in.lookahead != EOF)
+        pars.esym (syms::conflict);
+      else
+        {
+          std::vector<dropped_modified_conflict>::iterator tmp = i;
+          E(++tmp == conflicts.end(), origin::user,
+             F(conflicts_mismatch_msg));
+        }
+    }
+} // validate_dropped_modified_conflicts
+
+static void
 read_duplicate_name_conflict(basic_io::parser & pars,
                              duplicate_name_conflict & conflict,
                              roster_t const & left_roster,
@@ -2110,6 +2327,7 @@ 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_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);
     }
@@ -2122,6 +2340,7 @@ 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_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);
@@ -2212,6 +2431,7 @@ roster_merge_result::write_conflict_file
   report_directory_loop_conflicts(*left_roster, *right_roster, adaptor, true, output);
   report_orphaned_node_conflicts(*left_roster, *right_roster, adaptor, true, output);
   report_multiple_name_conflicts(*left_roster, *right_roster, adaptor, true, output);
+  report_dropped_modified_conflicts(*left_roster, *right_roster, adaptor, true, output);
   report_duplicate_name_conflicts(*left_roster, *right_roster, adaptor, true, output);
   report_attribute_conflicts(*left_roster, *right_roster, adaptor, true, output);
   report_file_content_conflicts(lua, *left_roster, *right_roster, adaptor, true, output);
@@ -2361,6 +2581,91 @@ roster_merge_result::resolve_orphaned_no
   orphaned_node_conflicts.clear();
 }
 
+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)
+{
+  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)
+    {
+      dropped_modified_conflict const & conflict = *i;
+      MM(conflict);
+
+      node_id   nid;
+      file_path modified_name;
+      file_id   modified_fid;
+
+      if (conflict.left_nid == the_null_node)
+        {
+          nid = conflict.right_nid;
+          right_roster.get_file_details(nid, modified_fid, modified_name);
+        }
+      else
+        {
+          nid = conflict.left_nid;
+          left_roster.get_file_details(nid, modified_fid, modified_name);
+        }
+
+      switch (conflict.resolution.first)
+        {
+        case resolve_conflicts::none:
+          E(false, origin::user,
+            F("no resolution provided for dropped_modifed '%s'") % modified_name);
+          break;
+
+        case resolve_conflicts::content_user:
+          P(F("replacing content of '%s' with '%s'") %
+            modified_name % conflict.resolution.second->as_external());
+
+          {
+            file_data modified_data, result_data;
+            data result_raw_data;
+            file_id result_fid;
+            adaptor.get_version(modified_fid, modified_data);
+
+            read_data(*conflict.resolution.second, result_raw_data);
+
+            result_data = file_data(result_raw_data);
+            calculate_ident(result_data, result_fid);
+
+            file_t result_node = downcast_to_file_t(roster.get_node_for_update(nid));
+            result_node->content = result_fid;
+
+            adaptor.record_file(modified_fid, result_fid, modified_data, result_data);
+
+            attach_node(lua, roster, nid, modified_name);
+          }
+          break;
+
+        case resolve_conflicts::drop:
+          P(F("dropping '%s'") % modified_name);
+
+          // already dropped; nothing to do
+          break;
+
+        case resolve_conflicts::keep:
+          P(F("keeping '%s'") % modified_name);
+          attach_node (lua, roster, nid, modified_name);
+          break;
+
+        default:
+          I(false);
+        }
+
+    } // end for
+
+  dropped_modified_conflicts.clear();
+}
+
 static void
 resolve_duplicate_name_one_side(lua_hooks & lua,
                                 resolve_conflicts::file_resolution_t const & resolution,
@@ -2433,11 +2738,6 @@ resolve_duplicate_name_one_side(lua_hook
         F("no resolution provided for duplicate_name '%s'") % name);
       break;
 
-    case resolve_conflicts::content_internal:
-      E(false, origin::user,
-        F("invalid resolution for duplicate_name '%s'") % name);
-      break;
-
     default:
       I(false);
     }
============================================================
--- src/merge_content.cc	76d5a0997d9217b309d75806f8059d8f56f2ca49
+++ src/merge_content.cc	f3b9e82e6c46b53d17147068607cec03d581957b
@@ -1,5 +1,5 @@
 // Copyright (C) 2008 Nathaniel Smith <address@hidden>
-//               2008, 2010 Stephen Leake <address@hidden>
+//               2008, 2010, 2012 Stephen Leake <address@hidden>
 //
 // This program is made available under the GNU GPL version 2.0 or
 // greater. See the accompanying file COPYING for details.
@@ -667,8 +667,9 @@ resolve_merge_conflicts(lua_hooks & lua,
           E(result.attribute_conflicts.size() == 0, origin::user,
             F(msg) % "attribute_conflicts");
 
-          // resolve the ones we can.
+          // resolve the ones we can, if they have resolutions specified
           result.resolve_orphaned_node_conflicts(lua, left_roster, right_roster, adaptor);
+          result.resolve_dropped_modified_conflicts(lua, left_roster, right_roster, adaptor);
           result.resolve_duplicate_name_conflicts(lua, left_roster, right_roster, adaptor);
           result.resolve_file_content_conflicts(lua, left_roster, right_roster, adaptor);
         }
@@ -682,6 +683,7 @@ resolve_merge_conflicts(lua_hooks & lua,
 
       result.report_orphaned_node_conflicts(left_roster, right_roster, adaptor, false, std::cout);
       result.report_multiple_name_conflicts(left_roster, right_roster, adaptor, false, std::cout);
+      result.report_dropped_modified_conflicts(left_roster, right_roster, adaptor, false, std::cout);
       result.report_duplicate_name_conflicts(left_roster, right_roster, adaptor, false, std::cout);
 
       result.report_attribute_conflicts(left_roster, right_roster, adaptor, false, std::cout);
============================================================
--- src/merge_roster.cc	98297d6264f77d540fc8e1578b1ebc5b2f36ec38
+++ src/merge_roster.cc	8f846a5a40e36dba86f99f8ed48b3579c3bbe494
@@ -1,4 +1,4 @@
-// Copyright (C) 2008, 2010 Stephen Leake <address@hidden>
+// Copyright (C) 2008, 2010, 2012 Stephen Leake <address@hidden>
 //               2005 Nathaniel Smith <address@hidden>
 //
 // This program is made available under the GNU GPL version 2.0 or
@@ -89,6 +89,21 @@ template <> void
 }
 
 template <> void
+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;
+  if (conflict.resolution.first != resolve_conflicts::none)
+    {
+      oss << " resolution: " << image(conflict.resolution.first);
+      oss << " name: " << conflict.resolution.second;
+    }
+  oss << "\n";
+  out = oss.str();
+}
+
+template <> void
 dump(duplicate_name_conflict const & conflict, string & out)
 {
   ostringstream oss;
@@ -146,6 +161,7 @@ roster_merge_result::clear()
 
   orphaned_node_conflicts.clear();
   multiple_name_conflicts.clear();
+  dropped_modified_conflicts.clear();
   duplicate_name_conflicts.clear();
 
   attribute_conflicts.clear();
@@ -175,6 +191,7 @@ roster_merge_result::has_non_content_con
     || !directory_loop_conflicts.empty()
     || !orphaned_node_conflicts.empty()
     || !multiple_name_conflicts.empty()
+    || !dropped_modified_conflicts.empty()
     || !duplicate_name_conflicts.empty()
     || !attribute_conflicts.empty();
 }
@@ -183,6 +200,7 @@ roster_merge_result::count_supported_res
 roster_merge_result::count_supported_resolution() const
 {
   return orphaned_node_conflicts.size()
+    + dropped_modified_conflicts.size()
     + file_content_conflicts.size()
     + duplicate_name_conflicts.size();
 }
@@ -209,6 +227,7 @@ dump_conflicts(roster_merge_result const
 
   dump(result.orphaned_node_conflicts, out);
   dump(result.multiple_name_conflicts, out);
+  dump(result.dropped_modified_conflicts, out);
   dump(result.duplicate_name_conflicts, out);
 
   dump(result.attribute_conflicts, out);
@@ -316,34 +335,42 @@ namespace
                    marking_map const & markings,
                    set<revision_id> const & uncommon_ancestors,
                    roster_t const & parent_roster,
-                   roster_t & new_roster)
+                   side_t const present_in,
+                   roster_merge_result & result)
   {
     const_marking_t const & m = markings.get_marking(n->self);
     revision_id const & birth = m->birth_revision;
     if (uncommon_ancestors.find(birth) != uncommon_ancestors.end())
-      create_node_for(n, new_roster);
+      create_node_for(n, result.roster);
     else
       {
         // In this branch we are NOT inserting the node into the new roster as it
         // has been deleted from the other side of the merge.
-        // In this case, output a warning if there are changes to the file on the
+        // In this case, create a conflict if there are changes to the file on the
         // side of the merge where it still exists.
         set<revision_id> const & content_marks = m->file_content;
         bool found_one_ignored_content = false;
-        for (set<revision_id>::const_iterator it = content_marks.begin(); it != content_marks.end(); it++)
+        for (set<revision_id>::const_iterator it = content_marks.begin();
+             (!found_one_ignored_content) && (it != content_marks.end());
+             it++)
           {
             if (uncommon_ancestors.find(*it) != uncommon_ancestors.end())
               {
                 if (!found_one_ignored_content)
                   {
-                    file_path fp;
-                    parent_roster.get_name(n->self, fp);
-                    W(F("content changes to the file '%s'\n"
-                        "will be ignored during this merge as the file has been\n"
-                        "removed on one side of the merge.  Affected revisions include:") % fp);
+                    switch (present_in)
+                    {
+                    case left_side:
+                      result.dropped_modified_conflicts.push_back
+                        (dropped_modified_conflict(n->self, the_null_node));
+                      break;
+                    case right_side:
+                      result.dropped_modified_conflicts.push_back
+                        (dropped_modified_conflict(the_null_node, n->self));
+                      break;
+                    }
                   }
                 found_one_ignored_content = true;
-                W(F("Revision: %s") % (*it));
               }
           }
       }
@@ -515,13 +542,15 @@ roster_merge(roster_t const & left_paren
           case parallel::in_left:
             insert_if_unborn(i.left_data(),
                              left_markings, left_uncommon_ancestors, left_parent,
-                             result.roster);
+                             left_side, // present_in
+                             result);
             break;
 
           case parallel::in_right:
             insert_if_unborn(i.right_data(),
                              right_markings, right_uncommon_ancestors, right_parent,
-                             result.roster);
+                             right_side, // present_in
+                             result);
             break;
 
           case parallel::in_both:
============================================================
--- src/merge_roster.hh	cd2da3b06f595187a27622a98580b0636aabea4b
+++ src/merge_roster.hh	605b6f3f2a3e674e8542864523660ca86e797693
@@ -1,5 +1,5 @@
 // Copyright (C) 2005, 2010 Nathaniel Smith <address@hidden>
-//               2008, 2009 Stephen Leake <address@hidden>
+//               2008, 2009, 2012 Stephen Leake <address@hidden>
 //
 // This program is made available under the GNU GPL version 2.0 or
 // greater. See the accompanying file COPYING for details.
@@ -90,6 +90,17 @@ struct multiple_name_conflict
   std::pair<node_id, path_component> left, right;
 };
 
+// nodes with drop/modified conflicts are left detached in the resulting
+// 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.
+  resolve_conflicts::file_resolution_t resolution;
+
+  dropped_modified_conflict(node_id left_nid, node_id right_nid) : left_nid(left_nid), right_nid(right_nid)
+  {resolution.first = resolve_conflicts::none;}
+};
+
 // this is when two distinct nodes want to have the same name.  these nodes
 // always each merged their names cleanly.  the nodes in the resulting roster
 // are both detached.
@@ -152,6 +163,7 @@ template <> void dump(multiple_name_conf
 
 template <> void dump(orphaned_node_conflict const & conflict, std::string & out);
 template <> void dump(multiple_name_conflict const & conflict, std::string & out);
+template <> void dump(dropped_modified_conflict const & conflict, std::string & out);
 template <> void dump(duplicate_name_conflict const & conflict, std::string & out);
 
 template <> void dump(attribute_conflict const & conflict, std::string & out);
@@ -166,16 +178,18 @@ struct roster_merge_result
   //   - duplicate name conflicts
   //   - orphaned node conflicts
   //   - multiple name conflicts
+  //   - drop/modified conflicts
   //   - directory loop conflicts
   // - attribute conflicts
   // - file content conflicts
 
-  bool missing_root_conflict;
+  bool missing_root_conflict; // there can only be one of these
   std::vector<invalid_name_conflict> invalid_name_conflicts;
   std::vector<directory_loop_conflict> directory_loop_conflicts;
 
   std::vector<orphaned_node_conflict> orphaned_node_conflicts;
   std::vector<multiple_name_conflict> multiple_name_conflicts;
+  std::vector<dropped_modified_conflict> dropped_modified_conflicts;
   std::vector<duplicate_name_conflict> duplicate_name_conflicts;
 
   std::vector<attribute_conflict> attribute_conflicts;
@@ -223,6 +237,16 @@ struct roster_merge_result
                                       bool const basic_io,
                                       std::ostream & output) const;
 
+  void report_dropped_modified_conflicts(roster_t const & left,
+                                         roster_t const & right,
+                                         content_merge_adaptor & adaptor,
+                                         bool const basic_io,
+                                         std::ostream & output) const;
+  void resolve_dropped_modified_conflicts(lua_hooks & lua,
+                                          roster_t const & left_roster,
+                                          roster_t const & right_roster,
+                                          content_merge_adaptor & adaptor);
+
   void report_duplicate_name_conflicts(roster_t const & left,
                                        roster_t const & right,
                                        content_merge_adaptor & adaptor,
============================================================
--- /dev/null	
+++ test/func/resolve_conflicts_dropped_modified/__driver__.lua	5cc8d92ebd6d5590401e902cfaccf8d8252c996a
@@ -0,0 +1,62 @@
+-- Test reporting and resolving drop/modified conflicts
+--
+-- this is currently not supported; we are documenting a test case
+-- that must be considered when implementing it.
+
+mtn_setup()
+
+-- Create conflict; modify and rename file in one head, drop in other
+addfile("foo", "foo base")
+commit("testbranch", "base")
+base = base_revision()
+
+writefile("foo", "foo left")
+check(mtn("mv", "foo", "bar"), 0, false, false)
+
+commit("testbranch", "left 1")
+left_1 = base_revision()
+
+revert_to(base)
+
+writefile("foo", "foo right")
+commit("testbranch", "right 1")
+right_1 = base_revision()
+
+check(mtn("drop", "foo"), 0, false, false)
+commit("testbranch", "right 2")
+right_2 = base_revision()
+
+check(mtn("conflicts", "store"), 0, nil, true)
+check(samelines("stderr",
+{"mtn: 1 conflict with supported resolutions.",
+ "mtn: stored in '_MTN/conflicts'"}))
+
+canonicalize("_MTN/conflicts")
+check(samefilestd("conflicts-1", "_MTN/conflicts"))
+
+check(mtn("conflicts", "show_first"), 0, nil, true)
+check(samelines("stderr",
+{"mtn: conflict: file 'bar'",
+ "mtn: dropped on the left",
+ "mtn: modified on the right",
+ "mtn: possible resolutions:",
+ "mtn: resolve_first drop",
+ "mtn: resolve_first keep",
+ "mtn: resolve_first user \"name\""}))
+
+check(mtn("merge", "--resolve-conflicts"), 1, nil, true)
+check(qgrep("no resolution provided for", "stderr"))
+             
+check(mtn("conflicts", "resolve_first", "drop"), 0, nil, true)
+
+canonicalize("_MTN/conflicts")
+check(samefilestd("conflicts-1-resolved", "_MTN/conflicts"))
+
+check(mtn("merge", "--resolve-conflicts"), 0, nil, true)
+check(qgrep("dropping 'bar'", "stderr"))
+
+-- FIXME: do three files at once: swap left/right, resolve keep; resolve user
+
+-- FIXME: add dropped_modified to 'show_conflicts' test (etc?)
+                           
+-- end of file
============================================================
--- /dev/null	
+++ test/func/resolve_conflicts_dropped_modified/conflicts-1	f67001a294d492e4e3c0a2196e37c2a23077e27a
@@ -0,0 +1,11 @@
+    left [92ea0d12182ab4761f8fe56a103aed9d65aff68d]
+   right [f5b4b65976746d1a2aa203ceaa67701353cc4172]
+ancestor [871b706565c414aa20f58f5f6db56778d64ece88]
+
+        conflict dropped_modified
+   ancestor_name "foo"
+ancestor_file_id [b02b6245b4b750465e12f65148d5369536b1b780]
+       left_type "dropped file"
+      right_type "modified file"
+      right_name "bar"
+   right_file_id [abd7d750f84aafa7c0b1f26540e2da28095bc082]
============================================================
--- /dev/null	
+++ test/func/resolve_conflicts_dropped_modified/conflicts-1-resolved	ecb345496807dd3a509bc7f4ef751abf55c8233a
@@ -0,0 +1,12 @@
+    left [92ea0d12182ab4761f8fe56a103aed9d65aff68d]
+   right [f5b4b65976746d1a2aa203ceaa67701353cc4172]
+ancestor [871b706565c414aa20f58f5f6db56778d64ece88]
+
+          conflict dropped_modified
+     ancestor_name "foo"
+  ancestor_file_id [b02b6245b4b750465e12f65148d5369536b1b780]
+         left_type "dropped file"
+        right_type "modified file"
+        right_name "bar"
+     right_file_id [abd7d750f84aafa7c0b1f26540e2da28095bc082]
+resolved_drop_left 

reply via email to

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