Documentation: bisect: add some titles to some paragraphs.
[gitweb.git] / revision.c
index 56819f8220b379575fd0e68d970acbffd9ed0249..adc381c268b6f336ab7820c464fafa39629c8e28 100644 (file)
@@ -6,8 +6,8 @@
 #include "diff.h"
 #include "refs.h"
 #include "revision.h"
-#include <regex.h>
 #include "grep.h"
+#include "reflog-walk.h"
 
 static char *path_name(struct name_path *path, const char *name)
 {
@@ -62,8 +62,7 @@ void mark_tree_uninteresting(struct tree *tree)
        if (parse_tree(tree) < 0)
                die("bad tree %s", sha1_to_hex(obj->sha1));
 
-       desc.buf = tree->buffer;
-       desc.size = tree->size;
+       init_tree_desc(&desc, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
                if (S_ISDIR(entry.mode))
                        mark_tree_uninteresting(lookup_tree(entry.sha1));
@@ -116,7 +115,12 @@ void mark_parents_uninteresting(struct commit *commit)
 
 void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
 {
+       if (revs->no_walk && (obj->flags & UNINTERESTING))
+               die("object ranges do not make sense when not walking revisions");
        add_object_array(obj, name, &revs->pending);
+       if (revs->reflog_info && obj->type == OBJ_COMMIT)
+               add_reflog_for_walk(revs->reflog_info,
+                               (struct commit *)obj, name);
 }
 
 static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
@@ -208,6 +212,13 @@ static int everybody_uninteresting(struct commit_list *orig)
        return 1;
 }
 
+/*
+ * The goal is to get REV_TREE_NEW as the result only if the
+ * diff consists of all '+' (and no other changes), and
+ * REV_TREE_DIFFERENT otherwise (of course if the trees are
+ * the same we want REV_TREE_SAME).  That means that once we
+ * get to REV_TREE_DIFFERENT, we do not have to look any further.
+ */
 static int tree_difference = REV_TREE_SAME;
 
 static void file_add_remove(struct diff_options *options,
@@ -231,6 +242,8 @@ static void file_add_remove(struct diff_options *options,
                diff = REV_TREE_NEW;
        }
        tree_difference = diff;
+       if (tree_difference == REV_TREE_DIFFERENT)
+               options->has_changes = 1;
 }
 
 static void file_change(struct diff_options *options,
@@ -240,6 +253,7 @@ static void file_change(struct diff_options *options,
                 const char *base, const char *path)
 {
        tree_difference = REV_TREE_DIFFERENT;
+       options->has_changes = 1;
 }
 
 int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
@@ -249,6 +263,7 @@ int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
        if (!t2)
                return REV_TREE_DIFFERENT;
        tree_difference = REV_TREE_SAME;
+       revs->pruning.has_changes = 0;
        if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "",
                           &revs->pruning) < 0)
                return REV_TREE_DIFFERENT;
@@ -259,24 +274,24 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
 {
        int retval;
        void *tree;
+       unsigned long size;
        struct tree_desc empty, real;
 
        if (!t1)
                return 0;
 
-       tree = read_object_with_reference(t1->object.sha1, tree_type, &real.size, NULL);
+       tree = read_object_with_reference(t1->object.sha1, tree_type, &size, NULL);
        if (!tree)
                return 0;
-       real.buf = tree;
+       init_tree_desc(&real, tree, size);
+       init_tree_desc(&empty, "", 0);
 
-       empty.buf = "";
-       empty.size = 0;
-
-       tree_difference = 0;
+       tree_difference = REV_TREE_SAME;
+       revs->pruning.has_changes = 0;
        retval = diff_tree(&empty, &real, "", &revs->pruning);
        free(tree);
 
-       return retval >= 0 && !tree_difference;
+       return retval >= 0 && (tree_difference == REV_TREE_SAME);
 }
 
 static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
@@ -432,54 +447,74 @@ static void limit_list(struct rev_info *revs)
                        continue;
                p = &commit_list_insert(commit, p)->next;
        }
-       if (revs->boundary) {
-               /* mark the ones that are on the result list first */
-               for (list = newlist; list; list = list->next) {
-                       struct commit *commit = list->item;
-                       commit->object.flags |= TMP_MARK;
-               }
-               for (list = newlist; list; list = list->next) {
-                       struct commit *commit = list->item;
-                       struct object *obj = &commit->object;
-                       struct commit_list *parent;
-                       if (obj->flags & UNINTERESTING)
-                               continue;
-                       for (parent = commit->parents;
-                            parent;
-                            parent = parent->next) {
-                               struct commit *pcommit = parent->item;
-                               if (!(pcommit->object.flags & UNINTERESTING))
-                                       continue;
-                               pcommit->object.flags |= BOUNDARY;
-                               if (pcommit->object.flags & TMP_MARK)
-                                       continue;
-                               pcommit->object.flags |= TMP_MARK;
-                               p = &commit_list_insert(pcommit, p)->next;
-                       }
-               }
-               for (list = newlist; list; list = list->next) {
-                       struct commit *commit = list->item;
-                       commit->object.flags &= ~TMP_MARK;
-               }
-       }
        revs->commits = newlist;
 }
 
-static int all_flags;
-static struct rev_info *all_revs;
+struct all_refs_cb {
+       int all_flags;
+       int warned_bad_reflog;
+       struct rev_info *all_revs;
+       const char *name_for_errormsg;
+};
 
 static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
 {
-       struct object *object = get_reference(all_revs, path, sha1, all_flags);
-       add_pending_object(all_revs, object, "");
+       struct all_refs_cb *cb = cb_data;
+       struct object *object = get_reference(cb->all_revs, path, sha1,
+                                             cb->all_flags);
+       add_pending_object(cb->all_revs, object, path);
        return 0;
 }
 
 static void handle_all(struct rev_info *revs, unsigned flags)
 {
-       all_revs = revs;
-       all_flags = flags;
-       for_each_ref(handle_one_ref, NULL);
+       struct all_refs_cb cb;
+       cb.all_revs = revs;
+       cb.all_flags = flags;
+       for_each_ref(handle_one_ref, &cb);
+}
+
+static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
+{
+       struct all_refs_cb *cb = cb_data;
+       if (!is_null_sha1(sha1)) {
+               struct object *o = parse_object(sha1);
+               if (o) {
+                       o->flags |= cb->all_flags;
+                       add_pending_object(cb->all_revs, o, "");
+               }
+               else if (!cb->warned_bad_reflog) {
+                       warn("reflog of '%s' references pruned commits",
+                               cb->name_for_errormsg);
+                       cb->warned_bad_reflog = 1;
+               }
+       }
+}
+
+static int handle_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+               const char *email, unsigned long timestamp, int tz,
+               const char *message, void *cb_data)
+{
+       handle_one_reflog_commit(osha1, cb_data);
+       handle_one_reflog_commit(nsha1, cb_data);
+       return 0;
+}
+
+static int handle_one_reflog(const char *path, const unsigned char *sha1, int flag, void *cb_data)
+{
+       struct all_refs_cb *cb = cb_data;
+       cb->warned_bad_reflog = 0;
+       cb->name_for_errormsg = path;
+       for_each_reflog_ent(path, handle_one_reflog_ent, cb_data);
+       return 0;
+}
+
+static void handle_reflog(struct rev_info *revs, unsigned flags)
+{
+       struct all_refs_cb cb;
+       cb.all_revs = revs;
+       cb.all_flags = flags;
+       for_each_reflog(handle_one_reflog, &cb);
 }
 
 static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
@@ -520,6 +555,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
        revs->pruning.recursive = 1;
+       revs->pruning.quiet = 1;
        revs->pruning.add_remove = file_add_remove;
        revs->pruning.change = file_change;
        revs->lifo = 1;
@@ -527,6 +563,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        revs->prefix = prefix;
        revs->max_age = -1;
        revs->min_age = -1;
+       revs->skip_count = -1;
        revs->max_count = -1;
 
        revs->prune_fn = NULL;
@@ -759,10 +796,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                const char *arg = argv[i];
                if (*arg == '-') {
                        int opts;
-                       if (!strncmp(arg, "--max-count=", 12)) {
+                       if (!prefixcmp(arg, "--max-count=")) {
                                revs->max_count = atoi(arg + 12);
                                continue;
                        }
+                       if (!prefixcmp(arg, "--skip=")) {
+                               revs->skip_count = atoi(arg + 7);
+                               continue;
+                       }
                        /* accept -<digit>, like traditional "head" */
                        if ((*arg == '-') && isdigit(arg[1])) {
                                revs->max_count = atoi(arg + 1);
@@ -774,31 +815,31 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->max_count = atoi(argv[++i]);
                                continue;
                        }
-                       if (!strncmp(arg,"-n",2)) {
+                       if (!prefixcmp(arg, "-n")) {
                                revs->max_count = atoi(arg + 2);
                                continue;
                        }
-                       if (!strncmp(arg, "--max-age=", 10)) {
+                       if (!prefixcmp(arg, "--max-age=")) {
                                revs->max_age = atoi(arg + 10);
                                continue;
                        }
-                       if (!strncmp(arg, "--since=", 8)) {
+                       if (!prefixcmp(arg, "--since=")) {
                                revs->max_age = approxidate(arg + 8);
                                continue;
                        }
-                       if (!strncmp(arg, "--after=", 8)) {
+                       if (!prefixcmp(arg, "--after=")) {
                                revs->max_age = approxidate(arg + 8);
                                continue;
                        }
-                       if (!strncmp(arg, "--min-age=", 10)) {
+                       if (!prefixcmp(arg, "--min-age=")) {
                                revs->min_age = atoi(arg + 10);
                                continue;
                        }
-                       if (!strncmp(arg, "--before=", 9)) {
+                       if (!prefixcmp(arg, "--before=")) {
                                revs->min_age = approxidate(arg + 9);
                                continue;
                        }
-                       if (!strncmp(arg, "--until=", 8)) {
+                       if (!prefixcmp(arg, "--until=")) {
                                revs->min_age = approxidate(arg + 8);
                                continue;
                        }
@@ -806,6 +847,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                handle_all(revs, flags);
                                continue;
                        }
+                       if (!strcmp(arg, "--reflog")) {
+                               handle_reflog(revs, flags);
+                               continue;
+                       }
+                       if (!strcmp(arg, "-g") ||
+                                       !strcmp(arg, "--walk-reflogs")) {
+                               init_reflog_walk(&revs->reflog_info);
+                               continue;
+                       }
                        if (!strcmp(arg, "--not")) {
                                flags ^= UNINTERESTING;
                                continue;
@@ -853,8 +903,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->boundary = 1;
                                continue;
                        }
-                       if (!strcmp(arg, "--no-left-right")) {
-                               revs->no_left_right = 1;
+                       if (!strcmp(arg, "--left-right")) {
+                               revs->left_right = 1;
                                continue;
                        }
                        if (!strcmp(arg, "--objects")) {
@@ -877,7 +927,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->num_ignore_packed = 0;
                                continue;
                        }
-                       if (!strncmp(arg, "--unpacked=", 11)) {
+                       if (!prefixcmp(arg, "--unpacked=")) {
                                revs->unpacked = 1;
                                add_ignore_packed(revs, arg+11);
                                continue;
@@ -913,7 +963,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->verbose_header = 1;
                                continue;
                        }
-                       if (!strncmp(arg, "--pretty", 8)) {
+                       if (!prefixcmp(arg, "--pretty")) {
                                revs->verbose_header = 1;
                                revs->commit_format = get_commit_format(arg+8);
                                continue;
@@ -938,7 +988,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->abbrev = DEFAULT_ABBREV;
                                continue;
                        }
-                       if (!strncmp(arg, "--abbrev=", 9)) {
+                       if (!prefixcmp(arg, "--abbrev=")) {
                                revs->abbrev = strtoul(arg + 9, NULL, 10);
                                if (revs->abbrev < MINIMUM_ABBREV)
                                        revs->abbrev = MINIMUM_ABBREV;
@@ -967,15 +1017,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                        /*
                         * Grepping the commit log
                         */
-                       if (!strncmp(arg, "--author=", 9)) {
+                       if (!prefixcmp(arg, "--author=")) {
                                add_header_grep(revs, "author", arg+9);
                                continue;
                        }
-                       if (!strncmp(arg, "--committer=", 12)) {
+                       if (!prefixcmp(arg, "--committer=")) {
                                add_header_grep(revs, "committer", arg+12);
                                continue;
                        }
-                       if (!strncmp(arg, "--grep=", 7)) {
+                       if (!prefixcmp(arg, "--grep=")) {
                                add_message_grep(revs, arg+7);
                                continue;
                        }
@@ -983,6 +1033,18 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                all_match = 1;
                                continue;
                        }
+                       if (!prefixcmp(arg, "--encoding=")) {
+                               arg += 11;
+                               if (strcmp(arg, "none"))
+                                       git_log_output_encoding = xstrdup(arg);
+                               else
+                                       git_log_output_encoding = "";
+                               continue;
+                       }
+                       if (!strcmp(arg, "--reverse")) {
+                               revs->reverse ^= 1;
+                               continue;
+                       }
 
                        opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
                        if (opts > 0) {
@@ -1055,31 +1117,26 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 void prepare_revision_walk(struct rev_info *revs)
 {
        int nr = revs->pending.nr;
-       int has_symmetric = 0;
-       struct object_array_entry *list = revs->pending.objects;
+       struct object_array_entry *e, *list;
 
+       e = list = revs->pending.objects;
        revs->pending.nr = 0;
        revs->pending.alloc = 0;
        revs->pending.objects = NULL;
        while (--nr >= 0) {
-               struct commit *commit;
-
-               if (list->item->flags & SYMMETRIC_LEFT)
-                       has_symmetric = 1;
-               commit = handle_commit(revs, list->item, list->name);
+               struct commit *commit = handle_commit(revs, e->item, e->name);
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
                                commit->object.flags |= SEEN;
                                insert_by_date(commit, &revs->commits);
                        }
                }
-               list++;
+               e++;
        }
+       free(list);
 
        if (revs->no_walk)
                return;
-       if (!revs->no_left_right && has_symmetric)
-               revs->left_right = 1;
        if (revs->limited)
                limit_list(revs);
        if (revs->topo_order)
@@ -1117,17 +1174,6 @@ static void rewrite_parents(struct rev_info *revs, struct commit *commit)
        }
 }
 
-static void mark_boundary_to_show(struct commit *commit)
-{
-       struct commit_list *p = commit->parents;
-       while (p) {
-               commit = p->item;
-               p = p->next;
-               if (commit->object.flags & BOUNDARY)
-                       commit->object.flags |= BOUNDARY_SHOW;
-       }
-}
-
 static int commit_match(struct commit *commit, struct rev_info *opt)
 {
        if (!opt->grep_filter)
@@ -1137,22 +1183,10 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
                           commit->buffer, strlen(commit->buffer));
 }
 
-struct commit *get_revision(struct rev_info *revs)
+static struct commit *get_revision_1(struct rev_info *revs)
 {
-       struct commit_list *list = revs->commits;
-
-       if (!list)
-               return NULL;
-
-       /* Check the max_count ... */
-       switch (revs->max_count) {
-       case -1:
-               break;
-       case 0:
+       if (!revs->commits)
                return NULL;
-       default:
-               revs->max_count--;
-       }
 
        do {
                struct commit_list *entry = revs->commits;
@@ -1161,6 +1195,9 @@ struct commit *get_revision(struct rev_info *revs)
                revs->commits = entry->next;
                free(entry);
 
+               if (revs->reflog_info)
+                       fake_reflog_parent(revs->reflog_info, commit);
+
                /*
                 * If we haven't done the list limiting, we need to look at
                 * the parents here. We also need to do the date-based limiting
@@ -1179,18 +1216,6 @@ struct commit *get_revision(struct rev_info *revs)
                                                    revs->ignore_packed))
                    continue;
 
-               /* We want to show boundary commits only when their
-                * children are shown.  When path-limiter is in effect,
-                * rewrite_parents() drops some commits from getting shown,
-                * and there is no point showing boundary parents that
-                * are not shown.  After rewrite_parents() rewrites the
-                * parents of a commit that is shown, we mark the boundary
-                * parents with BOUNDARY_SHOW.
-                */
-               if (commit->object.flags & BOUNDARY_SHOW) {
-                       commit->object.flags |= SHOWN;
-                       return commit;
-               }
                if (commit->object.flags & UNINTERESTING)
                        continue;
                if (revs->min_age != -1 && (commit->date > revs->min_age))
@@ -1213,10 +1238,136 @@ struct commit *get_revision(struct rev_info *revs)
                        if (revs->parents)
                                rewrite_parents(revs, commit);
                }
-               if (revs->boundary)
-                       mark_boundary_to_show(commit);
-               commit->object.flags |= SHOWN;
                return commit;
        } while (revs->commits);
        return NULL;
 }
+
+static void gc_boundary(struct object_array *array)
+{
+       unsigned nr = array->nr;
+       unsigned alloc = array->alloc;
+       struct object_array_entry *objects = array->objects;
+
+       if (alloc <= nr) {
+               unsigned i, j;
+               for (i = j = 0; i < nr; i++) {
+                       if (objects[i].item->flags & SHOWN)
+                               continue;
+                       if (i != j)
+                               objects[j] = objects[i];
+                       j++;
+               }
+               for (i = j; i < nr; i++)
+                       objects[i].item = NULL;
+               array->nr = j;
+       }
+}
+
+struct commit *get_revision(struct rev_info *revs)
+{
+       struct commit *c = NULL;
+       struct commit_list *l;
+
+       if (revs->boundary == 2) {
+               unsigned i;
+               struct object_array *array = &revs->boundary_commits;
+               struct object_array_entry *objects = array->objects;
+               for (i = 0; i < array->nr; i++) {
+                       c = (struct commit *)(objects[i].item);
+                       if (!c)
+                               continue;
+                       if (!(c->object.flags & CHILD_SHOWN))
+                               continue;
+                       if (!(c->object.flags & SHOWN))
+                               break;
+               }
+               if (array->nr <= i)
+                       return NULL;
+
+               c->object.flags |= SHOWN | BOUNDARY;
+               return c;
+       }
+
+       if (revs->reverse) {
+               int limit = -1;
+
+               if (0 <= revs->max_count) {
+                       limit = revs->max_count;
+                       if (0 < revs->skip_count)
+                               limit += revs->skip_count;
+               }
+               l = NULL;
+               while ((c = get_revision_1(revs))) {
+                       commit_list_insert(c, &l);
+                       if ((0 < limit) && !--limit)
+                               break;
+               }
+               revs->commits = l;
+               revs->reverse = 0;
+               revs->max_count = -1;
+               c = NULL;
+       }
+
+       /*
+        * Now pick up what they want to give us
+        */
+       c = get_revision_1(revs);
+       if (c) {
+               while (0 < revs->skip_count) {
+                       revs->skip_count--;
+                       c = get_revision_1(revs);
+                       if (!c)
+                               break;
+               }
+       }
+
+       /*
+        * Check the max_count.
+        */
+       switch (revs->max_count) {
+       case -1:
+               break;
+       case 0:
+               c = NULL;
+               break;
+       default:
+               revs->max_count--;
+       }
+
+       if (c)
+               c->object.flags |= SHOWN;
+
+       if (!revs->boundary) {
+               return c;
+       }
+
+       if (!c) {
+               /*
+                * get_revision_1() runs out the commits, and
+                * we are done computing the boundaries.
+                * switch to boundary commits output mode.
+                */
+               revs->boundary = 2;
+               return get_revision(revs);
+       }
+
+       /*
+        * boundary commits are the commits that are parents of the
+        * ones we got from get_revision_1() but they themselves are
+        * not returned from get_revision_1().  Before returning
+        * 'c', we need to mark its parents that they could be boundaries.
+        */
+
+       for (l = c->parents; l; l = l->next) {
+               struct object *p;
+               p = &(l->item->object);
+               if (p->flags & (CHILD_SHOWN | SHOWN))
+                       continue;
+               p->flags |= CHILD_SHOWN;
+               gc_boundary(&revs->boundary_commits);
+               add_object_array(p, NULL, &revs->boundary_commits);
+       }
+
+       return c;
+}