Add test case for $Id$ expanded in the repository
[gitweb.git] / revision.c
index adc381c268b6f336ab7820c464fafa39629c8e28..0125d41136871ad2cd07daaaabf40bd2f902a44f 100644 (file)
@@ -8,6 +8,7 @@
 #include "revision.h"
 #include "grep.h"
 #include "reflog-walk.h"
+#include "patch-ids.h"
 
 static char *path_name(struct name_path *path, const char *name)
 {
@@ -114,10 +115,15 @@ void mark_parents_uninteresting(struct commit *commit)
 }
 
 void add_pending_object(struct rev_info *revs, struct object *obj, const char *name)
+{
+       add_pending_object_with_mode(revs, obj, name, S_IFINVALID);
+}
+
+void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
 {
        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);
+       add_object_array_with_mode(obj, name, &revs->pending, mode);
        if (revs->reflog_info && obj->type == OBJ_COMMIT)
                add_reflog_for_walk(revs->reflog_info,
                                (struct commit *)obj, name);
@@ -312,7 +318,10 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
        while ((parent = *pp) != NULL) {
                struct commit *p = parent->item;
 
-               parse_commit(p);
+               if (parse_commit(p) < 0)
+                       die("cannot simplify commit %s (because of %s)",
+                           sha1_to_hex(commit->object.sha1),
+                           sha1_to_hex(p->object.sha1));
                switch (rev_compare_tree(revs, p->tree, commit->tree)) {
                case REV_TREE_SAME:
                        tree_same = 1;
@@ -341,7 +350,10 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
                                 * IOW, we pretend this parent is a
                                 * "root" commit.
                                 */
-                               parse_commit(p);
+                               if (parse_commit(p) < 0)
+                                       die("cannot simplify commit %s (invalid %s)",
+                                           sha1_to_hex(commit->object.sha1),
+                                           sha1_to_hex(p->object.sha1));
                                p->parents = NULL;
                        }
                /* fallthrough */
@@ -356,13 +368,14 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
                commit->object.flags |= TREECHANGE;
 }
 
-static void add_parents_to_list(struct rev_info *revs, struct commit *commit, struct commit_list **list)
+static int add_parents_to_list(struct rev_info *revs, struct commit *commit, struct commit_list **list)
 {
        struct commit_list *parent = commit->parents;
        unsigned left_flag;
+       int add, rest;
 
        if (commit->object.flags & ADDED)
-               return;
+               return 0;
        commit->object.flags |= ADDED;
 
        /*
@@ -381,7 +394,8 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st
                while (parent) {
                        struct commit *p = parent->item;
                        parent = parent->next;
-                       parse_commit(p);
+                       if (parse_commit(p) < 0)
+                               return -1;
                        p->object.flags |= UNINTERESTING;
                        if (p->parents)
                                mark_parents_uninteresting(p);
@@ -390,7 +404,7 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st
                        p->object.flags |= SEEN;
                        insert_by_date(p, list);
                }
-               return;
+               return 0;
        }
 
        /*
@@ -402,25 +416,108 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st
                revs->prune_fn(revs, commit);
 
        if (revs->no_walk)
-               return;
+               return 0;
 
        left_flag = (commit->object.flags & SYMMETRIC_LEFT);
-       parent = commit->parents;
-       while (parent) {
+
+       rest = !revs->first_parent_only;
+       for (parent = commit->parents, add = 1; parent; add = rest) {
                struct commit *p = parent->item;
 
                parent = parent->next;
-
-               parse_commit(p);
+               if (parse_commit(p) < 0)
+                       return -1;
                p->object.flags |= left_flag;
                if (p->object.flags & SEEN)
                        continue;
                p->object.flags |= SEEN;
-               insert_by_date(p, list);
+               if (add)
+                       insert_by_date(p, list);
+       }
+       return 0;
+}
+
+static void cherry_pick_list(struct commit_list *list)
+{
+       struct commit_list *p;
+       int left_count = 0, right_count = 0;
+       int left_first;
+       struct patch_ids ids;
+
+       /* First count the commits on the left and on the right */
+       for (p = list; p; p = p->next) {
+               struct commit *commit = p->item;
+               unsigned flags = commit->object.flags;
+               if (flags & BOUNDARY)
+                       ;
+               else if (flags & SYMMETRIC_LEFT)
+                       left_count++;
+               else
+                       right_count++;
+       }
+
+       left_first = left_count < right_count;
+       init_patch_ids(&ids);
+
+       /* Compute patch-ids for one side */
+       for (p = list; p; p = p->next) {
+               struct commit *commit = p->item;
+               unsigned flags = commit->object.flags;
+
+               if (flags & BOUNDARY)
+                       continue;
+               /*
+                * If we have fewer left, left_first is set and we omit
+                * commits on the right branch in this loop.  If we have
+                * fewer right, we skip the left ones.
+                */
+               if (left_first != !!(flags & SYMMETRIC_LEFT))
+                       continue;
+               commit->util = add_commit_patch_id(commit, &ids);
+       }
+
+       /* Check the other side */
+       for (p = list; p; p = p->next) {
+               struct commit *commit = p->item;
+               struct patch_id *id;
+               unsigned flags = commit->object.flags;
+
+               if (flags & BOUNDARY)
+                       continue;
+               /*
+                * If we have fewer left, left_first is set and we omit
+                * commits on the left branch in this loop.
+                */
+               if (left_first == !!(flags & SYMMETRIC_LEFT))
+                       continue;
+
+               /*
+                * Have we seen the same patch id?
+                */
+               id = has_commit_patch_id(commit, &ids);
+               if (!id)
+                       continue;
+               id->seen = 1;
+               commit->object.flags |= SHOWN;
+       }
+
+       /* Now check the original side for seen ones */
+       for (p = list; p; p = p->next) {
+               struct commit *commit = p->item;
+               struct patch_id *ent;
+
+               ent = commit->util;
+               if (!ent)
+                       continue;
+               if (ent->seen)
+                       commit->object.flags |= SHOWN;
+               commit->util = NULL;
        }
+
+       free_patch_ids(&ids);
 }
 
-static void limit_list(struct rev_info *revs)
+static int limit_list(struct rev_info *revs)
 {
        struct commit_list *list = revs->commits;
        struct commit_list *newlist = NULL;
@@ -436,7 +533,8 @@ static void limit_list(struct rev_info *revs)
 
                if (revs->max_age != -1 && (commit->date < revs->max_age))
                        obj->flags |= UNINTERESTING;
-               add_parents_to_list(revs, commit, &list);
+               if (add_parents_to_list(revs, commit, &list) < 0)
+                       return -1;
                if (obj->flags & UNINTERESTING) {
                        mark_parents_uninteresting(commit);
                        if (everybody_uninteresting(list))
@@ -447,7 +545,11 @@ static void limit_list(struct rev_info *revs)
                        continue;
                p = &commit_list_insert(commit, p)->next;
        }
+       if (revs->cherry_pick)
+               cherry_pick_list(newlist);
+
        revs->commits = newlist;
+       return 0;
 }
 
 struct all_refs_cb {
@@ -484,7 +586,7 @@ static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
                        add_pending_object(cb->all_revs, o, "");
                }
                else if (!cb->warned_bad_reflog) {
-                       warn("reflog of '%s' references pruned commits",
+                       warning("reflog of '%s' references pruned commits",
                                cb->name_for_errormsg);
                        cb->warned_bad_reflog = 1;
                }
@@ -565,6 +667,7 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        revs->min_age = -1;
        revs->skip_count = -1;
        revs->max_count = -1;
+       revs->subject_prefix = "PATCH";
 
        revs->prune_fn = NULL;
        revs->prune_data = NULL;
@@ -636,6 +739,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
                        int flags,
                        int cant_be_filename)
 {
+       unsigned mode;
        char *dotdot;
        struct object *object;
        unsigned char sha1[20];
@@ -709,12 +813,12 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
                local_flags = UNINTERESTING;
                arg++;
        }
-       if (get_sha1(arg, sha1))
+       if (get_sha1_with_mode(arg, sha1, &mode))
                return -1;
        if (!cant_be_filename)
                verify_non_filename(revs->prefix, arg);
        object = get_reference(revs, arg, sha1, flags ^ local_flags);
-       add_pending_object(revs, object, arg);
+       add_pending_object_with_mode(revs, object, arg, mode);
        return 0;
 }
 
@@ -847,6 +951,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                handle_all(revs, flags);
                                continue;
                        }
+                       if (!strcmp(arg, "--first-parent")) {
+                               revs->first_parent_only = 1;
+                               continue;
+                       }
                        if (!strcmp(arg, "--reflog")) {
                                handle_reflog(revs, flags);
                                continue;
@@ -907,6 +1015,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->left_right = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--cherry-pick")) {
+                               revs->cherry_pick = 1;
+                               continue;
+                       }
                        if (!strcmp(arg, "--objects")) {
                                revs->tag_objects = 1;
                                revs->tree_objects = 1;
@@ -1010,7 +1122,18 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                continue;
                        }
                        if (!strcmp(arg, "--relative-date")) {
-                               revs->relative_date = 1;
+                               revs->date_mode = DATE_RELATIVE;
+                               continue;
+                       }
+                       if (!strncmp(arg, "--date=", 7)) {
+                               if (!strcmp(arg + 7, "relative"))
+                                       revs->date_mode = DATE_RELATIVE;
+                               else if (!strcmp(arg + 7, "local"))
+                                       revs->date_mode = DATE_LOCAL;
+                               else if (!strcmp(arg + 7, "default"))
+                                       revs->date_mode = DATE_NORMAL;
+                               else
+                                       die("unknown date format %s", arg);
                                continue;
                        }
 
@@ -1082,10 +1205,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        if (def && !revs->pending.nr) {
                unsigned char sha1[20];
                struct object *object;
-               if (get_sha1(def, sha1))
+               unsigned mode;
+               if (get_sha1_with_mode(def, sha1, &mode))
                        die("bad default revision '%s'", def);
                object = get_reference(revs, def, sha1, 0);
-               add_pending_object(revs, object, def);
+               add_pending_object_with_mode(revs, object, def, mode);
        }
 
        if (revs->topo_order)
@@ -1114,7 +1238,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        return left;
 }
 
-void prepare_revision_walk(struct rev_info *revs)
+int prepare_revision_walk(struct rev_info *revs)
 {
        int nr = revs->pending.nr;
        struct object_array_entry *e, *list;
@@ -1136,42 +1260,57 @@ void prepare_revision_walk(struct rev_info *revs)
        free(list);
 
        if (revs->no_walk)
-               return;
+               return 0;
        if (revs->limited)
-               limit_list(revs);
+               if (limit_list(revs) < 0)
+                       return -1;
        if (revs->topo_order)
                sort_in_topological_order_fn(&revs->commits, revs->lifo,
                                             revs->topo_setter,
                                             revs->topo_getter);
+       return 0;
 }
 
-static int rewrite_one(struct rev_info *revs, struct commit **pp)
+enum rewrite_result {
+       rewrite_one_ok,
+       rewrite_one_noparents,
+       rewrite_one_error,
+};
+
+static enum rewrite_result rewrite_one(struct rev_info *revs, struct commit **pp)
 {
        for (;;) {
                struct commit *p = *pp;
                if (!revs->limited)
-                       add_parents_to_list(revs, p, &revs->commits);
+                       if (add_parents_to_list(revs, p, &revs->commits) < 0)
+                               return rewrite_one_error;
                if (p->parents && p->parents->next)
-                       return 0;
+                       return rewrite_one_ok;
                if (p->object.flags & (TREECHANGE | UNINTERESTING))
-                       return 0;
+                       return rewrite_one_ok;
                if (!p->parents)
-                       return -1;
+                       return rewrite_one_noparents;
                *pp = p->parents->item;
        }
 }
 
-static void rewrite_parents(struct rev_info *revs, struct commit *commit)
+static int rewrite_parents(struct rev_info *revs, struct commit *commit)
 {
        struct commit_list **pp = &commit->parents;
        while (*pp) {
                struct commit_list *parent = *pp;
-               if (rewrite_one(revs, &parent->item) < 0) {
+               switch (rewrite_one(revs, &parent->item)) {
+               case rewrite_one_ok:
+                       break;
+               case rewrite_one_noparents:
                        *pp = parent->next;
                        continue;
+               case rewrite_one_error:
+                       return -1;
                }
                pp = &parent->next;
        }
+       return 0;
 }
 
 static int commit_match(struct commit *commit, struct rev_info *opt)
@@ -1207,7 +1346,8 @@ static struct commit *get_revision_1(struct rev_info *revs)
                        if (revs->max_age != -1 &&
                            (commit->date < revs->max_age))
                                continue;
-                       add_parents_to_list(revs, commit, &revs->commits);
+                       if (add_parents_to_list(revs, commit, &revs->commits) < 0)
+                               return NULL;
                }
                if (commit->object.flags & SHOWN)
                        continue;
@@ -1235,8 +1375,8 @@ static struct commit *get_revision_1(struct rev_info *revs)
                                if (!commit->parents || !commit->parents->next)
                                        continue;
                        }
-                       if (revs->parents)
-                               rewrite_parents(revs, commit);
+                       if (revs->parents && rewrite_parents(revs, commit) < 0)
+                               return NULL;
                }
                return commit;
        } while (revs->commits);