Code

Merge branch 'en/d-f-conflict-fix'
authorJunio C Hamano <gitster@pobox.com>
Tue, 31 Aug 2010 23:23:58 +0000 (16:23 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 31 Aug 2010 23:23:58 +0000 (16:23 -0700)
* en/d-f-conflict-fix:
  merge-recursive: Avoid excessive output for and reprocessing of renames
  merge-recursive: Fix multiple file rename across D/F conflict
  t6031: Add a testcase covering multiple renames across a D/F conflict
  merge-recursive: Fix typo
  Mark tests that use symlinks as needing SYMLINKS prerequisite
  t/t6035-merge-dir-to-symlink.sh: Remove TODO on passing test
  fast-import: Improve robustness when D->F changes provided in wrong order
  fast-export: Fix output order of D/F changes
  merge_recursive: Fix renames across paths below D/F conflicts
  merge-recursive: Fix D/F conflicts
  Add a rename + D/F conflict testcase
  Add additional testcases for D/F conflicts

Conflicts:
merge-recursive.c

1  2 
builtin/fast-export.c
fast-import.c
merge-recursive.c
t/t6035-merge-dir-to-symlink.sh
t/t9350-fast-export.sh

Simple merge
diff --cc fast-import.c
Simple merge
index 638076ec6ecde537b51041d1bdf4ef5a00a4b3cc,a576f9b10ec27cd46ee305301c4f68eddde1fe52..df90be44a51f3af5ebf9eb65e95b7d4fc18cc2f9
@@@ -1176,48 -1182,84 +1180,106 @@@ static int process_entry(struct merge_o
        return clean_merge;
  }
  
 -struct unpack_trees_error_msgs get_porcelain_error_msgs(void)
+ /*
+  * Per entry merge function for D/F conflicts, to be called only after
+  * all files below dir have been processed.  We do this because in the
+  * cases we can cleanly resolve D/F conflicts, process_entry() can clean
+  * out all the files below the directory for us.
+  */
+ static int process_df_entry(struct merge_options *o,
+                        const char *path, struct stage_data *entry)
+ {
+       int clean_merge = 1;
+       unsigned o_mode = entry->stages[1].mode;
+       unsigned a_mode = entry->stages[2].mode;
+       unsigned b_mode = entry->stages[3].mode;
+       unsigned char *o_sha = stage_sha(entry->stages[1].sha, o_mode);
+       unsigned char *a_sha = stage_sha(entry->stages[2].sha, a_mode);
+       unsigned char *b_sha = stage_sha(entry->stages[3].sha, b_mode);
+       const char *add_branch;
+       const char *other_branch;
+       unsigned mode;
+       const unsigned char *sha;
+       const char *conf;
+       struct stat st;
+       /* We currently only handle D->F cases */
+       assert((!o_sha && a_sha && !b_sha) ||
+              (!o_sha && !a_sha && b_sha));
+       entry->processed = 1;
+       if (a_sha) {
+               add_branch = o->branch1;
+               other_branch = o->branch2;
+               mode = a_mode;
+               sha = a_sha;
+               conf = "file/directory";
+       } else {
+               add_branch = o->branch2;
+               other_branch = o->branch1;
+               mode = b_mode;
+               sha = b_sha;
+               conf = "directory/file";
+       }
+       if (lstat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
+               const char *new_path = unique_path(o, path, add_branch);
+               clean_merge = 0;
+               output(o, 1, "CONFLICT (%s): There is a directory with name %s in %s. "
+                      "Adding %s as %s",
+                      conf, path, other_branch, path, new_path);
+               remove_file(o, 0, path, 0);
+               update_file(o, 0, sha, mode, new_path);
+       } else {
+               output(o, 2, "Adding %s", path);
+               update_file(o, 1, sha, mode, path);
+       }
+       return clean_merge;
+ }
 +void set_porcelain_error_msgs(const char **msgs, const char *cmd)
  {
 -      struct unpack_trees_error_msgs msgs = {
 -              /* would_overwrite */
 -              "Your local changes to '%s' would be overwritten by merge.  Aborting.",
 -              /* not_uptodate_file */
 -              "Your local changes to '%s' would be overwritten by merge.  Aborting.",
 -              /* not_uptodate_dir */
 -              "Updating '%s' would lose untracked files in it.  Aborting.",
 -              /* would_lose_untracked */
 -              "Untracked working tree file '%s' would be %s by merge.  Aborting",
 -              /* bind_overlap -- will not happen here */
 -              NULL,
 -      };
 -      if (advice_commit_before_merge) {
 -              msgs.would_overwrite = msgs.not_uptodate_file =
 -                      "Your local changes to '%s' would be overwritten by merge.  Aborting.\n"
 -                      "Please, commit your changes or stash them before you can merge.";
 -      }
 -      return msgs;
 +      const char *msg;
 +      char *tmp;
 +      const char *cmd2 = strcmp(cmd, "checkout") ? cmd : "switch branches";
 +      if (advice_commit_before_merge)
 +              msg = "Your local changes to the following files would be overwritten by %s:\n%%s"
 +                      "Please, commit your changes or stash them before you can %s.";
 +      else
 +              msg = "Your local changes to the following files would be overwritten by %s:\n%%s";
 +      tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen(cmd2) - 2);
 +      sprintf(tmp, msg, cmd, cmd2);
 +      msgs[ERROR_WOULD_OVERWRITE] = tmp;
 +      msgs[ERROR_NOT_UPTODATE_FILE] = tmp;
 +
 +      msgs[ERROR_NOT_UPTODATE_DIR] =
 +              "Updating the following directories would lose untracked files in it:\n%s";
 +
 +      if (advice_commit_before_merge)
 +              msg = "The following untracked working tree files would be %s by %s:\n%%s"
 +                      "Please move or remove them before you can %s.";
 +      else
 +              msg = "The following untracked working tree files would be %s by %s:\n%%s";
 +      tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("removed") + strlen(cmd2) - 4);
 +      sprintf(tmp, msg, "removed", cmd, cmd2);
 +      msgs[ERROR_WOULD_LOSE_UNTRACKED_REMOVED] = tmp;
 +      tmp = xmalloc(strlen(msg) + strlen(cmd) + strlen("overwritten") + strlen(cmd2) - 4);
 +      sprintf(tmp, msg, "overwritten", cmd, cmd2);
 +      msgs[ERROR_WOULD_LOSE_UNTRACKED_OVERWRITTEN] = tmp;
 +
 +      /*
 +       * Special case: ERROR_BIND_OVERLAP refers to a pair of paths, we
 +       * cannot easily display it as a list.
 +       */
 +      msgs[ERROR_BIND_OVERLAP] = "Entry '%s' overlaps with '%s'.  Cannot bind.";
 +
 +      msgs[ERROR_SPARSE_NOT_UPTODATE_FILE] =
 +              "Cannot update sparse checkout: the following entries are not up-to-date:\n%s";
 +      msgs[ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN] =
 +              "The following Working tree files would be overwritten by sparse checkout update:\n%s";
 +      msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
 +              "The following Working tree files would be removed by sparse checkout update:\n%s";
  }
  
  int merge_trees(struct merge_options *o,
Simple merge
Simple merge