Merge branch 'jc/check-attr-honor-working-tree'
[gitweb.git] / revision.c
index a8adb3fc8891a73fb4017d18b00020bca790c766..bd027bc0152a59ac0c4584ab752ff70bee5abbfa 100644 (file)
@@ -16,6 +16,7 @@
 #include "line-log.h"
 #include "mailmap.h"
 #include "commit-slab.h"
+#include "dir.h"
 
 volatile show_early_output_fn_t show_early_output;
 
@@ -104,17 +105,12 @@ static void mark_blob_uninteresting(struct blob *blob)
        blob->object.flags |= UNINTERESTING;
 }
 
-void mark_tree_uninteresting(struct tree *tree)
+static void mark_tree_contents_uninteresting(struct tree *tree)
 {
        struct tree_desc desc;
        struct name_entry entry;
        struct object *obj = &tree->object;
 
-       if (!tree)
-               return;
-       if (obj->flags & UNINTERESTING)
-               return;
-       obj->flags |= UNINTERESTING;
        if (!has_sha1_file(obj->sha1))
                return;
        if (parse_tree(tree) < 0)
@@ -142,6 +138,18 @@ void mark_tree_uninteresting(struct tree *tree)
        free_tree_buffer(tree);
 }
 
+void mark_tree_uninteresting(struct tree *tree)
+{
+       struct object *obj = &tree->object;
+
+       if (!tree)
+               return;
+       if (obj->flags & UNINTERESTING)
+               return;
+       obj->flags |= UNINTERESTING;
+       mark_tree_contents_uninteresting(tree);
+}
+
 void mark_parents_uninteresting(struct commit *commit)
 {
        struct commit_list *parents = NULL, *l;
@@ -276,6 +284,7 @@ static struct commit *handle_commit(struct rev_info *revs,
                                return NULL;
                        die("bad object %s", sha1_to_hex(tag->tagged->sha1));
                }
+               object->flags |= flags;
        }
 
        /*
@@ -287,7 +296,6 @@ static struct commit *handle_commit(struct rev_info *revs,
                if (parse_commit(commit) < 0)
                        die("unable to parse commit %s", name);
                if (flags & UNINTERESTING) {
-                       commit->object.flags |= UNINTERESTING;
                        mark_parents_uninteresting(commit);
                        revs->limited = 1;
                }
@@ -305,7 +313,7 @@ static struct commit *handle_commit(struct rev_info *revs,
                if (!revs->tree_objects)
                        return NULL;
                if (flags & UNINTERESTING) {
-                       mark_tree_uninteresting(tree);
+                       mark_tree_contents_uninteresting(tree);
                        return NULL;
                }
                add_pending_object(revs, object, "");
@@ -316,13 +324,10 @@ static struct commit *handle_commit(struct rev_info *revs,
         * Blob object? You know the drill by now..
         */
        if (object->type == OBJ_BLOB) {
-               struct blob *blob = (struct blob *)object;
                if (!revs->blob_objects)
                        return NULL;
-               if (flags & UNINTERESTING) {
-                       mark_blob_uninteresting(blob);
+               if (flags & UNINTERESTING)
                        return NULL;
-               }
                add_pending_object(revs, object, "");
                return NULL;
        }
@@ -492,24 +497,14 @@ static int rev_compare_tree(struct rev_info *revs,
 static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
 {
        int retval;
-       void *tree;
-       unsigned long size;
-       struct tree_desc empty, real;
        struct tree *t1 = commit->tree;
 
        if (!t1)
                return 0;
 
-       tree = read_object_with_reference(t1->object.sha1, tree_type, &size, NULL);
-       if (!tree)
-               return 0;
-       init_tree_desc(&real, tree, size);
-       init_tree_desc(&empty, "", 0);
-
        tree_difference = REV_TREE_SAME;
        DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
-       retval = diff_tree(&empty, &real, "", &revs->pruning);
-       free(tree);
+       retval = diff_tree_sha1(NULL, t1->object.sha1, "", &revs->pruning);
 
        return retval >= 0 && (tree_difference == REV_TREE_SAME);
 }
@@ -779,6 +774,10 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit,
                return 0;
        commit->object.flags |= ADDED;
 
+       if (revs->include_check &&
+           !revs->include_check(commit, revs->include_check_data))
+               return 0;
+
        /*
         * If the commit is uninteresting, don't try to
         * prune parents - we want the maximal uninteresting
@@ -1180,11 +1179,28 @@ struct all_refs_cb {
        const char *name_for_errormsg;
 };
 
+int ref_excluded(struct string_list *ref_excludes, const char *path)
+{
+       struct string_list_item *item;
+
+       if (!ref_excludes)
+               return 0;
+       for_each_string_list_item(item, ref_excludes) {
+               if (!fnmatch(item->string, path, 0))
+                       return 1;
+       }
+       return 0;
+}
+
 static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
        struct all_refs_cb *cb = cb_data;
-       struct object *object = get_reference(cb->all_revs, path, sha1,
-                                             cb->all_flags);
+       struct object *object;
+
+       if (ref_excluded(cb->all_revs->ref_excludes, path))
+           return 0;
+
+       object = get_reference(cb->all_revs, path, sha1, cb->all_flags);
        add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
        add_pending_sha1(cb->all_revs, path, sha1, cb->all_flags);
        return 0;
@@ -1197,6 +1213,24 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
        cb->all_flags = flags;
 }
 
+void clear_ref_exclusion(struct string_list **ref_excludes_p)
+{
+       if (*ref_excludes_p) {
+               string_list_clear(*ref_excludes_p, 0);
+               free(*ref_excludes_p);
+       }
+       *ref_excludes_p = NULL;
+}
+
+void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude)
+{
+       if (!*ref_excludes_p) {
+               *ref_excludes_p = xcalloc(1, sizeof(**ref_excludes_p));
+               (*ref_excludes_p)->strdup_strings = 1;
+       }
+       string_list_append(*ref_excludes_p, exclude);
+}
+
 static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
                int (*for_each)(const char *, each_ref_fn, void *))
 {
@@ -1361,7 +1395,7 @@ static void prepare_show_merge(struct rev_info *revs)
                const struct cache_entry *ce = active_cache[i];
                if (!ce_stage(ce))
                        continue;
-               if (ce_path_match(ce, &revs->prune_data)) {
+               if (ce_path_match(ce, &revs->prune_data, NULL)) {
                        prune_num++;
                        prune = xrealloc(prune, sizeof(*prune) * prune_num);
                        prune[prune_num-2] = ce->name;
@@ -1592,9 +1626,9 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
            !strcmp(arg, "--tags") || !strcmp(arg, "--remotes") ||
            !strcmp(arg, "--reflog") || !strcmp(arg, "--not") ||
            !strcmp(arg, "--no-walk") || !strcmp(arg, "--do-walk") ||
-           !strcmp(arg, "--bisect") || !prefixcmp(arg, "--glob=") ||
-           !prefixcmp(arg, "--branches=") || !prefixcmp(arg, "--tags=") ||
-           !prefixcmp(arg, "--remotes=") || !prefixcmp(arg, "--no-walk="))
+           !strcmp(arg, "--bisect") || starts_with(arg, "--glob=") ||
+           starts_with(arg, "--branches=") || starts_with(arg, "--tags=") ||
+           starts_with(arg, "--remotes=") || starts_with(arg, "--no-walk="))
        {
                unkv[(*unkc)++] = arg;
                return 1;
@@ -1617,7 +1651,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->max_count = atoi(argv[1]);
                revs->no_walk = 0;
                return 2;
-       } else if (!prefixcmp(arg, "-n")) {
+       } else if (starts_with(arg, "-n")) {
                revs->max_count = atoi(arg + 2);
                revs->no_walk = 0;
        } else if ((argcount = parse_long_opt("max-age", argv, &optarg))) {
@@ -1677,7 +1711,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
        } else if (!strcmp(arg, "--author-date-order")) {
                revs->sort_order = REV_SORT_BY_AUTHOR_DATE;
                revs->topo_order = 1;
-       } else if (!prefixcmp(arg, "--early-output")) {
+       } else if (starts_with(arg, "--early-output")) {
                int count = 100;
                switch (arg[14]) {
                case '=':
@@ -1702,13 +1736,13 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->min_parents = 2;
        } else if (!strcmp(arg, "--no-merges")) {
                revs->max_parents = 1;
-       } else if (!prefixcmp(arg, "--min-parents=")) {
+       } else if (starts_with(arg, "--min-parents=")) {
                revs->min_parents = atoi(arg+14);
-       } else if (!prefixcmp(arg, "--no-min-parents")) {
+       } else if (starts_with(arg, "--no-min-parents")) {
                revs->min_parents = 0;
-       } else if (!prefixcmp(arg, "--max-parents=")) {
+       } else if (starts_with(arg, "--max-parents=")) {
                revs->max_parents = atoi(arg+14);
-       } else if (!prefixcmp(arg, "--no-max-parents")) {
+       } else if (starts_with(arg, "--no-max-parents")) {
                revs->max_parents = -1;
        } else if (!strcmp(arg, "--boundary")) {
                revs->boundary = 1;
@@ -1758,7 +1792,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->verify_objects = 1;
        } else if (!strcmp(arg, "--unpacked")) {
                revs->unpacked = 1;
-       } else if (!prefixcmp(arg, "--unpacked=")) {
+       } else if (starts_with(arg, "--unpacked=")) {
                die("--unpacked=<packfile> no longer supported.");
        } else if (!strcmp(arg, "-r")) {
                revs->diff = 1;
@@ -1783,7 +1817,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->verbose_header = 1;
                revs->pretty_given = 1;
                get_commit_format(arg+8, revs);
-       } else if (!prefixcmp(arg, "--pretty=") || !prefixcmp(arg, "--format=")) {
+       } else if (starts_with(arg, "--pretty=") || starts_with(arg, "--format=")) {
                /*
                 * Detached form ("--pretty X" as opposed to "--pretty=X")
                 * not allowed, since the argument is optional.
@@ -1797,12 +1831,12 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->notes_opt.use_default_notes = 1;
        } else if (!strcmp(arg, "--show-signature")) {
                revs->show_signature = 1;
-       } else if (!prefixcmp(arg, "--show-notes=") ||
-                  !prefixcmp(arg, "--notes=")) {
+       } else if (starts_with(arg, "--show-notes=") ||
+                  starts_with(arg, "--notes=")) {
                struct strbuf buf = STRBUF_INIT;
                revs->show_notes = 1;
                revs->show_notes_given = 1;
-               if (!prefixcmp(arg, "--show-notes")) {
+               if (starts_with(arg, "--show-notes")) {
                        if (revs->notes_opt.use_default_notes < 0)
                                revs->notes_opt.use_default_notes = 1;
                        strbuf_addstr(&buf, arg+13);
@@ -1845,7 +1879,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->abbrev = 0;
        } else if (!strcmp(arg, "--abbrev")) {
                revs->abbrev = DEFAULT_ABBREV;
-       } else if (!prefixcmp(arg, "--abbrev=")) {
+       } else if (starts_with(arg, "--abbrev=")) {
                revs->abbrev = strtoul(arg + 9, NULL, 10);
                if (revs->abbrev < MINIMUM_ABBREV)
                        revs->abbrev = MINIMUM_ABBREV;
@@ -1969,40 +2003,51 @@ static int handle_revision_pseudo_opt(const char *submodule,
        if (!strcmp(arg, "--all")) {
                handle_refs(submodule, revs, *flags, for_each_ref_submodule);
                handle_refs(submodule, revs, *flags, head_ref_submodule);
+               clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--branches")) {
                handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);
+               clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--bisect")) {
                handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);
                handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref);
                revs->bisect = 1;
        } else if (!strcmp(arg, "--tags")) {
                handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);
+               clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--remotes")) {
                handle_refs(submodule, revs, *flags, for_each_remote_ref_submodule);
+               clear_ref_exclusion(&revs->ref_excludes);
        } else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref(handle_one_ref, optarg, &cb);
+               clear_ref_exclusion(&revs->ref_excludes);
+               return argcount;
+       } else if ((argcount = parse_long_opt("exclude", argv, &optarg))) {
+               add_ref_exclusion(&revs->ref_excludes, optarg);
                return argcount;
-       } else if (!prefixcmp(arg, "--branches=")) {
+       } else if (starts_with(arg, "--branches=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb);
-       } else if (!prefixcmp(arg, "--tags=")) {
+               clear_ref_exclusion(&revs->ref_excludes);
+       } else if (starts_with(arg, "--tags=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb);
-       } else if (!prefixcmp(arg, "--remotes=")) {
+               clear_ref_exclusion(&revs->ref_excludes);
+       } else if (starts_with(arg, "--remotes=")) {
                struct all_refs_cb cb;
                init_all_refs_cb(&cb, revs, *flags);
                for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
+               clear_ref_exclusion(&revs->ref_excludes);
        } else if (!strcmp(arg, "--reflog")) {
                handle_reflog(revs, *flags);
        } else if (!strcmp(arg, "--not")) {
                *flags ^= UNINTERESTING | BOTTOM;
        } else if (!strcmp(arg, "--no-walk")) {
                revs->no_walk = REVISION_WALK_NO_WALK_SORTED;
-       } else if (!prefixcmp(arg, "--no-walk=")) {
+       } else if (starts_with(arg, "--no-walk=")) {
                /*
                 * Detached form ("--no-walk X" as opposed to "--no-walk=X")
                 * not allowed, since the argument is optional.