revision: parse "git log -<count>" more carefully
[gitweb.git] / revision.c
index ac20d1aaed7b5e1a52682dfd196f87e2f94e87c7..d5e2f1a257dfd508bd2a2d1f1fde6c49a03541f5 100644 (file)
@@ -104,17 +104,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)
@@ -139,8 +134,19 @@ void mark_tree_uninteresting(struct tree *tree)
         * We don't care about the tree any more
         * after it has been marked uninteresting.
         */
-       free(tree->buffer);
-       tree->buffer = NULL;
+       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)
@@ -201,7 +207,7 @@ static void add_pending_object_with_mode(struct rev_info *revs,
                revs->no_walk = 0;
        if (revs->reflog_info && obj->type == OBJ_COMMIT) {
                struct strbuf buf = STRBUF_INIT;
-               int len = interpret_branch_name(name, &buf);
+               int len = interpret_branch_name(name, 0, &buf);
                int st;
 
                if (0 < len && name[len] && buf.len)
@@ -277,6 +283,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;
        }
 
        /*
@@ -288,7 +295,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;
                }
@@ -306,7 +312,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, "");
@@ -317,13 +323,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;
        }
@@ -1373,7 +1376,8 @@ static void prepare_show_merge(struct rev_info *revs)
                        i++;
        }
        free_pathspec(&revs->prune_data);
-       init_pathspec(&revs->prune_data, prune);
+       parse_pathspec(&revs->prune_data, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
+                      PATHSPEC_PREFER_FULL | PATHSPEC_LITERAL_PATH, "", prune);
        revs->limited = 1;
 }
 
@@ -1420,26 +1424,40 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
                }
                if (!get_sha1_committish(this, from_sha1) &&
                    !get_sha1_committish(next, sha1)) {
-                       struct commit *a, *b;
-                       struct commit_list *exclude;
-
-                       a = lookup_commit_reference(from_sha1);
-                       b = lookup_commit_reference(sha1);
-                       if (!a || !b) {
-                               if (revs->ignore_missing)
-                                       return 0;
-                               die(symmetric ?
-                                   "Invalid symmetric difference expression %s...%s" :
-                                   "Invalid revision range %s..%s",
-                                   arg, next);
-                       }
+                       struct object *a_obj, *b_obj;
 
                        if (!cant_be_filename) {
                                *dotdot = '.';
                                verify_non_filename(revs->prefix, arg);
                        }
 
-                       if (symmetric) {
+                       a_obj = parse_object(from_sha1);
+                       b_obj = parse_object(sha1);
+                       if (!a_obj || !b_obj) {
+                       missing:
+                               if (revs->ignore_missing)
+                                       return 0;
+                               die(symmetric
+                                   ? "Invalid symmetric difference expression %s"
+                                   : "Invalid revision range %s", arg);
+                       }
+
+                       if (!symmetric) {
+                               /* just A..B */
+                               a_flags = flags_exclude;
+                       } else {
+                               /* A...B -- find merge bases between the two */
+                               struct commit *a, *b;
+                               struct commit_list *exclude;
+
+                               a = (a_obj->type == OBJ_COMMIT
+                                    ? (struct commit *)a_obj
+                                    : lookup_commit_reference(a_obj->sha1));
+                               b = (b_obj->type == OBJ_COMMIT
+                                    ? (struct commit *)b_obj
+                                    : lookup_commit_reference(b_obj->sha1));
+                               if (!a || !b)
+                                       goto missing;
                                exclude = get_merge_bases(a, b, 1);
                                add_rev_cmdline_list(revs, exclude,
                                                     REV_CMD_MERGE_BASE,
@@ -1447,17 +1465,18 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
                                add_pending_commit_list(revs, exclude,
                                                        flags_exclude);
                                free_commit_list(exclude);
+
                                a_flags = flags | SYMMETRIC_LEFT;
-                       } else
-                               a_flags = flags_exclude;
-                       a->object.flags |= a_flags;
-                       b->object.flags |= flags;
-                       add_rev_cmdline(revs, &a->object, this,
+                       }
+
+                       a_obj->flags |= a_flags;
+                       b_obj->flags |= flags;
+                       add_rev_cmdline(revs, a_obj, this,
                                        REV_CMD_LEFT, a_flags);
-                       add_rev_cmdline(revs, &b->object, next,
+                       add_rev_cmdline(revs, b_obj, next,
                                        REV_CMD_RIGHT, flags);
-                       add_pending_object(revs, &a->object, this);
-                       add_pending_object(revs, &b->object, next);
+                       add_pending_object(revs, a_obj, this);
+                       add_pending_object(revs, b_obj, next);
                        return 0;
                }
                *dotdot = '.';
@@ -1504,7 +1523,7 @@ struct cmdline_pathspec {
 static void append_prune_data(struct cmdline_pathspec *prune, const char **av)
 {
        while (*av) {
-               ALLOC_GROW(prune->path, prune->nr+1, prune->alloc);
+               ALLOC_GROW(prune->path, prune->nr + 1, prune->alloc);
                prune->path[prune->nr++] = *(av++);
        }
 }
@@ -1516,7 +1535,7 @@ static void read_pathspec_from_stdin(struct rev_info *revs, struct strbuf *sb,
                int len = sb->len;
                if (len && sb->buf[len - 1] == '\n')
                        sb->buf[--len] = '\0';
-               ALLOC_GROW(prune->path, prune->nr+1, prune->alloc);
+               ALLOC_GROW(prune->path, prune->nr + 1, prune->alloc);
                prune->path[prune->nr++] = xstrdup(sb->buf);
        }
 }
@@ -1593,8 +1612,10 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->skip_count = atoi(optarg);
                return argcount;
        } else if ((*arg == '-') && isdigit(arg[1])) {
-       /* accept -<digit>, like traditional "head" */
-               revs->max_count = atoi(arg + 1);
+               /* accept -<digit>, like traditional "head" */
+               if (strtol_i(arg + 1, 10, &revs->max_count) < 0 ||
+                   revs->max_count < 0)
+                       die("'%s': not a non-negative integer", arg + 1);
                revs->no_walk = 0;
        } else if (!strcmp(arg, "-n")) {
                if (argc <= 1)
@@ -2119,10 +2140,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                 *      call init_pathspec() to set revs->prune_data here.
                 * }
                 */
-               ALLOC_GROW(prune_data.path, prune_data.nr+1, prune_data.alloc);
+               ALLOC_GROW(prune_data.path, prune_data.nr + 1, prune_data.alloc);
                prune_data.path[prune_data.nr++] = NULL;
-               init_pathspec(&revs->prune_data,
-                             get_pathspec(revs->prefix, prune_data.path));
+               parse_pathspec(&revs->prune_data, 0, 0,
+                              revs->prefix, prune_data.path);
        }
 
        if (revs->def == NULL)
@@ -2155,12 +2176,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                revs->limited = 1;
 
        if (revs->prune_data.nr) {
-               diff_tree_setup_paths(revs->prune_data.raw, &revs->pruning);
+               copy_pathspec(&revs->pruning.pathspec, &revs->prune_data);
                /* Can't prune commits with rename following: the paths change.. */
                if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
                        revs->prune = 1;
                if (!revs->full_diff)
-                       diff_tree_setup_paths(revs->prune_data.raw, &revs->diffopt);
+                       copy_pathspec(&revs->diffopt.pathspec,
+                                     &revs->prune_data);
        }
        if (revs->combine_merges)
                revs->ignore_merges = 0;
@@ -2971,7 +2993,7 @@ static struct commit *get_revision_internal(struct rev_info *revs)
        if (revs->max_count) {
                c = get_revision_1(revs);
                if (c) {
-                       while (0 < revs->skip_count) {
+                       while (revs->skip_count > 0) {
                                revs->skip_count--;
                                c = get_revision_1(revs);
                                if (!c)
@@ -2986,9 +3008,8 @@ static struct commit *get_revision_internal(struct rev_info *revs)
        if (c)
                c->object.flags |= SHOWN;
 
-       if (!revs->boundary) {
+       if (!revs->boundary)
                return c;
-       }
 
        if (!c) {
                /*
@@ -3034,9 +3055,8 @@ struct commit *get_revision(struct rev_info *revs)
 
        if (revs->reverse) {
                reversed = NULL;
-               while ((c = get_revision_internal(revs))) {
+               while ((c = get_revision_internal(revs)))
                        commit_list_insert(c, &reversed);
-               }
                revs->commits = reversed;
                revs->reverse = 0;
                revs->reverse_output_stage = 1;