Merge branch 'eb/mail'
[gitweb.git] / revision.c
index 113dd5a89fc03ff17b04443f2ac7148b4c36af33..7bff2a10b1044651a776b42d4ee3d91661830aa2 100644 (file)
@@ -53,8 +53,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 +64,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)
@@ -134,7 +140,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 +153,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 +169,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 +184,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 +303,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
@@ -477,12 +483,43 @@ static void handle_all(struct rev_info *revs, unsigned flags)
        for_each_ref(handle_one_ref);
 }
 
+static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
+{
+       unsigned char sha1[20];
+       struct object *it;
+       struct commit *commit;
+       struct commit_list *parents;
+
+       if (*arg == '^') {
+               flags ^= UNINTERESTING;
+               arg++;
+       }
+       if (get_sha1(arg, sha1))
+               return 0;
+       while (1) {
+               it = get_reference(revs, arg, sha1, 0);
+               if (it->type != TYPE_TAG)
+                       break;
+               memcpy(sha1, ((struct tag*)it)->tagged->sha1, 20);
+       }
+       if (it->type != TYPE_COMMIT)
+               return 0;
+       commit = (struct commit *)it;
+       for (parents = commit->parents; parents; parents = parents->next) {
+               it = &parents->item->object;
+               it->flags |= flags;
+               add_pending_object(revs, it, arg);
+       }
+       return 1;
+}
+
 void init_revisions(struct rev_info *revs)
 {
        memset(revs, 0, sizeof(*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;
@@ -544,7 +581,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->max_count = atoi(arg + 12);
                                continue;
                        }
-                       /* accept -<digit>, like traditilnal "head" */
+                       /* accept -<digit>, like traditional "head" */
                        if ((*arg == '-') && isdigit(arg[1])) {
                                revs->max_count = atoi(arg + 1);
                                continue;
@@ -664,6 +701,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                        }
                        if (!strcmp(arg, "-c")) {
                                revs->diff = 1;
+                               revs->dense_combined_merges = 0;
                                revs->combine_merges = 1;
                                continue;
                        }
@@ -702,6 +740,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;
@@ -711,6 +757,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;
@@ -740,39 +790,56 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                include = get_reference(revs, next, sha1, flags);
                                if (!exclude || !include)
                                        die("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);
                                continue;
                        }
                        *dotdot = '.';
                }
+               dotdot = strstr(arg, "^@");
+               if (dotdot && !dotdot[2]) {
+                       *dotdot = 0;
+                       if (add_parents_only(revs, arg, flags))
+                               continue;
+                       *dotdot = '^';
+               }
                local_flags = 0;
                if (*arg == '^') {
                        local_flags = UNINTERESTING;
                        arg++;
                }
-               if (get_sha1(arg, sha1) < 0) {
-                       struct stat st;
+               if (get_sha1(arg, sha1)) {
                        int j;
 
                        if (seen_dashdash || local_flags)
                                die("bad revision '%s'", arg);
 
-                       /* If we didn't have a "--", all filenames must exist */
-                       for (j = i; j < argc; j++) {
-                               if (lstat(argv[j], &st) < 0)
-                                       die("'%s': %s", argv[j], strerror(errno));
-                       }
+                       /* If we didn't have a "--":
+                        * (1) all filenames must exist;
+                        * (2) all rev-args must not be interpretable
+                        *     as a valid filename.
+                        * but the latter we have checked in the main loop.
+                        */
+                       for (j = i; j < argc; j++)
+                               verify_filename(revs->prefix, argv[j]);
+
                        revs->prune_data = get_pathspec(revs->prefix, argv + i);
                        break;
                }
+               if (!seen_dashdash)
+                       verify_non_filename(revs->prefix, arg);
                object = get_reference(revs, arg, sha1, flags ^ local_flags);
                add_pending_object(revs, object, arg);
        }
        if (def && !revs->pending_objects) {
                unsigned char sha1[20];
                struct object *object;
-               if (get_sha1(def, sha1) < 0)
+               if (get_sha1(def, sha1))
                        die("bad default revision '%s'", def);
                object = get_reference(revs, def, sha1, 0);
                add_pending_object(revs, object, def);
@@ -789,7 +856,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        }
        if (revs->combine_merges) {
                revs->ignore_merges = 0;
-               if (revs->dense_combined_merges)
+               if (revs->dense_combined_merges &&
+                   (revs->diffopt.output_format != DIFF_FORMAT_DIFFSTAT))
                        revs->diffopt.output_format = DIFF_FORMAT_PATCH;
        }
        revs->diffopt.abbrev = revs->abbrev;
@@ -881,9 +949,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