Merge branch 'jk/delete-modechange-conflict'
authorJunio C Hamano <gitster@pobox.com>
Fri, 30 Oct 2015 20:06:56 +0000 (13:06 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 30 Oct 2015 20:06:57 +0000 (13:06 -0700)
Merging a branch that removes a path and another that changes the
mode bits on the same path should have conflicted at the path, but
it didn't and silently favoured the removal.

* jk/delete-modechange-conflict:
merge: detect delete/modechange conflict
t6031: generalize for recursive and resolve strategies
t6031: move triple-rename test to t3030

1  2 
merge-recursive.c
diff --combined merge-recursive.c
index a5e74d85fd9c82534cf437d3b615d02289ecd7ba,a1ee9b7e24eda27ee16f1fe273840fc4c502f2f3..21e680a78e758eeaf03cf03a8b485095d96bcf5d
@@@ -275,20 -275,23 +275,20 @@@ struct tree *write_tree_from_memory(str
  }
  
  static int save_files_dirs(const unsigned char *sha1,
 -              const char *base, int baselen, const char *path,
 +              struct strbuf *base, const char *path,
                unsigned int mode, int stage, void *context)
  {
 -      int len = strlen(path);
 -      char *newpath = xmalloc(baselen + len + 1);
 +      int baselen = base->len;
        struct merge_options *o = context;
  
 -      memcpy(newpath, base, baselen);
 -      memcpy(newpath + baselen, path, len);
 -      newpath[baselen + len] = '\0';
 +      strbuf_addstr(base, path);
  
        if (S_ISDIR(mode))
 -              string_list_insert(&o->current_directory_set, newpath);
 +              string_list_insert(&o->current_directory_set, base->buf);
        else
 -              string_list_insert(&o->current_file_set, newpath);
 -      free(newpath);
 +              string_list_insert(&o->current_file_set, base->buf);
  
 +      strbuf_setlen(base, baselen);
        return (S_ISDIR(mode) ? READ_TREE_RECURSIVE : 0);
  }
  
@@@ -611,6 -614,7 +611,6 @@@ static char *unique_path(struct merge_o
  {
        struct strbuf newpath = STRBUF_INIT;
        int suffix = 0;
 -      struct stat st;
        size_t base_len;
  
        strbuf_addf(&newpath, "%s~", path);
        base_len = newpath.len;
        while (string_list_has_string(&o->current_file_set, newpath.buf) ||
               string_list_has_string(&o->current_directory_set, newpath.buf) ||
 -             lstat(newpath.buf, &st) == 0) {
 +             file_exists(newpath.buf)) {
                strbuf_setlen(&newpath, base_len);
                strbuf_addf(&newpath, "_%d", suffix++);
        }
  
  static int dir_in_way(const char *path, int check_working_copy)
  {
 -      int pos, pathlen = strlen(path);
 -      char *dirpath = xmalloc(pathlen + 2);
 +      int pos;
 +      struct strbuf dirpath = STRBUF_INIT;
        struct stat st;
  
 -      strcpy(dirpath, path);
 -      dirpath[pathlen] = '/';
 -      dirpath[pathlen+1] = '\0';
 +      strbuf_addstr(&dirpath, path);
 +      strbuf_addch(&dirpath, '/');
  
 -      pos = cache_name_pos(dirpath, pathlen+1);
 +      pos = cache_name_pos(dirpath.buf, dirpath.len);
  
        if (pos < 0)
                pos = -1 - pos;
        if (pos < active_nr &&
 -          !strncmp(dirpath, active_cache[pos]->name, pathlen+1)) {
 -              free(dirpath);
 +          !strncmp(dirpath.buf, active_cache[pos]->name, dirpath.len)) {
 +              strbuf_release(&dirpath);
                return 1;
        }
  
 -      free(dirpath);
 +      strbuf_release(&dirpath);
        return check_working_copy && !lstat(path, &st) && S_ISDIR(st.st_mode);
  }
  
@@@ -1530,13 -1535,17 +1530,17 @@@ static int read_sha1_strbuf(const unsig
  }
  
  static int blob_unchanged(const unsigned char *o_sha,
+                         unsigned o_mode,
                          const unsigned char *a_sha,
+                         unsigned a_mode,
                          int renormalize, const char *path)
  {
        struct strbuf o = STRBUF_INIT;
        struct strbuf a = STRBUF_INIT;
        int ret = 0; /* assume changed for safety */
  
+       if (a_mode != o_mode)
+               return 0;
        if (sha_eq(o_sha, a_sha))
                return 1;
        if (!renormalize)
@@@ -1722,8 -1731,8 +1726,8 @@@ static int process_entry(struct merge_o
        } else if (o_sha && (!a_sha || !b_sha)) {
                /* Case A: Deleted in one */
                if ((!a_sha && !b_sha) ||
-                   (!b_sha && blob_unchanged(o_sha, a_sha, normalize, path)) ||
-                   (!a_sha && blob_unchanged(o_sha, b_sha, normalize, path))) {
+                   (!b_sha && blob_unchanged(o_sha, o_mode, a_sha, a_mode, normalize, path)) ||
+                   (!a_sha && blob_unchanged(o_sha, o_mode, b_sha, b_mode, normalize, path))) {
                        /* Deleted in both or deleted in one and
                         * unchanged in the other */
                        if (a_sha)
@@@ -1856,9 -1865,6 +1860,9 @@@ int merge_trees(struct merge_options *o
                string_list_clear(re_head, 0);
                string_list_clear(entries, 1);
  
 +              free(re_merge);
 +              free(re_head);
 +              free(entries);
        }
        else
                clean = 1;
@@@ -1902,7 -1908,7 +1906,7 @@@ int merge_recursive(struct merge_option
        }
  
        if (!ca) {
 -              ca = get_merge_bases(h1, h2, 1);
 +              ca = get_merge_bases(h1, h2);
                ca = reverse_commit_list(ca);
        }