expose a helper function peel_to_type().
[gitweb.git] / revision.c
index 26610bb42d1c2359ac27af3e8ad4b92c888f3b16..6e85aaa3fb30e98f3b02f094af7f1763577cdb8d 100644 (file)
@@ -67,10 +67,17 @@ void mark_tree_uninteresting(struct tree *tree)
 
        init_tree_desc(&desc, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
-               if (S_ISDIR(entry.mode))
+               switch (object_type(entry.mode)) {
+               case OBJ_TREE:
                        mark_tree_uninteresting(lookup_tree(entry.sha1));
-               else
+                       break;
+               case OBJ_BLOB:
                        mark_blob_uninteresting(lookup_blob(entry.sha1));
+                       break;
+               default:
+                       /* Subproject commit - not in this repository */
+                       break;
+               }
        }
 
        /*
@@ -132,6 +139,18 @@ void add_pending_object(struct rev_info *revs, struct object *obj, const char *n
        add_pending_object_with_mode(revs, obj, name, S_IFINVALID);
 }
 
+void add_head_to_pending(struct rev_info *revs)
+{
+       unsigned char sha1[20];
+       struct object *obj;
+       if (get_sha1("HEAD", sha1))
+               return;
+       obj = parse_object(sha1);
+       if (!obj)
+               return;
+       add_pending_object(revs, obj, "HEAD");
+}
+
 static struct object *get_reference(struct rev_info *revs, const char *name, const unsigned char *sha1, unsigned int flags)
 {
        struct object *object;
@@ -252,7 +271,7 @@ static void file_add_remove(struct diff_options *options,
        }
        tree_difference = diff;
        if (tree_difference == REV_TREE_DIFFERENT)
-               options->has_changes = 1;
+               DIFF_OPT_SET(options, HAS_CHANGES);
 }
 
 static void file_change(struct diff_options *options,
@@ -262,7 +281,7 @@ static void file_change(struct diff_options *options,
                 const char *base, const char *path)
 {
        tree_difference = REV_TREE_DIFFERENT;
-       options->has_changes = 1;
+       DIFF_OPT_SET(options, HAS_CHANGES);
 }
 
 static int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree *t2)
@@ -272,7 +291,7 @@ static int rev_compare_tree(struct rev_info *revs, struct tree *t1, struct tree
        if (!t2)
                return REV_TREE_DIFFERENT;
        tree_difference = REV_TREE_SAME;
-       revs->pruning.has_changes = 0;
+       DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
        if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "",
                           &revs->pruning) < 0)
                return REV_TREE_DIFFERENT;
@@ -296,7 +315,7 @@ static int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
        init_tree_desc(&empty, "", 0);
 
        tree_difference = REV_TREE_SAME;
-       revs->pruning.has_changes = 0;
+       DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
        retval = diff_tree(&empty, &real, "", &revs->pruning);
        free(tree);
 
@@ -308,15 +327,28 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
        struct commit_list **pp, *parent;
        int tree_changed = 0, tree_same = 0;
 
+       /*
+        * If we don't do pruning, everything is interesting
+        */
+       if (!revs->prune)
+               return;
+
        if (!commit->tree)
                return;
 
        if (!commit->parents) {
-               if (!rev_same_tree_as_empty(revs, commit->tree))
-                       commit->object.flags |= TREECHANGE;
+               if (rev_same_tree_as_empty(revs, commit->tree))
+                       commit->object.flags |= TREESAME;
                return;
        }
 
+       /*
+        * Normal non-merge commit? If we don't want to make the
+        * history dense, we consider it always to be a change..
+        */
+       if (!revs->dense && !commit->parents->next)
+               return;
+
        pp = &commit->parents;
        while ((parent = *pp) != NULL) {
                struct commit *p = parent->item;
@@ -340,6 +372,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
                        }
                        parent->next = NULL;
                        commit->parents = parent;
+                       commit->object.flags |= TREESAME;
                        return;
 
                case REV_TREE_NEW:
@@ -368,7 +401,8 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
                die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
        }
        if (tree_changed && !tree_same)
-               commit->object.flags |= TREECHANGE;
+               return;
+       commit->object.flags |= TREESAME;
 }
 
 static int add_parents_to_list(struct rev_info *revs, struct commit *commit, struct commit_list **list)
@@ -415,8 +449,7 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, str
         * simplify the commit history and find the parent
         * that has no differences in the path set if one exists.
         */
-       if (revs->prune_fn)
-               revs->prune_fn(revs, commit);
+       try_to_simplify_commit(revs, commit);
 
        if (revs->no_walk)
                return 0;
@@ -672,8 +705,8 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        revs->abbrev = DEFAULT_ABBREV;
        revs->ignore_merges = 1;
        revs->simplify_history = 1;
-       revs->pruning.recursive = 1;
-       revs->pruning.quiet = 1;
+       DIFF_OPT_SET(&revs->pruning, RECURSIVE);
+       DIFF_OPT_SET(&revs->pruning, QUIET);
        revs->pruning.add_remove = file_add_remove;
        revs->pruning.change = file_change;
        revs->lifo = 1;
@@ -684,9 +717,6 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        revs->skip_count = -1;
        revs->max_count = -1;
 
-       revs->prune_fn = NULL;
-       revs->prune_data = NULL;
-
        revs->commit_format = CMIT_FMT_DEFAULT;
 
        diff_setup(&revs->diffopt);
@@ -1073,13 +1103,13 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                        }
                        if (!strcmp(arg, "-r")) {
                                revs->diff = 1;
-                               revs->diffopt.recursive = 1;
+                               DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
                                continue;
                        }
                        if (!strcmp(arg, "-t")) {
                                revs->diff = 1;
-                               revs->diffopt.recursive = 1;
-                               revs->diffopt.tree_in_recursive = 1;
+                               DIFF_OPT_SET(&revs->diffopt, RECURSIVE);
+                               DIFF_OPT_SET(&revs->diffopt, TREE_IN_RECURSIVE);
                                continue;
                        }
                        if (!strcmp(arg, "-m")) {
@@ -1260,8 +1290,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        if (revs->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT)
                revs->diff = 1;
 
-       /* Pickaxe and rename following needs diffs */
-       if (revs->diffopt.pickaxe || revs->diffopt.follow_renames)
+       /* Pickaxe, diff-filter and rename following need diffs */
+       if (revs->diffopt.pickaxe ||
+           revs->diffopt.filter ||
+           DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
                revs->diff = 1;
 
        if (revs->topo_order)
@@ -1270,8 +1302,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        if (revs->prune_data) {
                diff_tree_setup_paths(revs->prune_data, &revs->pruning);
                /* Can't prune commits with rename following: the paths change.. */
-               if (!revs->diffopt.follow_renames)
-                       revs->prune_fn = try_to_simplify_commit;
+               if (!DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
+                       revs->prune = 1;
                if (!revs->full_diff)
                        diff_tree_setup_paths(revs->prune_data, &revs->diffopt);
        }
@@ -1341,7 +1373,9 @@ static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp
                                return rewrite_one_error;
                if (p->parents && p->parents->next)
                        return rewrite_one_ok;
-               if (p->object.flags & (TREECHANGE | UNINTERESTING))
+               if (p->object.flags & UNINTERESTING)
+                       return rewrite_one_ok;
+               if (!(p->object.flags & TREESAME))
                        return rewrite_one_ok;
                if (!p->parents)
                        return rewrite_one_noparents;
@@ -1398,6 +1432,36 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
                           commit->buffer, strlen(commit->buffer));
 }
 
+enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
+{
+       if (commit->object.flags & SHOWN)
+               return commit_ignore;
+       if (revs->unpacked && has_sha1_pack(commit->object.sha1, revs->ignore_packed))
+               return commit_ignore;
+       if (commit->object.flags & UNINTERESTING)
+               return commit_ignore;
+       if (revs->min_age != -1 && (commit->date > revs->min_age))
+               return commit_ignore;
+       if (revs->no_merges && commit->parents && commit->parents->next)
+               return commit_ignore;
+       if (!commit_match(commit, revs))
+               return commit_ignore;
+       if (revs->prune && revs->dense) {
+               /* Commit without changes? */
+               if (commit->object.flags & TREESAME) {
+                       /* drop merges unless we want parenthood */
+                       if (!revs->parents)
+                               return commit_ignore;
+                       /* non-merge - always ignore it */
+                       if (!commit->parents || !commit->parents->next)
+                               return commit_ignore;
+               }
+               if (revs->parents && rewrite_parents(revs, commit) < 0)
+                       return commit_error;
+       }
+       return commit_show;
+}
+
 static struct commit *get_revision_1(struct rev_info *revs)
 {
        if (!revs->commits)
@@ -1425,36 +1489,15 @@ static struct commit *get_revision_1(struct rev_info *revs)
                        if (add_parents_to_list(revs, commit, &revs->commits) < 0)
                                return NULL;
                }
-               if (commit->object.flags & SHOWN)
-                       continue;
 
-               if (revs->unpacked && has_sha1_pack(commit->object.sha1,
-                                                   revs->ignore_packed))
-                   continue;
-
-               if (commit->object.flags & UNINTERESTING)
+               switch (simplify_commit(revs, commit)) {
+               case commit_ignore:
                        continue;
-               if (revs->min_age != -1 && (commit->date > revs->min_age))
-                       continue;
-               if (revs->no_merges &&
-                   commit->parents && commit->parents->next)
-                       continue;
-               if (!commit_match(commit, revs))
-                       continue;
-               if (revs->prune_fn && revs->dense) {
-                       /* Commit without changes? */
-                       if (!(commit->object.flags & TREECHANGE)) {
-                               /* drop merges unless we want parenthood */
-                               if (!revs->parents)
-                                       continue;
-                               /* non-merge - always ignore it */
-                               if (!commit->parents || !commit->parents->next)
-                                       continue;
-                       }
-                       if (revs->parents && rewrite_parents(revs, commit) < 0)
-                               return NULL;
+               case commit_error:
+                       return NULL;
+               default:
+                       return commit;
                }
-               return commit;
        } while (revs->commits);
        return NULL;
 }