Fold get_merge_bases_clean() into get_merge_bases()
[gitweb.git] / revision.c
index 2294b16ea2aa0b508073e7800ccb5975a2254301..27fc1e30753a07ba686958168ce2c682c7376b53 100644 (file)
@@ -31,17 +31,12 @@ static char *path_name(struct name_path *path, const char *name)
        return n;
 }
 
-struct object_list **add_object(struct object *obj,
-                                      struct object_list **p,
-                                      struct name_path *path,
-                                      const char *name)
+void add_object(struct object *obj,
+               struct object_array *p,
+               struct name_path *path,
+               const char *name)
 {
-       struct object_list *entry = xmalloc(sizeof(*entry));
-       entry->item = obj;
-       entry->next = *p;
-       entry->name = path_name(path, name);
-       *p = entry;
-       return &entry->next;
+       add_object_array(obj, path_name(path, name), p);
 }
 
 static void mark_blob_uninteresting(struct blob *blob)
@@ -53,8 +48,9 @@ static void mark_blob_uninteresting(struct blob *blob)
 
 void mark_tree_uninteresting(struct tree *tree)
 {
+       struct tree_desc desc;
+       struct name_entry entry;
        struct object *obj = &tree->object;
-       struct tree_entry_list *entry;
 
        if (obj->flags & UNINTERESTING)
                return;
@@ -63,17 +59,22 @@ void mark_tree_uninteresting(struct tree *tree)
                return;
        if (parse_tree(tree) < 0)
                die("bad tree %s", sha1_to_hex(obj->sha1));
-       entry = tree->entries;
-       tree->entries = NULL;
-       while (entry) {
-               struct tree_entry_list *next = entry->next;
-               if (entry->directory)
-                       mark_tree_uninteresting(entry->item.tree);
+
+       desc.buf = tree->buffer;
+       desc.size = tree->size;
+       while (tree_entry(&desc, &entry)) {
+               if (S_ISDIR(entry.mode))
+                       mark_tree_uninteresting(lookup_tree(entry.sha1));
                else
-                       mark_blob_uninteresting(entry->item.blob);
-               free(entry);
-               entry = next;
+                       mark_blob_uninteresting(lookup_blob(entry.sha1));
        }
+
+       /*
+        * We don't care about the tree any more
+        * after it has been marked uninteresting.
+        */
+       free(tree->buffer);
+       tree->buffer = NULL;
 }
 
 void mark_parents_uninteresting(struct commit *commit)
@@ -111,9 +112,9 @@ void mark_parents_uninteresting(struct commit *commit)
        }
 }
 
-static void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
+void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
 {
-       add_object(obj, &revs->pending_objects, NULL, name);
+       add_object_array(obj, name, &revs->pending);
 }
 
 static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
@@ -134,7 +135,7 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object
        /*
         * Tag object? Look what it points to..
         */
-       while (object->type == tag_type) {
+       while (object->type == TYPE_TAG) {
                struct tag *tag = (struct tag *) object;
                if (revs->tag_objects && !(flags & UNINTERESTING))
                        add_pending_object(revs, object, tag->tag);
@@ -147,7 +148,7 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object
         * Commit object? Just return it, we'll do all the complex
         * reachability crud.
         */
-       if (object->type == commit_type) {
+       if (object->type == TYPE_COMMIT) {
                struct commit *commit = (struct commit *)object;
                if (parse_commit(commit) < 0)
                        die("unable to parse commit %s", name);
@@ -163,7 +164,7 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object
         * Tree object? Either mark it uniniteresting, or add it
         * to the list of objects to look at later..
         */
-       if (object->type == tree_type) {
+       if (object->type == TYPE_TREE) {
                struct tree *tree = (struct tree *)object;
                if (!revs->tree_objects)
                        return NULL;
@@ -178,7 +179,7 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object
        /*
         * Blob object? You know the drill by now..
         */
-       if (object->type == blob_type) {
+       if (object->type == TYPE_BLOB) {
                struct blob *blob = (struct blob *)object;
                if (!revs->blob_objects)
                        return NULL;
@@ -297,7 +298,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
                parse_commit(p);
                switch (rev_compare_tree(revs, p->tree, commit->tree)) {
                case REV_TREE_SAME:
-                       if (p->object.flags & UNINTERESTING) {
+                       if (!revs->simplify_history || (p->object.flags & UNINTERESTING)) {
                                /* Even if a merge with an uninteresting
                                 * side branch brought the entire change
                                 * we are interested in, we do not want
@@ -492,11 +493,11 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
                return 0;
        while (1) {
                it = get_reference(revs, arg, sha1, 0);
-               if (strcmp(it->type, tag_type))
+               if (it->type != TYPE_TAG)
                        break;
                memcpy(sha1, ((struct tag*)it)->tagged->sha1, 20);
        }
-       if (strcmp(it->type, commit_type))
+       if (it->type != TYPE_COMMIT)
                return 0;
        commit = (struct commit *)it;
        for (parents = commit->parents; parents; parents = parents->next) {
@@ -513,6 +514,7 @@ void init_revisions(struct rev_info *revs)
 
        revs->abbrev = DEFAULT_ABBREV;
        revs->ignore_merges = 1;
+       revs->simplify_history = 1;
        revs->pruning.recursive = 1;
        revs->pruning.add_remove = file_add_remove;
        revs->pruning.change = file_change;
@@ -534,6 +536,18 @@ void init_revisions(struct rev_info *revs)
        diff_setup(&revs->diffopt);
 }
 
+static void add_pending_commit_list(struct rev_info *revs,
+                                    struct commit_list *commit_list,
+                                    unsigned int flags)
+{
+       while (commit_list) {
+               struct object *object = &commit_list->item->object;
+               object->flags |= flags;
+               add_pending_object(revs, object, sha1_to_hex(object->sha1));
+               commit_list = commit_list->next;
+       }
+}
+
 /*
  * Parse revision information, filling in the "rev_info" structure,
  * and removing the used arguments from the argument list.
@@ -733,6 +747,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->abbrev = DEFAULT_ABBREV;
                                continue;
                        }
+                       if (!strncmp(arg, "--abbrev=", 9)) {
+                               revs->abbrev = strtoul(arg + 9, NULL, 10);
+                               if (revs->abbrev < MINIMUM_ABBREV)
+                                       revs->abbrev = MINIMUM_ABBREV;
+                               else if (revs->abbrev > 40)
+                                       revs->abbrev = 40;
+                               continue;
+                       }
                        if (!strcmp(arg, "--abbrev-commit")) {
                                revs->abbrev_commit = 1;
                                continue;
@@ -742,6 +764,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->full_diff = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--full-history")) {
+                               revs->simplify_history = 0;
+                               continue;
+                       }
                        opts = diff_opt_parse(&revs->diffopt, argv+i, argc-i);
                        if (opts > 0) {
                                revs->diff = 1;
@@ -757,27 +783,45 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                        unsigned char from_sha1[20];
                        const char *next = dotdot + 2;
                        const char *this = arg;
+                       int symmetric = *next == '.';
+                       unsigned int flags_exclude = flags ^ UNINTERESTING;
+
                        *dotdot = 0;
+                       next += symmetric;
+
                        if (!*next)
                                next = "HEAD";
                        if (dotdot == arg)
                                this = "HEAD";
                        if (!get_sha1(this, from_sha1) &&
                            !get_sha1(next, sha1)) {
-                               struct object *exclude;
-                               struct object *include;
-
-                               exclude = get_reference(revs, this, from_sha1, flags ^ UNINTERESTING);
-                               include = get_reference(revs, next, sha1, flags);
-                               if (!exclude || !include)
-                                       die("Invalid revision range %s..%s", arg, next);
+                               struct commit *a, *b;
+                               struct commit_list *exclude;
+
+                               a = lookup_commit_reference(from_sha1);
+                               b = lookup_commit_reference(sha1);
+                               if (!a || !b) {
+                                       die(symmetric ?
+                                           "Invalid symmetric difference expression %s...%s" :
+                                           "Invalid revision range %s..%s",
+                                           arg, next);
+                               }
 
                                if (!seen_dashdash) {
                                        *dotdot = '.';
                                        verify_non_filename(revs->prefix, arg);
                                }
-                               add_pending_object(revs, exclude, this);
-                               add_pending_object(revs, include, next);
+
+                               if (symmetric) {
+                                       exclude = get_merge_bases(a, b, 1);
+                                       add_pending_commit_list(revs, exclude,
+                                                               flags_exclude);
+                                       a->object.flags |= flags;
+                               } else
+                                       a->object.flags |= flags_exclude;
+                               b->object.flags |= flags;
+                               add_pending_object(revs, &a->object, this);
+                               add_pending_object(revs, &b->object, next);
                                continue;
                        }
                        *dotdot = '.';
@@ -817,7 +861,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                object = get_reference(revs, arg, sha1, flags ^ local_flags);
                add_pending_object(revs, object, arg);
        }
-       if (def && !revs->pending_objects) {
+       if (def && !revs->pending.nr) {
                unsigned char sha1[20];
                struct object *object;
                if (get_sha1(def, sha1))
@@ -849,11 +893,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
 
 void prepare_revision_walk(struct rev_info *revs)
 {
-       struct object_list *list;
+       int nr = revs->pending.nr;
+       struct object_array_entry *list = revs->pending.objects;
 
-       list = revs->pending_objects;
-       revs->pending_objects = NULL;
-       while (list) {
+       revs->pending.nr = 0;
+       revs->pending.alloc = 0;
+       revs->pending.objects = NULL;
+       while (--nr >= 0) {
                struct commit *commit = handle_commit(revs, list->item, list->name);
                if (commit) {
                        if (!(commit->object.flags & SEEN)) {
@@ -861,7 +907,7 @@ void prepare_revision_walk(struct rev_info *revs)
                                insert_by_date(commit, &revs->commits);
                        }
                }
-               list = list->next;
+               list++;
        }
 
        if (revs->no_walk)
@@ -930,9 +976,11 @@ struct commit *get_revision(struct rev_info *revs)
        }
 
        do {
-               struct commit *commit = revs->commits->item;
+               struct commit_list *entry = revs->commits;
+               struct commit *commit = entry->item;
 
-               revs->commits = revs->commits->next;
+               revs->commits = entry->next;
+               free(entry);
 
                /*
                 * If we haven't done the list limiting, we need to look at