Code

Windows: TMP and TEMP environment variables specify a temporary directory.
[git.git] / revision.c
index d79f2b3a7b7fda64c1288f79a971f7b3e6d51cb2..fc667552592daa3c894d0df4e97469d1809dbf27 100644 (file)
@@ -6,6 +6,7 @@
 #include "diff.h"
 #include "refs.h"
 #include "revision.h"
+#include "graph.h"
 #include "grep.h"
 #include "reflog-walk.h"
 #include "patch-ids.h"
@@ -46,6 +47,8 @@ void add_object(struct object *obj,
 
 static void mark_blob_uninteresting(struct blob *blob)
 {
+       if (!blob)
+               return;
        if (blob->object.flags & UNINTERESTING)
                return;
        blob->object.flags |= UNINTERESTING;
@@ -57,6 +60,8 @@ void mark_tree_uninteresting(struct tree *tree)
        struct name_entry entry;
        struct object *obj = &tree->object;
 
+       if (!tree)
+               return;
        if (obj->flags & UNINTERESTING)
                return;
        obj->flags |= UNINTERESTING;
@@ -173,6 +178,8 @@ static struct commit *handle_commit(struct rev_info *revs, struct object *object
                struct tag *tag = (struct tag *) object;
                if (revs->tag_objects && !(flags & UNINTERESTING))
                        add_pending_object(revs, object, tag->tag);
+               if (!tag->tagged)
+                       die("bad tag");
                object = parse_object(tag->tagged->sha1);
                if (!object)
                        die("bad object %s", sha1_to_hex(tag->tagged->sha1));
@@ -409,7 +416,6 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, str
 {
        struct commit_list *parent = commit->parents;
        unsigned left_flag;
-       int add, rest;
 
        if (commit->object.flags & ADDED)
                return 0;
@@ -456,19 +462,18 @@ static int add_parents_to_list(struct rev_info *revs, struct commit *commit, str
 
        left_flag = (commit->object.flags & SYMMETRIC_LEFT);
 
-       rest = !revs->first_parent_only;
-       for (parent = commit->parents, add = 1; parent; add = rest) {
+       for (parent = commit->parents; parent; parent = parent->next) {
                struct commit *p = parent->item;
 
-               parent = parent->next;
                if (parse_commit(p) < 0)
                        return -1;
                p->object.flags |= left_flag;
-               if (p->object.flags & SEEN)
-                       continue;
-               p->object.flags |= SEEN;
-               if (add)
+               if (!(p->object.flags & SEEN)) {
+                       p->object.flags |= SEEN;
                        insert_by_date(p, list);
+               }
+               if(revs->first_parent_only)
+                       break;
        }
        return 0;
 }
@@ -558,8 +563,39 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
        free_patch_ids(&ids);
 }
 
+/* How many extra uninteresting commits we want to see.. */
+#define SLOP 5
+
+static int still_interesting(struct commit_list *src, unsigned long date, int slop)
+{
+       /*
+        * No source list at all? We're definitely done..
+        */
+       if (!src)
+               return 0;
+
+       /*
+        * Does the destination list contain entries with a date
+        * before the source list? Definitely _not_ done.
+        */
+       if (date < src->item->date)
+               return SLOP;
+
+       /*
+        * Does the source list still have interesting commits in
+        * it? Definitely not done..
+        */
+       if (!everybody_uninteresting(src))
+               return SLOP;
+
+       /* Ok, we're closing in.. */
+       return slop-1;
+}
+
 static int limit_list(struct rev_info *revs)
 {
+       int slop = SLOP;
+       unsigned long date = ~0ul;
        struct commit_list *list = revs->commits;
        struct commit_list *newlist = NULL;
        struct commit_list **p = &newlist;
@@ -579,12 +615,19 @@ static int limit_list(struct rev_info *revs)
                        return -1;
                if (obj->flags & UNINTERESTING) {
                        mark_parents_uninteresting(commit);
-                       if (everybody_uninteresting(list))
-                               break;
-                       continue;
+                       if (revs->show_all)
+                               p = &commit_list_insert(commit, p)->next;
+                       slop = still_interesting(list, date, slop);
+                       if (slop)
+                               continue;
+                       /* If showing all, add the whole pending list to the end */
+                       if (revs->show_all)
+                               *p = list;
+                       break;
                }
                if (revs->min_age != -1 && (commit->date > revs->min_age))
                        continue;
+               date = commit->date;
                p = &commit_list_insert(commit, p)->next;
 
                show = show_early_output;
@@ -617,12 +660,13 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, int flag,
        return 0;
 }
 
-static void handle_all(struct rev_info *revs, unsigned flags)
+static void handle_refs(struct rev_info *revs, unsigned flags,
+               int (*for_each)(each_ref_fn, void *))
 {
        struct all_refs_cb cb;
        cb.all_revs = revs;
        cb.all_flags = flags;
-       for_each_ref(handle_one_ref, &cb);
+       for_each(handle_one_ref, &cb);
 }
 
 static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
@@ -685,6 +729,8 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
                it = get_reference(revs, arg, sha1, 0);
                if (it->type != OBJ_TAG)
                        break;
+               if (!((struct tag*)it)->tagged)
+                       return 0;
                hashcpy(sha1, ((struct tag*)it)->tagged->sha1);
        }
        if (it->type != OBJ_COMMIT)
@@ -720,6 +766,10 @@ void init_revisions(struct rev_info *revs, const char *prefix)
        revs->commit_format = CMIT_FMT_DEFAULT;
 
        diff_setup(&revs->diffopt);
+       if (prefix && !revs->diffopt.prefix) {
+               revs->diffopt.prefix = prefix;
+               revs->diffopt.prefix_length = strlen(prefix);
+       }
 }
 
 static void add_pending_commit_list(struct rev_info *revs,
@@ -920,6 +970,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        int left = 1;
        int all_match = 0;
        int regflags = 0;
+       int fixed = 0;
 
        /* First, search for "--" */
        seen_dashdash = 0;
@@ -988,7 +1039,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                continue;
                        }
                        if (!strcmp(arg, "--all")) {
-                               handle_all(revs, flags);
+                               handle_refs(revs, flags, for_each_ref);
+                               continue;
+                       }
+                       if (!strcmp(arg, "--branches")) {
+                               handle_refs(revs, flags, for_each_branch_ref);
+                               continue;
+                       }
+                       if (!strcmp(arg, "--tags")) {
+                               handle_refs(revs, flags, for_each_tag_ref);
+                               continue;
+                       }
+                       if (!strcmp(arg, "--remotes")) {
+                               handle_refs(revs, flags, for_each_remote_ref);
                                continue;
                        }
                        if (!strcmp(arg, "--first-parent")) {
@@ -1041,7 +1104,8 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                }
                        }
                        if (!strcmp(arg, "--parents")) {
-                               revs->parents = 1;
+                               revs->rewrite_parents = 1;
+                               revs->print_parents = 1;
                                continue;
                        }
                        if (!strcmp(arg, "--dense")) {
@@ -1052,6 +1116,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->dense = 0;
                                continue;
                        }
+                       if (!strcmp(arg, "--show-all")) {
+                               revs->show_all = 1;
+                               continue;
+                       }
                        if (!strcmp(arg, "--remove-empty")) {
                                revs->remove_empty_trees = 1;
                                continue;
@@ -1129,9 +1197,20 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                revs->verbose_header = 1;
                                continue;
                        }
-                       if (!prefixcmp(arg, "--pretty")) {
+                       if (!strcmp(arg, "--pretty")) {
                                revs->verbose_header = 1;
-                               revs->commit_format = get_commit_format(arg+8);
+                               get_commit_format(arg+8, revs);
+                               continue;
+                       }
+                       if (!prefixcmp(arg, "--pretty=")) {
+                               revs->verbose_header = 1;
+                               get_commit_format(arg+9, revs);
+                               continue;
+                       }
+                       if (!strcmp(arg, "--graph")) {
+                               revs->topo_order = 1;
+                               revs->rewrite_parents = 1;
+                               revs->graph = graph_init(revs);
                                continue;
                        }
                        if (!strcmp(arg, "--root")) {
@@ -1213,6 +1292,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                regflags |= REG_ICASE;
                                continue;
                        }
+                       if (!strcmp(arg, "--fixed-strings") ||
+                           !strcmp(arg, "-F")) {
+                               fixed = 1;
+                               continue;
+                       }
                        if (!strcmp(arg, "--all-match")) {
                                all_match = 1;
                                continue;
@@ -1268,8 +1352,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                }
        }
 
-       if (revs->grep_filter)
+       if (revs->grep_filter) {
                revs->grep_filter->regflags |= regflags;
+               revs->grep_filter->fixed = fixed;
+       }
 
        if (show_merge)
                prepare_show_merge(revs);
@@ -1321,6 +1407,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
        if (revs->reverse && revs->reflog_info)
                die("cannot combine --reverse with --walk-reflogs");
 
+       /*
+        * Limitations on the graph functionality
+        */
+       if (revs->reverse && revs->graph)
+               die("cannot combine --reverse with --graph");
+
+       if (revs->reflog_info && revs->graph)
+               die("cannot combine --walk-reflogs with --graph");
+
        return left;
 }
 
@@ -1435,6 +1530,8 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
                return commit_ignore;
        if (revs->unpacked && has_sha1_pack(commit->object.sha1, revs->ignore_packed))
                return commit_ignore;
+       if (revs->show_all)
+               return commit_show;
        if (commit->object.flags & UNINTERESTING)
                return commit_ignore;
        if (revs->min_age != -1 && (commit->date > revs->min_age))
@@ -1447,13 +1544,13 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
                /* Commit without changes? */
                if (commit->object.flags & TREESAME) {
                        /* drop merges unless we want parenthood */
-                       if (!revs->parents)
+                       if (!revs->rewrite_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)
+               if (revs->rewrite_parents && rewrite_parents(revs, commit) < 0)
                        return commit_error;
        }
        return commit_show;
@@ -1520,28 +1617,62 @@ static void gc_boundary(struct object_array *array)
        }
 }
 
-struct commit *get_revision(struct rev_info *revs)
+static void create_boundary_commit_list(struct rev_info *revs)
+{
+       unsigned i;
+       struct commit *c;
+       struct object_array *array = &revs->boundary_commits;
+       struct object_array_entry *objects = array->objects;
+
+       /*
+        * If revs->commits is non-NULL at this point, an error occurred in
+        * get_revision_1().  Ignore the error and continue printing the
+        * boundary commits anyway.  (This is what the code has always
+        * done.)
+        */
+       if (revs->commits) {
+               free_commit_list(revs->commits);
+               revs->commits = NULL;
+       }
+
+       /*
+        * Put all of the actual boundary commits from revs->boundary_commits
+        * into revs->commits
+        */
+       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 | BOUNDARY))
+                       continue;
+               c->object.flags |= BOUNDARY;
+               commit_list_insert(c, &revs->commits);
+       }
+
+       /*
+        * If revs->topo_order is set, sort the boundary commits
+        * in topological order
+        */
+       sort_in_topological_order(&revs->commits, revs->lifo);
+}
+
+static struct commit *get_revision_internal(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;
+               /*
+                * All of the normal commits have already been returned,
+                * and we are now returning boundary commits.
+                * create_boundary_commit_list() has populated
+                * revs->commits with the remaining commits to return.
+                */
+               c = pop_commit(&revs->commits);
+               if (c)
+                       c->object.flags |= SHOWN;
                return c;
        }
 
@@ -1605,7 +1736,14 @@ struct commit *get_revision(struct rev_info *revs)
                 * switch to boundary commits output mode.
                 */
                revs->boundary = 2;
-               return get_revision(revs);
+
+               /*
+                * Update revs->commits to contain the list of
+                * boundary commits.
+                */
+               create_boundary_commit_list(revs);
+
+               return get_revision_internal(revs);
        }
 
        /*
@@ -1627,3 +1765,11 @@ struct commit *get_revision(struct rev_info *revs)
 
        return c;
 }
+
+struct commit *get_revision(struct rev_info *revs)
+{
+       struct commit *c = get_revision_internal(revs);
+       if (c && revs->graph)
+               graph_update(revs->graph, c);
+       return c;
+}