vcs-svn: improve reporting of input errors
[gitweb.git] / tree-diff.c
index 26bdbdd2bfea5eab99b9f1c38936efe56f1ab45a..12c9a88884ec3bb70a1744e44235c578a44e08e6 100644 (file)
@@ -15,6 +15,15 @@ static char *malloc_base(const char *base, int baselen, const char *path, int pa
        return newbase;
 }
 
+static char *malloc_fullname(const char *base, int baselen, const char *path, int pathlen)
+{
+       char *fullname = xmalloc(baselen + pathlen + 1);
+       memcpy(fullname, base, baselen);
+       memcpy(fullname + baselen, path, pathlen);
+       fullname[baselen + pathlen] = 0;
+       return fullname;
+}
+
 static void show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc,
                       const char *base, int baselen);
 
@@ -24,6 +33,7 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
        const char *path1, *path2;
        const unsigned char *sha1, *sha2;
        int cmp, pathlen1, pathlen2;
+       char *fullname;
 
        sha1 = tree_entry_extract(t1, &path1, &mode1);
        sha2 = tree_entry_extract(t2, &path2, &mode2);
@@ -39,7 +49,7 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
                show_entry(opt, "+", t2, base, baselen);
                return 1;
        }
-       if (!opt->find_copies_harder && !hashcmp(sha1, sha2) && mode1 == mode2)
+       if (!DIFF_OPT_TST(opt, FIND_COPIES_HARDER) && !hashcmp(sha1, sha2) && mode1 == mode2)
                return 0;
 
        /*
@@ -52,24 +62,31 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
                return 0;
        }
 
-       if (opt->recursive && S_ISDIR(mode1)) {
+       if (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode1)) {
                int retval;
                char *newbase = malloc_base(base, baselen, path1, pathlen1);
-               if (opt->tree_in_recursive)
+               if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) {
+                       newbase[baselen + pathlen1] = 0;
                        opt->change(opt, mode1, mode2,
-                                   sha1, sha2, base, path1);
+                                   sha1, sha2, newbase, 0, 0);
+                       newbase[baselen + pathlen1] = '/';
+               }
                retval = diff_tree_sha1(sha1, sha2, newbase, opt);
                free(newbase);
                return retval;
        }
 
-       opt->change(opt, mode1, mode2, sha1, sha2, base, path1);
+       fullname = malloc_fullname(base, baselen, path1, pathlen1);
+       opt->change(opt, mode1, mode2, sha1, sha2, fullname, 0, 0);
+       free(fullname);
        return 0;
 }
 
 /*
  * Is a tree entry interesting given the pathspec we have?
  *
+ * Pre-condition: baselen == 0 || base[baselen-1] == '/'
+ *
  * Return:
  *  - 2 for "yes, and all subsequent entries will be"
  *  - 1 for yes
@@ -86,7 +103,7 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int
        int never_interesting = -1;
 
        if (!opt->nr_paths)
-               return 1;
+               return 2;
 
        sha1 = tree_entry_extract(desc, &path, &mode);
 
@@ -103,10 +120,16 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int
                                continue;
 
                        /*
-                        * The base is a subdirectory of a path which
-                        * was specified, so all of them are interesting.
+                        * If the base is a subdirectory of a path which
+                        * was specified, all of them are interesting.
                         */
-                       return 2;
+                       if (!matchlen ||
+                           base[matchlen] == '/' ||
+                           match[matchlen - 1] == '/')
+                               return 2;
+
+                       /* Just a random prefix match */
+                       continue;
                }
 
                /* Does the base match? */
@@ -205,10 +228,10 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
        unsigned mode;
        const char *path;
        const unsigned char *sha1 = tree_entry_extract(desc, &path, &mode);
+       int pathlen = tree_entry_len(path, sha1);
 
-       if (opt->recursive && S_ISDIR(mode)) {
+       if (DIFF_OPT_TST(opt, RECURSIVE) && S_ISDIR(mode)) {
                enum object_type type;
-               int pathlen = tree_entry_len(path, sha1);
                char *newbase = malloc_base(base, baselen, path, pathlen);
                struct tree_desc inner;
                void *tree;
@@ -218,29 +241,30 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
                if (!tree || type != OBJ_TREE)
                        die("corrupt tree sha %s", sha1_to_hex(sha1));
 
+               if (DIFF_OPT_TST(opt, TREE_IN_RECURSIVE)) {
+                       newbase[baselen + pathlen] = 0;
+                       opt->add_remove(opt, *prefix, mode, sha1, newbase, 0);
+                       newbase[baselen + pathlen] = '/';
+               }
+
                init_tree_desc(&inner, tree, size);
                show_tree(opt, prefix, &inner, newbase, baselen + 1 + pathlen);
 
                free(tree);
                free(newbase);
        } else {
-               opt->add_remove(opt, prefix[0], mode, sha1, base, path);
+               char *fullname = malloc_fullname(base, baselen, path, pathlen);
+               opt->add_remove(opt, prefix[0], mode, sha1, fullname, 0);
+               free(fullname);
        }
 }
 
-static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt)
+static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt, int *all_interesting)
 {
-       int all_interesting = 0;
        while (t->size) {
-               int show;
-
-               if (all_interesting)
-                       show = 1;
-               else {
-                       show = tree_entry_interesting(t, base, baselen, opt);
-                       if (show == 2)
-                               all_interesting = 1;
-               }
+               int show = tree_entry_interesting(t, base, baselen, opt);
+               if (show == 2)
+                       *all_interesting = 1;
                if (!show) {
                        update_tree_entry(t);
                        continue;
@@ -255,13 +279,20 @@ static void skip_uninteresting(struct tree_desc *t, const char *base, int basele
 int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
 {
        int baselen = strlen(base);
+       int all_t1_interesting = 0;
+       int all_t2_interesting = 0;
 
        for (;;) {
-               if (opt->quiet && opt->has_changes)
+               if (DIFF_OPT_TST(opt, QUICK) &&
+                   DIFF_OPT_TST(opt, HAS_CHANGES))
                        break;
                if (opt->nr_paths) {
-                       skip_uninteresting(t1, base, baselen, opt);
-                       skip_uninteresting(t2, base, baselen, opt);
+                       if (!all_t1_interesting)
+                               skip_uninteresting(t1, base, baselen, opt,
+                                                  &all_t1_interesting);
+                       if (!all_t2_interesting)
+                               skip_uninteresting(t2, base, baselen, opt,
+                                                  &all_t2_interesting);
                }
                if (!t1->size) {
                        if (!t2->size)
@@ -286,7 +317,7 @@ int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, stru
                        update_tree_entry(t2);
                        continue;
                }
-               die("git-diff-tree: internal error");
+               die("git diff-tree: internal error");
        }
        return 0;
 }
@@ -315,18 +346,21 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
        q->nr = 0;
 
        diff_setup(&diff_opts);
-       diff_opts.recursive = 1;
-       diff_opts.detect_rename = DIFF_DETECT_RENAME;
+       DIFF_OPT_SET(&diff_opts, RECURSIVE);
+       DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_opts.single_follow = opt->paths[0];
+       diff_opts.break_opt = opt->break_opt;
        paths[0] = NULL;
        diff_tree_setup_paths(paths, &diff_opts);
        if (diff_setup_done(&diff_opts) < 0)
                die("unable to set up diff options to follow renames");
        diff_tree(t1, t2, base, &diff_opts);
        diffcore_std(&diff_opts);
+       diff_tree_release_paths(&diff_opts);
 
        /* Go through the new set of filepairing, and see if we find a more interesting one */
+       opt->found_follow = 0;
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
 
@@ -341,14 +375,25 @@ static void try_to_follow_renames(struct tree_desc *t1, struct tree_desc *t2, co
                        choice = p;
 
                        /* Update the path we use from now on.. */
+                       diff_tree_release_paths(opt);
                        opt->paths[0] = xstrdup(p->one->path);
                        diff_tree_setup_paths(opt->paths, opt);
+
+                       /*
+                        * The caller expects us to return a set of vanilla
+                        * filepairs to let a later call to diffcore_std()
+                        * it makes to sort the renames out (among other
+                        * things), but we already have found renames
+                        * ourselves; signal diffcore_std() not to muck with
+                        * rename information.
+                        */
+                       opt->found_follow = 1;
                        break;
                }
        }
 
        /*
-        * Then, discard all the non-relevane file pairs...
+        * Then, discard all the non-relevant file pairs...
         */
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
@@ -379,7 +424,7 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha
        init_tree_desc(&t1, tree1, size1);
        init_tree_desc(&t2, tree2, size2);
        retval = diff_tree(&t1, &t2, base, opt);
-       if (opt->follow_renames && diff_might_be_rename()) {
+       if (!*base && DIFF_OPT_TST(opt, FOLLOW_RENAMES) && diff_might_be_rename()) {
                init_tree_desc(&t1, tree1, size1);
                init_tree_desc(&t2, tree2, size2);
                try_to_follow_renames(&t1, &t2, base, opt);