{"thread":{"id":"3498","subject":"[PATCH 0/3] git-rev-list libification effort: the next stage","startedAt":"2006-02-28T19:19:41Z","lastAt":"2006-03-01T20:06:32Z","messageCount":13,"participants":["Linus Torvalds","Junio C Hamano","Martin Langhoff"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"16915","messageId":"Pine.LNX.4.64.0602281115110.22647@g5.osdl.org","threadId":"3498","inReplyTo":null,"subject":"[PATCH 0/3] git-rev-list libification effort: the next stage","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-02-28T19:19:41Z","receivedAt":"2006-02-28T19:19:41Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nOk, the following three patches that I'll send out are still pretty rough, \nbut they actually get us to the first real point of this whole exercise: \nwriting one of the trivial git helper scripts in C.\n\nIn particular, at the end, we have \"git log\" being implemented as this \ntrivial C function:\n\n\t#define LOGSIZE (65536)\n\n\tstatic int cmd_log(int argc, char **argv, char **envp)\n\t{\n\t\tstruct rev_info rev;\n\t\tstruct commit *commit;\n\t\tchar *buf = xmalloc(LOGSIZE);\n\n\t\targc = setup_revisions(argc, argv, &rev, \"HEAD\");\n\t\tprepare_revision_walk(&rev);\n\t\tsetup_pager();\n\t\twhile ((commit = get_revision(&rev)) != NULL) {\n\t\t\tpretty_print_commit(CMIT_FMT_DEFAULT, commit, ~0, buf, LOGSIZE, 18);\n\t\t\tprintf(\"%s\\n\", buf);\n\t\t}\n\t\tfree(buf);\n\t\treturn 0;\n\t}\n\nwhich is actually a pretty good example of what I wanted to do.  It's\nnot perfect yet (it doesn't parse the \"--pretty=xxx\" option yet, nor the\n\"--since\" and \"--until\" dates, for example), but I think this is all\ngoing in the right direction. \n\n\t\t\tLinus\n"},{"id":"16916","messageId":"Pine.LNX.4.64.0602281120060.22647@g5.osdl.org","threadId":"3498","inReplyTo":"Pine.LNX.4.64.0602281115110.22647@g5.osdl.org","subject":"[PATCH 1/3] git-rev-list libification: rev-list walking","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-02-28T19:24:00Z","receivedAt":"2006-02-28T19:24:00Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThis actually moves the \"meat\" of the revision walking from rev-list.c\nto the new library code in revision.h. It introduces the new functions\n\n\tvoid prepare_revision_walk(struct rev_info *revs);\n\tstruct commit *get_revision(struct rev_info *revs);\n\nto prepare and then walk the revisions that we have.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n---\n\nAll the same old warnigns apply!! This is a bit more intrusive than the \nprevious patch series, since it actually changes how things work. It \npasses all the tests I threw at it (well, actually, I only tested the end \nresult of the whole series, bad me), but I'd obviously like to remind \neverybody that this is some really core code, and mistakes are bad.\n\nI didn't worry about cleaning code up. It probably could be cleaned up, \nbut I worked at trying to just move as much of it as-is from rev-list.c as \npossible, while leaving any code that was really only relevant to rev-list \nitself alone.\n\ndiff --git a/rev-list.c b/rev-list.c\nindex 2e80930..94f22dd 100644\n--- a/rev-list.c\n+++ b/rev-list.c\n@@ -8,11 +8,10 @@\n #include \"diff.h\"\n #include \"revision.h\"\n \n-/* bits #0 and #1 in revision.h */\n+/* bits #0-2 in revision.h */\n \n-#define COUNTED\t\t(1u << 2)\n-#define SHOWN\t\t(1u << 3)\n-#define TREECHANGE\t(1u << 4)\n+#define COUNTED\t\t(1u << 3)\n+#define SHOWN\t\t(1u << 4)\n #define TMP_MARK\t(1u << 5) /* for isolated cases; clean after use */\n \n static const char rev_list_usage[] =\n@@ -213,17 +212,17 @@ static struct object_list **process_tree\n \treturn p;\n }\n \n-static void show_commit_list(struct commit_list *list)\n+static void show_commit_list(struct rev_info *revs)\n {\n+\tstruct commit *commit;\n \tstruct object_list *objects = NULL, **p = &objects, *pending;\n-\twhile (list) {\n-\t\tstruct commit *commit = pop_most_recent_commit(&list, SEEN);\n \n+\twhile ((commit = get_revision(revs)) != NULL) {\n \t\tp = process_tree(commit->tree, p, NULL, \"\");\n \t\tif (process_commit(commit) == STOP)\n \t\t\tbreak;\n \t}\n-\tfor (pending = revs.pending_objects; pending; pending = pending->next) {\n+\tfor (pending = revs->pending_objects; pending; pending = pending->next) {\n \t\tstruct object *obj = pending->item;\n \t\tconst char *name = pending->name;\n \t\tif (obj->flags & (UNINTERESTING | SEEN))\n@@ -259,19 +258,6 @@ static void show_commit_list(struct comm\n \t}\n }\n \n-static int everybody_uninteresting(struct commit_list *orig)\n-{\n-\tstruct commit_list *list = orig;\n-\twhile (list) {\n-\t\tstruct commit *commit = list->item;\n-\t\tlist = list->next;\n-\t\tif (commit->object.flags & UNINTERESTING)\n-\t\t\tcontinue;\n-\t\treturn 0;\n-\t}\n-\treturn 1;\n-}\n-\n /*\n  * This is a truly stupid algorithm, but it's only\n  * used for bisection, and we just don't care enough.\n@@ -379,224 +365,12 @@ static void mark_edges_uninteresting(str\n \t}\n }\n \n-#define TREE_SAME\t0\n-#define TREE_NEW\t1\n-#define TREE_DIFFERENT\t2\n-static int tree_difference = TREE_SAME;\n-\n-static void file_add_remove(struct diff_options *options,\n-\t\t    int addremove, unsigned mode,\n-\t\t    const unsigned char *sha1,\n-\t\t    const char *base, const char *path)\n-{\n-\tint diff = TREE_DIFFERENT;\n-\n-\t/*\n-\t * Is it an add of a new file? It means that\n-\t * the old tree didn't have it at all, so we\n-\t * will turn \"TREE_SAME\" -> \"TREE_NEW\", but\n-\t * leave any \"TREE_DIFFERENT\" alone (and if\n-\t * it already was \"TREE_NEW\", we'll keep it\n-\t * \"TREE_NEW\" of course).\n-\t */\n-\tif (addremove == '+') {\n-\t\tdiff = tree_difference;\n-\t\tif (diff != TREE_SAME)\n-\t\t\treturn;\n-\t\tdiff = TREE_NEW;\n-\t}\n-\ttree_difference = diff;\n-}\n-\n-static void file_change(struct diff_options *options,\n-\t\t unsigned old_mode, unsigned new_mode,\n-\t\t const unsigned char *old_sha1,\n-\t\t const unsigned char *new_sha1,\n-\t\t const char *base, const char *path)\n-{\n-\ttree_difference = TREE_DIFFERENT;\n-}\n-\n-static struct diff_options diff_opt = {\n-\t.recursive = 1,\n-\t.add_remove = file_add_remove,\n-\t.change = file_change,\n-};\n-\n-static int compare_tree(struct tree *t1, struct tree *t2)\n-{\n-\tif (!t1)\n-\t\treturn TREE_NEW;\n-\tif (!t2)\n-\t\treturn TREE_DIFFERENT;\n-\ttree_difference = TREE_SAME;\n-\tif (diff_tree_sha1(t1->object.sha1, t2->object.sha1, \"\", &diff_opt) < 0)\n-\t\treturn TREE_DIFFERENT;\n-\treturn tree_difference;\n-}\n-\n-static int same_tree_as_empty(struct tree *t1)\n-{\n-\tint retval;\n-\tvoid *tree;\n-\tstruct tree_desc empty, real;\n-\n-\tif (!t1)\n-\t\treturn 0;\n-\n-\ttree = read_object_with_reference(t1->object.sha1, \"tree\", &real.size, NULL);\n-\tif (!tree)\n-\t\treturn 0;\n-\treal.buf = tree;\n-\n-\tempty.buf = \"\";\n-\tempty.size = 0;\n-\n-\ttree_difference = 0;\n-\tretval = diff_tree(&empty, &real, \"\", &diff_opt);\n-\tfree(tree);\n-\n-\treturn retval >= 0 && !tree_difference;\n-}\n-\n-static void try_to_simplify_commit(struct commit *commit)\n-{\n-\tstruct commit_list **pp, *parent;\n-\n-\tif (!commit->tree)\n-\t\treturn;\n-\n-\tif (!commit->parents) {\n-\t\tif (!same_tree_as_empty(commit->tree))\n-\t\t\tcommit->object.flags |= TREECHANGE;\n-\t\treturn;\n-\t}\n-\n-\tpp = &commit->parents;\n-\twhile ((parent = *pp) != NULL) {\n-\t\tstruct commit *p = parent->item;\n-\n-\t\tif (p->object.flags & UNINTERESTING) {\n-\t\t\tpp = &parent->next;\n-\t\t\tcontinue;\n-\t\t}\n-\n-\t\tparse_commit(p);\n-\t\tswitch (compare_tree(p->tree, commit->tree)) {\n-\t\tcase TREE_SAME:\n-\t\t\tparent->next = NULL;\n-\t\t\tcommit->parents = parent;\n-\t\t\treturn;\n-\n-\t\tcase TREE_NEW:\n-\t\t\tif (revs.remove_empty_trees && same_tree_as_empty(p->tree)) {\n-\t\t\t\t*pp = parent->next;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t/* fallthrough */\n-\t\tcase TREE_DIFFERENT:\n-\t\t\tpp = &parent->next;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tdie(\"bad tree compare for commit %s\", sha1_to_hex(commit->object.sha1));\n-\t}\n-\tcommit->object.flags |= TREECHANGE;\n-}\n-\n-static void add_parents_to_list(struct commit *commit, struct commit_list **list)\n-{\n-\tstruct commit_list *parent = commit->parents;\n-\n-\t/*\n-\t * If the commit is uninteresting, don't try to\n-\t * prune parents - we want the maximal uninteresting\n-\t * set.\n-\t *\n-\t * Normally we haven't parsed the parent\n-\t * yet, so we won't have a parent of a parent\n-\t * here. However, it may turn out that we've\n-\t * reached this commit some other way (where it\n-\t * wasn't uninteresting), in which case we need\n-\t * to mark its parents recursively too..\n-\t */\n-\tif (commit->object.flags & UNINTERESTING) {\n-\t\twhile (parent) {\n-\t\t\tstruct commit *p = parent->item;\n-\t\t\tparent = parent->next;\n-\t\t\tparse_commit(p);\n-\t\t\tp->object.flags |= UNINTERESTING;\n-\t\t\tif (p->parents)\n-\t\t\t\tmark_parents_uninteresting(p);\n-\t\t\tif (p->object.flags & SEEN)\n-\t\t\t\tcontinue;\n-\t\t\tp->object.flags |= SEEN;\n-\t\t\tinsert_by_date(p, list);\n-\t\t}\n-\t\treturn;\n-\t}\n-\n-\t/*\n-\t * Ok, the commit wasn't uninteresting. Try to\n-\t * simplify the commit history and find the parent\n-\t * that has no differences in the path set if one exists.\n-\t */\n-\tif (revs.paths)\n-\t\ttry_to_simplify_commit(commit);\n-\n-\tparent = commit->parents;\n-\twhile (parent) {\n-\t\tstruct commit *p = parent->item;\n-\n-\t\tparent = parent->next;\n-\n-\t\tparse_commit(p);\n-\t\tif (p->object.flags & SEEN)\n-\t\t\tcontinue;\n-\t\tp->object.flags |= SEEN;\n-\t\tinsert_by_date(p, list);\n-\t}\n-}\n-\n-static struct commit_list *limit_list(struct commit_list *list)\n-{\n-\tstruct commit_list *newlist = NULL;\n-\tstruct commit_list **p = &newlist;\n-\twhile (list) {\n-\t\tstruct commit_list *entry = list;\n-\t\tstruct commit *commit = list->item;\n-\t\tstruct object *obj = &commit->object;\n-\n-\t\tlist = list->next;\n-\t\tfree(entry);\n-\n-\t\tif (revs.max_age != -1 && (commit->date < revs.max_age))\n-\t\t\tobj->flags |= UNINTERESTING;\n-\t\tif (revs.unpacked && has_sha1_pack(obj->sha1))\n-\t\t\tobj->flags |= UNINTERESTING;\n-\t\tadd_parents_to_list(commit, &list);\n-\t\tif (obj->flags & UNINTERESTING) {\n-\t\t\tmark_parents_uninteresting(commit);\n-\t\t\tif (everybody_uninteresting(list))\n-\t\t\t\tbreak;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (revs.min_age != -1 && (commit->date > revs.min_age))\n-\t\t\tcontinue;\n-\t\tp = &commit_list_insert(commit, p)->next;\n-\t}\n-\tif (revs.tree_objects)\n-\t\tmark_edges_uninteresting(newlist);\n-\tif (bisect_list)\n-\t\tnewlist = find_bisection(newlist);\n-\treturn newlist;\n-}\n-\n int main(int argc, const char **argv)\n {\n \tstruct commit_list *list;\n \tint i;\n \n-\targc = setup_revisions(argc, argv, &revs);\n+\targc = setup_revisions(argc, argv, &revs, NULL);\n \n \tfor (i = 1 ; i < argc; i++) {\n \t\tconst char *arg = argv[i];\n@@ -672,24 +446,18 @@ int main(int argc, const char **argv)\n \t    (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) && !revs.pending_objects))\n \t\tusage(rev_list_usage);\n \n-\tif (revs.paths)\n-\t\tdiff_tree_setup_paths(revs.paths);\n+\tprepare_revision_walk(&revs);\n+\tif (revs.tree_objects)\n+\t\tmark_edges_uninteresting(revs.commits);\n+\n+\tif (bisect_list)\n+\t\trevs.commits = find_bisection(revs.commits);\n \n \tsave_commit_buffer = verbose_header;\n \ttrack_object_refs = 0;\n \n-\tif (!merge_order) {\t\t\n-\t\tsort_by_date(&list);\n-\t\tif (list && !revs.limited && revs.max_count == 1 &&\n-\t\t    !revs.tag_objects && !revs.tree_objects && !revs.blob_objects) {\n-\t\t\tshow_commit(list->item);\n-\t\t\treturn 0;\n-\t\t}\n-\t        if (revs.limited)\n-\t\t\tlist = limit_list(list);\n-\t\tif (revs.topo_order)\n-\t\t\tsort_in_topological_order(&list, revs.lifo);\n-\t\tshow_commit_list(list);\n+\tif (!merge_order) {\n+\t\tshow_commit_list(&revs);\n \t} else {\n #ifndef NO_OPENSSL\n \t\tif (sort_list_in_merge_order(list, &process_commit)) {\ndiff --git a/revision.c b/revision.c\nindex 0422593..fb728c1 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -3,6 +3,7 @@\n #include \"blob.h\"\n #include \"tree.h\"\n #include \"commit.h\"\n+#include \"diff.h\"\n #include \"refs.h\"\n #include \"revision.h\"\n \n@@ -183,6 +184,229 @@ static struct commit *get_commit_referen\n \tdie(\"%s is unknown object\", name);\n }\n \n+static int everybody_uninteresting(struct commit_list *orig)\n+{\n+\tstruct commit_list *list = orig;\n+\twhile (list) {\n+\t\tstruct commit *commit = list->item;\n+\t\tlist = list->next;\n+\t\tif (commit->object.flags & UNINTERESTING)\n+\t\t\tcontinue;\n+\t\treturn 0;\n+\t}\n+\treturn 1;\n+}\n+\n+#define TREE_SAME\t0\n+#define TREE_NEW\t1\n+#define TREE_DIFFERENT\t2\n+static int tree_difference = TREE_SAME;\n+\n+static void file_add_remove(struct diff_options *options,\n+\t\t    int addremove, unsigned mode,\n+\t\t    const unsigned char *sha1,\n+\t\t    const char *base, const char *path)\n+{\n+\tint diff = TREE_DIFFERENT;\n+\n+\t/*\n+\t * Is it an add of a new file? It means that\n+\t * the old tree didn't have it at all, so we\n+\t * will turn \"TREE_SAME\" -> \"TREE_NEW\", but\n+\t * leave any \"TREE_DIFFERENT\" alone (and if\n+\t * it already was \"TREE_NEW\", we'll keep it\n+\t * \"TREE_NEW\" of course).\n+\t */\n+\tif (addremove == '+') {\n+\t\tdiff = tree_difference;\n+\t\tif (diff != TREE_SAME)\n+\t\t\treturn;\n+\t\tdiff = TREE_NEW;\n+\t}\n+\ttree_difference = diff;\n+}\n+\n+static void file_change(struct diff_options *options,\n+\t\t unsigned old_mode, unsigned new_mode,\n+\t\t const unsigned char *old_sha1,\n+\t\t const unsigned char *new_sha1,\n+\t\t const char *base, const char *path)\n+{\n+\ttree_difference = TREE_DIFFERENT;\n+}\n+\n+static struct diff_options diff_opt = {\n+\t.recursive = 1,\n+\t.add_remove = file_add_remove,\n+\t.change = file_change,\n+};\n+\n+static int compare_tree(struct tree *t1, struct tree *t2)\n+{\n+\tif (!t1)\n+\t\treturn TREE_NEW;\n+\tif (!t2)\n+\t\treturn TREE_DIFFERENT;\n+\ttree_difference = TREE_SAME;\n+\tif (diff_tree_sha1(t1->object.sha1, t2->object.sha1, \"\", &diff_opt) < 0)\n+\t\treturn TREE_DIFFERENT;\n+\treturn tree_difference;\n+}\n+\n+static int same_tree_as_empty(struct tree *t1)\n+{\n+\tint retval;\n+\tvoid *tree;\n+\tstruct tree_desc empty, real;\n+\n+\tif (!t1)\n+\t\treturn 0;\n+\n+\ttree = read_object_with_reference(t1->object.sha1, \"tree\", &real.size, NULL);\n+\tif (!tree)\n+\t\treturn 0;\n+\treal.buf = tree;\n+\n+\tempty.buf = \"\";\n+\tempty.size = 0;\n+\n+\ttree_difference = 0;\n+\tretval = diff_tree(&empty, &real, \"\", &diff_opt);\n+\tfree(tree);\n+\n+\treturn retval >= 0 && !tree_difference;\n+}\n+\n+static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)\n+{\n+\tstruct commit_list **pp, *parent;\n+\n+\tif (!commit->tree)\n+\t\treturn;\n+\n+\tif (!commit->parents) {\n+\t\tif (!same_tree_as_empty(commit->tree))\n+\t\t\tcommit->object.flags |= TREECHANGE;\n+\t\treturn;\n+\t}\n+\n+\tpp = &commit->parents;\n+\twhile ((parent = *pp) != NULL) {\n+\t\tstruct commit *p = parent->item;\n+\n+\t\tif (p->object.flags & UNINTERESTING) {\n+\t\t\tpp = &parent->next;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tparse_commit(p);\n+\t\tswitch (compare_tree(p->tree, commit->tree)) {\n+\t\tcase TREE_SAME:\n+\t\t\tparent->next = NULL;\n+\t\t\tcommit->parents = parent;\n+\t\t\treturn;\n+\n+\t\tcase TREE_NEW:\n+\t\t\tif (revs->remove_empty_trees && same_tree_as_empty(p->tree)) {\n+\t\t\t\t*pp = parent->next;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t/* fallthrough */\n+\t\tcase TREE_DIFFERENT:\n+\t\t\tpp = &parent->next;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tdie(\"bad tree compare for commit %s\", sha1_to_hex(commit->object.sha1));\n+\t}\n+\tcommit->object.flags |= TREECHANGE;\n+}\n+\n+static void add_parents_to_list(struct rev_info *revs, struct commit *commit, struct commit_list **list)\n+{\n+\tstruct commit_list *parent = commit->parents;\n+\n+\t/*\n+\t * If the commit is uninteresting, don't try to\n+\t * prune parents - we want the maximal uninteresting\n+\t * set.\n+\t *\n+\t * Normally we haven't parsed the parent\n+\t * yet, so we won't have a parent of a parent\n+\t * here. However, it may turn out that we've\n+\t * reached this commit some other way (where it\n+\t * wasn't uninteresting), in which case we need\n+\t * to mark its parents recursively too..\n+\t */\n+\tif (commit->object.flags & UNINTERESTING) {\n+\t\twhile (parent) {\n+\t\t\tstruct commit *p = parent->item;\n+\t\t\tparent = parent->next;\n+\t\t\tparse_commit(p);\n+\t\t\tp->object.flags |= UNINTERESTING;\n+\t\t\tif (p->parents)\n+\t\t\t\tmark_parents_uninteresting(p);\n+\t\t\tif (p->object.flags & SEEN)\n+\t\t\t\tcontinue;\n+\t\t\tp->object.flags |= SEEN;\n+\t\t\tinsert_by_date(p, list);\n+\t\t}\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * Ok, the commit wasn't uninteresting. Try to\n+\t * simplify the commit history and find the parent\n+\t * that has no differences in the path set if one exists.\n+\t */\n+\tif (revs->paths)\n+\t\ttry_to_simplify_commit(revs, commit);\n+\n+\tparent = commit->parents;\n+\twhile (parent) {\n+\t\tstruct commit *p = parent->item;\n+\n+\t\tparent = parent->next;\n+\n+\t\tparse_commit(p);\n+\t\tif (p->object.flags & SEEN)\n+\t\t\tcontinue;\n+\t\tp->object.flags |= SEEN;\n+\t\tinsert_by_date(p, list);\n+\t}\n+}\n+\n+static void limit_list(struct rev_info *revs)\n+{\n+\tstruct commit_list *list = revs->commits;\n+\tstruct commit_list *newlist = NULL;\n+\tstruct commit_list **p = &newlist;\n+\n+\twhile (list) {\n+\t\tstruct commit_list *entry = list;\n+\t\tstruct commit *commit = list->item;\n+\t\tstruct object *obj = &commit->object;\n+\n+\t\tlist = list->next;\n+\t\tfree(entry);\n+\n+\t\tif (revs->max_age != -1 && (commit->date < revs->max_age))\n+\t\t\tobj->flags |= UNINTERESTING;\n+\t\tif (revs->unpacked && has_sha1_pack(obj->sha1))\n+\t\t\tobj->flags |= UNINTERESTING;\n+\t\tadd_parents_to_list(revs, commit, &list);\n+\t\tif (obj->flags & UNINTERESTING) {\n+\t\t\tmark_parents_uninteresting(commit);\n+\t\t\tif (everybody_uninteresting(list))\n+\t\t\t\tbreak;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (revs->min_age != -1 && (commit->date > revs->min_age))\n+\t\t\tcontinue;\n+\t\tp = &commit_list_insert(commit, p)->next;\n+\t}\n+\trevs->commits = newlist;\n+}\n+\n static void add_one_commit(struct commit *commit, struct rev_info *revs)\n {\n \tif (!commit || (commit->object.flags & SEEN))\n@@ -214,10 +438,9 @@ static void handle_all(struct rev_info *\n  *\n  * Returns the number of arguments left (\"new argc\").\n  */\n-int setup_revisions(int argc, const char **argv, struct rev_info *revs)\n+int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)\n {\n \tint i, flags, seen_dashdash;\n-\tconst char *def = NULL;\n \tconst char **unrecognized = argv+1;\n \tint left = 1;\n \n@@ -381,3 +604,23 @@ int setup_revisions(int argc, const char\n \t*unrecognized = NULL;\n \treturn left;\n }\n+\n+void prepare_revision_walk(struct rev_info *revs)\n+{\n+\tif (revs->paths)\n+\t\tdiff_tree_setup_paths(revs->paths);\n+\tsort_by_date(&revs->commits);\n+\tif (revs->limited)\n+\t\tlimit_list(revs);\n+\tif (revs->topo_order)\n+\t\tsort_in_topological_order(&revs->commits, revs->lifo);\n+}\n+\n+struct commit *get_revision(struct rev_info *revs)\n+{\n+\tif (!revs->commits)\n+\t\treturn NULL;\n+\treturn pop_most_recent_commit(&revs->commits, SEEN);\n+}\n+\n+\ndiff --git a/revision.h b/revision.h\nindex a22f198..0bed3c0 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -3,6 +3,7 @@\n \n #define SEEN\t\t(1u<<0)\n #define UNINTERESTING   (1u<<1)\n+#define TREECHANGE\t(1u<<2)\n \n struct rev_info {\n \t/* Starting list */\n@@ -32,7 +33,10 @@ struct rev_info {\n };\n \n /* revision.c */\n-extern int setup_revisions(int argc, const char **argv, struct rev_info *revs);\n+extern int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def);\n+extern void prepare_revision_walk(struct rev_info *revs);\n+extern struct commit *get_revision(struct rev_info *revs);\n+\n extern void mark_parents_uninteresting(struct commit *commit);\n extern void mark_tree_uninteresting(struct tree *tree);\n \n"},{"id":"16917","messageId":"Pine.LNX.4.64.0602281124120.22647@g5.osdl.org","threadId":"3498","inReplyTo":"Pine.LNX.4.64.0602281115110.22647@g5.osdl.org","subject":"[PATCH 2/3] Introduce trivial new pager.c helper infrastructure","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-02-28T19:26:21Z","receivedAt":"2006-02-28T19:26:21Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThis introduces the new function\n\n\tvoid setup_pager(void);\n\nto set up output to be written through a pager applocation.\n\nAll in preparation for doing the simple scripts in C.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n---\n\nOk, this should be pretty obvious, which is not to say that it shouldn't \nbe improved (ie it only handles trivial definitions of PAGER). Any obvious \nimprovements are left as an exercise for the reader, as not important for \nmy current goal of actually having something working.\n\n\ndiff --git a/Makefile b/Makefile\nindex 3575489..0b1a998 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -205,7 +205,7 @@ LIB_OBJS = \\\n \tquote.o read-cache.o refs.o run-command.o \\\n \tserver-info.o setup.o sha1_file.o sha1_name.o strbuf.o \\\n \ttag.o tree.o usage.o config.o environment.o ctype.o copy.o \\\n-\tfetch-clone.o revision.o \\\n+\tfetch-clone.o revision.o pager.o \\\n \t$(DIFF_OBJS)\n \n LIBS = $(LIB_FILE)\ndiff --git a/cache.h b/cache.h\nindex 58eec00..3af6b86 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -352,4 +352,7 @@ extern int copy_fd(int ifd, int ofd);\n extern int receive_unpack_pack(int fd[2], const char *me, int quiet);\n extern int receive_keep_pack(int fd[2], const char *me, int quiet);\n \n+/* pager.c */\n+extern void setup_pager(void);\n+\n #endif /* CACHE_H */\ndiff --git a/pager.c b/pager.c\nnew file mode 100644\nindex 0000000..1364e15\n--- /dev/null\n+++ b/pager.c\n@@ -0,0 +1,48 @@\n+#include \"cache.h\"\n+\n+/*\n+ * This is split up from the rest of git so that we might do\n+ * something different on Windows, for example.\n+ */\n+\n+static void run_pager(void)\n+{\n+\tconst char *prog = getenv(\"PAGER\");\n+\tif (!prog)\n+\t\tprog = \"less\";\n+\tsetenv(\"LESS\", \"-S\", 0);\n+\texeclp(prog, prog, NULL);\n+}\n+\n+void setup_pager(void)\n+{\n+\tpid_t pid;\n+\tint fd[2];\n+\n+\tif (!isatty(1))\n+\t\treturn;\n+\tif (pipe(fd) < 0)\n+\t\treturn;\n+\tpid = fork();\n+\tif (pid < 0) {\n+\t\tclose(fd[0]);\n+\t\tclose(fd[1]);\n+\t\treturn;\n+\t}\n+\n+\t/* return in the child */\n+\tif (!pid) {\n+\t\tdup2(fd[1], 1);\n+\t\tclose(fd[0]);\n+\t\tclose(fd[1]);\n+\t\treturn;\n+\t}\n+\n+\t/* The original process turns into the PAGER */\n+\tdup2(fd[0], 0);\n+\tclose(fd[0]);\n+\tclose(fd[1]);\n+\n+\trun_pager();\n+\texit(255);\n+}\n"},{"id":"16918","messageId":"Pine.LNX.4.64.0602281126340.22647@g5.osdl.org","threadId":"3498","inReplyTo":"Pine.LNX.4.64.0602281115110.22647@g5.osdl.org","subject":"[PATCH 3/3] Tie it all together: \"git log\"","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-02-28T19:30:19Z","receivedAt":"2006-02-28T19:30:19Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThis is what the previous diffs all built up to.\n\nWe can do \"git log\" as a trivial small helper function inside git.c, \nbecause the infrastructure is all there for us to use as a library.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n----\n\nAgain, this may not do exactly what the current \"git log\" does. That's not \nthe point. The point is to introduce the fundamental functionality, so \nthat people can play with this and improve on it, and fix any of my stupid \nbugs.\n\nIt should be pretty easy to change some of the other rev-list-walking \nfunctions to use the library interfaces too, instead of executing an \nexternal \"git-rev-list\" process. This was a perfect example of how to get \nsomething working, though.\n\n\t\tLinus\n\ndiff --git a/Makefile b/Makefile\nindex 0b1a998..ead13be 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -450,7 +450,7 @@ strip: $(PROGRAMS) git$X\n \n git$X: git.c $(LIB_FILE)\n \t$(CC) -DGIT_VERSION='\"$(GIT_VERSION)\"' \\\n-\t\t$(CFLAGS) $(COMPAT_CFLAGS) -o $@ $(filter %.c,$^) $(LIB_FILE)\n+\t\t$(ALL_CFLAGS) -o $@ $(filter %.c,$^) $(LIB_FILE) $(LIBS)\n \n $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh\n \trm -f $@\ndiff --git a/git.c b/git.c\nindex 993cd0d..b0da6b1 100644\n--- a/git.c\n+++ b/git.c\n@@ -12,6 +12,10 @@\n #include \"git-compat-util.h\"\n #include \"exec_cmd.h\"\n \n+#include \"cache.h\"\n+#include \"commit.h\"\n+#include \"revision.h\"\n+\n #ifndef PATH_MAX\n # define PATH_MAX 4096\n #endif\n@@ -245,6 +249,25 @@ static int cmd_help(int argc, char **arg\n \treturn 0;\n }\n \n+#define LOGSIZE (65536)\n+\n+static int cmd_log(int argc, char **argv, char **envp)\n+{\n+\tstruct rev_info rev;\n+\tstruct commit *commit;\n+\tchar *buf = xmalloc(LOGSIZE);\n+\n+\targc = setup_revisions(argc, argv, &rev, \"HEAD\");\n+\tprepare_revision_walk(&rev);\n+\tsetup_pager();\n+\twhile ((commit = get_revision(&rev)) != NULL) {\n+\t\tpretty_print_commit(CMIT_FMT_DEFAULT, commit, ~0, buf, LOGSIZE, 18);\n+\t\tprintf(\"%s\\n\", buf);\n+\t}\n+\tfree(buf);\n+\treturn 0;\n+}\n+\n #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))\n \n static void handle_internal_command(int argc, char **argv, char **envp)\n@@ -256,6 +279,7 @@ static void handle_internal_command(int \n \t} commands[] = {\n \t\t{ \"version\", cmd_version },\n \t\t{ \"help\", cmd_help },\n+\t\t{ \"log\", cmd_log },\n \t};\n \tint i;\n \n"},{"id":"16922","messageId":"Pine.LNX.4.64.0602281251390.22647@g5.osdl.org","threadId":"3498","inReplyTo":"Pine.LNX.4.64.0602281126340.22647@g5.osdl.org","subject":"Re: [PATCH 3/3] Tie it all together: \"git log\"","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-02-28T20:59:03Z","receivedAt":"2006-02-28T20:59:03Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 28 Feb 2006, Linus Torvalds wrote:\n> \n> Again, this may not do exactly what the current \"git log\" does. That's not \n> the point. The point is to introduce the fundamental functionality, so \n> that people can play with this and improve on it, and fix any of my stupid \n> bugs.\n\nBtw, before anybody even pipes up: the missing piece here is the nasty \n\"filter_commit()\" that rev-list.c does, and that really should be moved \ninto revision.c, and this is where I hit on the \"--merge-order\" issues.\n\nSo for example, if you do \"git log -- <filename>\" with the new git, it \nwon't filter out the commits that just change the passed-in <filename> \nproperly, because the filtering code still exists only in git-rev-list \n(even if revision.c now does the traversal).\n\nSame goes for the max-count-based filtering, for the same reason.\n\nSo the \"process_commit()\" handling should be moved into \"get_revision()\", \nbut since the merge-order code also hooks into it...\n\nAnyway, apart from that issue (which I think should be trivial to sort out \nif we accept breaking --merge-order), the rest looks like it should just \nget more testing and handling of the few missing flags from rev-parse in \nrevision.c, and it should be good.\n\n\t\tLinus\n"},{"id":"16924","messageId":"7vr75nm8cl.fsf@assigned-by-dhcp.cox.net","threadId":"3498","inReplyTo":"Pine.LNX.4.64.0602281251390.22647@g5.osdl.org","subject":"Re: [PATCH 3/3] Tie it all together: \"git log\"","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-02-28T22:22:50Z","receivedAt":"2006-02-28T22:22:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> Anyway, apart from that issue (which I think should be trivial to sort out \n> if we accept breaking --merge-order), the rest looks like it should just \n> get more testing and handling of the few missing flags from rev-parse in \n> revision.c, and it should be good.\n\nI would say we should just rip merge-order out.  Who uses it,\nand why does it not work with topo-order, again?\n"},{"id":"16926","messageId":"Pine.LNX.4.64.0602281504280.22647@g5.osdl.org","threadId":"3498","inReplyTo":"7vr75nm8cl.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 3/3] Tie it all together: \"git log\"","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-02-28T23:07:20Z","receivedAt":"2006-02-28T23:07:20Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 28 Feb 2006, Junio C Hamano wrote:\n> \n> I would say we should just rip merge-order out.  Who uses it,\n> and why does it not work with topo-order, again?\n\nWell, assuming breaking --merge-order is fine, here's a patch (on top of \nthe other ones) that makes\n\n\tgit log <filename>\n\nactually work, as far as I can tell. \n\nI didn't add the logic for --before/--after flags, but that should be \npretty trivial, and is independent of this anyway.\n\nPerhaps more importantly, I didn't remove the tests that now start \nfailing, nor did I remove the actual code to do --merge-order ;/\n\n\t\tLinus\n\n----\ndiff --git a/rev-list.c b/rev-list.c\nindex 94f22dd..6993b1a 100644\n--- a/rev-list.c\n+++ b/rev-list.c\n@@ -8,10 +8,9 @@\n #include \"diff.h\"\n #include \"revision.h\"\n \n-/* bits #0-2 in revision.h */\n+/* bits #0-3 in revision.h */\n \n-#define COUNTED\t\t(1u << 3)\n-#define SHOWN\t\t(1u << 4)\n+#define COUNTED\t\t(1u << 4)\n #define TMP_MARK\t(1u << 5) /* for isolated cases; clean after use */\n \n static const char rev_list_usage[] =\n@@ -25,7 +24,6 @@ static const char rev_list_usage[] =\n \"    --remove-empty\\n\"\n \"    --all\\n\"\n \"  ordering output:\\n\"\n-\"    --merge-order [ --show-breaks ]\\n\"\n \"    --topo-order\\n\"\n \"    --date-order\\n\"\n \"  formatting output:\\n\"\n@@ -47,22 +45,9 @@ static int show_parents = 0;\n static int hdr_termination = 0;\n static const char *commit_prefix = \"\";\n static enum cmit_fmt commit_format = CMIT_FMT_RAW;\n-static int merge_order = 0;\n-static int show_breaks = 0;\n-static int stop_traversal = 0;\n-static int no_merges = 0;\n \n static void show_commit(struct commit *commit)\n {\n-\tcommit->object.flags |= SHOWN;\n-\tif (show_breaks) {\n-\t\tcommit_prefix = \"| \";\n-\t\tif (commit->object.flags & DISCONTINUITY) {\n-\t\t\tcommit_prefix = \"^ \";     \n-\t\t} else if (commit->object.flags & BOUNDARY) {\n-\t\t\tcommit_prefix = \"= \";\n-\t\t} \n-        }        \t\t\n \tprintf(\"%s%s\", commit_prefix, sha1_to_hex(commit->object.sha1));\n \tif (show_parents) {\n \t\tstruct commit_list *parents = commit->parents;\n@@ -96,73 +81,6 @@ static void show_commit(struct commit *c\n \tfflush(stdout);\n }\n \n-static int rewrite_one(struct commit **pp)\n-{\n-\tfor (;;) {\n-\t\tstruct commit *p = *pp;\n-\t\tif (p->object.flags & (TREECHANGE | UNINTERESTING))\n-\t\t\treturn 0;\n-\t\tif (!p->parents)\n-\t\t\treturn -1;\n-\t\t*pp = p->parents->item;\n-\t}\n-}\n-\n-static void rewrite_parents(struct commit *commit)\n-{\n-\tstruct commit_list **pp = &commit->parents;\n-\twhile (*pp) {\n-\t\tstruct commit_list *parent = *pp;\n-\t\tif (rewrite_one(&parent->item) < 0) {\n-\t\t\t*pp = parent->next;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tpp = &parent->next;\n-\t}\n-}\n-\n-static int filter_commit(struct commit * commit)\n-{\n-\tif (stop_traversal && (commit->object.flags & BOUNDARY))\n-\t\treturn STOP;\n-\tif (commit->object.flags & (UNINTERESTING|SHOWN))\n-\t\treturn CONTINUE;\n-\tif (revs.min_age != -1 && (commit->date > revs.min_age))\n-\t\treturn CONTINUE;\n-\tif (revs.max_age != -1 && (commit->date < revs.max_age)) {\n-\t\tstop_traversal=1;\n-\t\treturn CONTINUE;\n-\t}\n-\tif (no_merges && (commit->parents && commit->parents->next))\n-\t\treturn CONTINUE;\n-\tif (revs.paths && revs.dense) {\n-\t\tif (!(commit->object.flags & TREECHANGE))\n-\t\t\treturn CONTINUE;\n-\t\trewrite_parents(commit);\n-\t}\n-\treturn DO;\n-}\n-\n-static int process_commit(struct commit * commit)\n-{\n-\tint action=filter_commit(commit);\n-\n-\tif (action == STOP) {\n-\t\treturn STOP;\n-\t}\n-\n-\tif (action == CONTINUE) {\n-\t\treturn CONTINUE;\n-\t}\n-\n-\tif (revs.max_count != -1 && !revs.max_count--)\n-\t\treturn STOP;\n-\n-\tshow_commit(commit);\n-\n-\treturn CONTINUE;\n-}\n-\n static struct object_list **process_blob(struct blob *blob,\n \t\t\t\t\t struct object_list **p,\n \t\t\t\t\t struct name_path *path,\n@@ -219,8 +137,7 @@ static void show_commit_list(struct rev_\n \n \twhile ((commit = get_revision(revs)) != NULL) {\n \t\tp = process_tree(commit->tree, p, NULL, \"\");\n-\t\tif (process_commit(commit) == STOP)\n-\t\t\tbreak;\n+\t\tshow_commit(commit);\n \t}\n \tfor (pending = revs->pending_objects; pending; pending = pending->next) {\n \t\tstruct object *obj = pending->item;\n@@ -416,10 +333,6 @@ int main(int argc, const char **argv)\n \t\t\t\tcommit_prefix = \"commit \";\n \t\t\tcontinue;\n \t\t}\n-\t\tif (!strncmp(arg, \"--no-merges\", 11)) {\n-\t\t\tno_merges = 1;\n-\t\t\tcontinue;\n-\t\t}\n \t\tif (!strcmp(arg, \"--parents\")) {\n \t\t\tshow_parents = 1;\n \t\t\tcontinue;\n@@ -428,14 +341,6 @@ int main(int argc, const char **argv)\n \t\t\tbisect_list = 1;\n \t\t\tcontinue;\n \t\t}\n-\t\tif (!strcmp(arg, \"--merge-order\")) {\n-\t\t        merge_order = 1;\n-\t\t\tcontinue;\n-\t\t}\n-\t\tif (!strcmp(arg, \"--show-breaks\")) {\n-\t\t\tshow_breaks = 1;\n-\t\t\tcontinue;\n-\t\t}\n \t\tusage(rev_list_usage);\n \n \t}\n@@ -456,17 +361,7 @@ int main(int argc, const char **argv)\n \tsave_commit_buffer = verbose_header;\n \ttrack_object_refs = 0;\n \n-\tif (!merge_order) {\n-\t\tshow_commit_list(&revs);\n-\t} else {\n-#ifndef NO_OPENSSL\n-\t\tif (sort_list_in_merge_order(list, &process_commit)) {\n-\t\t\tdie(\"merge order sort failed\\n\");\n-\t\t}\n-#else\n-\t\tdie(\"merge order sort unsupported, OpenSSL not linked\");\n-#endif\n-\t}\n+\tshow_commit_list(&revs);\n \n \treturn 0;\n }\ndiff --git a/revision.c b/revision.c\nindex fb728c1..f98fae9 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -381,6 +381,9 @@ static void limit_list(struct rev_info *\n \tstruct commit_list *newlist = NULL;\n \tstruct commit_list **p = &newlist;\n \n+\tif (revs->paths)\n+\t\tdiff_tree_setup_paths(revs->paths);\n+\n \twhile (list) {\n \t\tstruct commit_list *entry = list;\n \t\tstruct commit *commit = list->item;\n@@ -436,12 +439,13 @@ static void handle_all(struct rev_info *\n  * Parse revision information, filling in the \"rev_info\" structure,\n  * and removing the used arguments from the argument list.\n  *\n- * Returns the number of arguments left (\"new argc\").\n+ * Returns the number of arguments left that weren't recognized\n+ * (which are also moved to the head of the argument list)\n  */\n int setup_revisions(int argc, const char **argv, struct rev_info *revs, const char *def)\n {\n \tint i, flags, seen_dashdash;\n-\tconst char **unrecognized = argv+1;\n+\tconst char **unrecognized = argv + 1;\n \tint left = 1;\n \n \tmemset(revs, 0, sizeof(*revs));\n@@ -525,6 +529,10 @@ int setup_revisions(int argc, const char\n \t\t\t\trevs->remove_empty_trees = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (!strncmp(arg, \"--no-merges\", 11)) {\n+\t\t\t\trevs->no_merges = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (!strcmp(arg, \"--objects\")) {\n \t\t\t\trevs->tag_objects = 1;\n \t\t\t\trevs->tree_objects = 1;\n@@ -601,14 +609,11 @@ int setup_revisions(int argc, const char\n \t}\n \tif (revs->paths)\n \t\trevs->limited = 1;\n-\t*unrecognized = NULL;\n \treturn left;\n }\n \n void prepare_revision_walk(struct rev_info *revs)\n {\n-\tif (revs->paths)\n-\t\tdiff_tree_setup_paths(revs->paths);\n \tsort_by_date(&revs->commits);\n \tif (revs->limited)\n \t\tlimit_list(revs);\n@@ -616,11 +621,67 @@ void prepare_revision_walk(struct rev_in\n \t\tsort_in_topological_order(&revs->commits, revs->lifo);\n }\n \n+static int rewrite_one(struct commit **pp)\n+{\n+\tfor (;;) {\n+\t\tstruct commit *p = *pp;\n+\t\tif (p->object.flags & (TREECHANGE | UNINTERESTING))\n+\t\t\treturn 0;\n+\t\tif (!p->parents)\n+\t\t\treturn -1;\n+\t\t*pp = p->parents->item;\n+\t}\n+}\n+\n+static void rewrite_parents(struct commit *commit)\n+{\n+\tstruct commit_list **pp = &commit->parents;\n+\twhile (*pp) {\n+\t\tstruct commit_list *parent = *pp;\n+\t\tif (rewrite_one(&parent->item) < 0) {\n+\t\t\t*pp = parent->next;\n+\t\t\tcontinue;\n+\t\t}\n+\t\tpp = &parent->next;\n+\t}\n+}\n+\n struct commit *get_revision(struct rev_info *revs)\n {\n-\tif (!revs->commits)\n+\tstruct commit_list *list = revs->commits;\n+\tstruct commit *commit;\n+\n+\tif (!list)\n \t\treturn NULL;\n-\treturn pop_most_recent_commit(&revs->commits, SEEN);\n-}\n \n+\t/* Check the max_count ... */\n+\tcommit = list->item;\n+\tswitch (revs->max_count) {\n+\tcase -1:\n+\t\tbreak;\n+\tcase 0:\n+\t\treturn NULL;\n+\tdefault:\n+\t\trevs->max_count--;\n+\t}\n \n+\tdo {\n+\t\tcommit = pop_most_recent_commit(&revs->commits, SEEN);\n+\t\tif (commit->object.flags & (UNINTERESTING|SHOWN))\n+\t\t\tcontinue;\n+\t\tif (revs->min_age != -1 && (commit->date > revs->min_age))\n+\t\t\tcontinue;\n+\t\tif (revs->max_age != -1 && (commit->date < revs->max_age))\n+\t\t\treturn NULL;\n+\t\tif (revs->no_merges && commit->parents && commit->parents->next)\n+\t\t\tcontinue;\n+\t\tif (revs->paths && revs->dense) {\n+\t\t\tif (!(commit->object.flags & TREECHANGE))\n+\t\t\t\tcontinue;\n+\t\t\trewrite_parents(commit);\n+\t\t}\n+\t\tcommit->object.flags |= SHOWN;\n+\t\treturn commit;\n+\t} while (revs->commits);\n+\treturn NULL;\n+}\ndiff --git a/revision.h b/revision.h\nindex 0bed3c0..0043c16 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -4,6 +4,7 @@\n #define SEEN\t\t(1u<<0)\n #define UNINTERESTING   (1u<<1)\n #define TREECHANGE\t(1u<<2)\n+#define SHOWN\t\t(1u<<3)\n \n struct rev_info {\n \t/* Starting list */\n@@ -16,6 +17,7 @@ struct rev_info {\n \n \t/* Traversal flags */\n \tunsigned int\tdense:1,\n+\t\t\tno_merges:1,\n \t\t\tremove_empty_trees:1,\n \t\t\tlifo:1,\n \t\t\ttopo_order:1,\n"},{"id":"16927","messageId":"46a038f90602281538m90c4d04pbb6f277e3bec89e8@mail.gmail.com","threadId":"3498","inReplyTo":"7vr75nm8cl.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 3/3] Tie it all together: \"git log\"","fromName":"Martin Langhoff","fromEmail":"martin.langhoff@gmail.com","sentAt":"2006-02-28T23:38:53Z","receivedAt":"2006-02-28T23:38:53Z","isPatch":true,"sender":{"key":"martin.langhoff@gmail.com","avatar":"https://gravatar.com/avatar/1e3f311b6c4c15836501901ca58f8c0b0667246488084ba524d8bc9867e22fd9?d=mp&s=160"},"body":"On 3/1/06, Junio C Hamano <junkio@cox.net> wrote:\n> I would say we should just rip merge-order out.  Who uses it,\n> and why does it not work with topo-order, again?\n\nIIRC archimport uses it, but there's no reason why topo-order wouldn't work.\n\ncheers,\n\n\nmartin\n"},{"id":"16935","messageId":"7vpsl6lepz.fsf@assigned-by-dhcp.cox.net","threadId":"3498","inReplyTo":"46a038f90602281538m90c4d04pbb6f277e3bec89e8@mail.gmail.com","subject":"Re: [PATCH 3/3] Tie it all together: \"git log\"","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-03-01T09:02:48Z","receivedAt":"2006-03-01T09:02:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Martin Langhoff\" <martin.langhoff@gmail.com> writes:\n\n> On 3/1/06, Junio C Hamano <junkio@cox.net> wrote:\n>> I would say we should just rip merge-order out.  Who uses it,\n>> and why does it not work with topo-order, again?\n>\n> IIRC archimport uses it, but there's no reason why topo-order wouldn't work.\n\nThanks.  I'll push out a few patches on top of Linus' series,\nwith s/merge-order/topo-order/ on archimport.\n"},{"id":"16940","messageId":"7vhd6igxol.fsf@assigned-by-dhcp.cox.net","threadId":"3498","inReplyTo":"Pine.LNX.4.64.0602281504280.22647@g5.osdl.org","subject":"[PATCH 1/2] git-log (internal): add approxidate.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-03-01T12:24:26Z","receivedAt":"2006-03-01T12:24:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Next will be the pretty-print format.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n\n---\n  Linus Torvalds <torvalds@osdl.org> writes:\n\n  > I didn't add the logic for --before/--after flags, but that should be \n  > pretty trivial, and is independent of this anyway.\n  >\n  > Perhaps more importantly, I didn't remove the tests that now start \n  > failing, nor did I remove the actual code to do --merge-order ;/\n\n  I've done the janitorial, and have two more on top.  Here is the\n  first one.  I'd appreciate comments on the second one.\n\n revision.c |   20 ++++++++++++++++++++\n 1 files changed, 20 insertions(+), 0 deletions(-)\n\n2eba658eaffdf4c5c9d0767b49e4c27d7281cda6\ndiff --git a/revision.c b/revision.c\nindex c84f146..4885871 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -492,6 +492,26 @@ int setup_revisions(int argc, const char\n \t\t\t\trevs->limited = 1;\n \t\t\t\tcontinue;\n \t\t\t}\n+\t\t\tif (!strncmp(arg, \"--since=\", 8)) {\n+\t\t\t\trevs->max_age = approxidate(arg + 8);\n+\t\t\t\trevs->limited = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strncmp(arg, \"--after=\", 8)) {\n+\t\t\t\trevs->max_age = approxidate(arg + 8);\n+\t\t\t\trevs->limited = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strncmp(arg, \"--before=\", 9)) {\n+\t\t\t\trevs->min_age = approxidate(arg + 9);\n+\t\t\t\trevs->limited = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (!strncmp(arg, \"--until=\", 8)) {\n+\t\t\t\trevs->min_age = approxidate(arg + 8);\n+\t\t\t\trevs->limited = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n \t\t\tif (!strcmp(arg, \"--all\")) {\n \t\t\t\thandle_all(revs, flags);\n \t\t\t\tcontinue;\n-- \n1.2.3.g9425\n"},{"id":"16941","messageId":"7vbqwqgxo8.fsf@assigned-by-dhcp.cox.net","threadId":"3498","inReplyTo":"Pine.LNX.4.64.0602281504280.22647@g5.osdl.org","subject":"[PATCH 2/2] git-log (internal): more options.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-03-01T12:24:39Z","receivedAt":"2006-03-01T12:24:39Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This ports the following options from rev-list based git-log\nimplementation:\n\n * -<n>, -n<n>, and -n <n>.  I am still wondering if we want\n    this natively supported by setup_revisions(), which already\n    takes --max-count.  We may want to move them in the next\n    round.  Also I am not sure if we can get away with not\n    setting revs->limited when we set max-count.  The latest\n    rev-list.c and revision.c in this series do not, so I left\n    them as they are.\n\n * --pretty and --pretty=<fmt>.\n\n * --abbrev=<n> and --no-abbrev.\n\nThe previous commit already handles time-based limiters\n(--since, --until and friends).  The remaining things that\nrev-list based git-log happens to do are not useful in a pure\nlog-viewing purposes, and not ported:\n\n * --bisect (obviously).\n\n * --header.  I am actually in favor of doing the NUL\n   terminated record format, but rev-list based one always\n   passed --pretty, which defeated this option.  Maybe next\n   round.\n\n * --parents.  I do not think of a reason a log viewer wants\n   this.  The flag is primarily for feeding squashed history\n   via pipe to downstream tools.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n\n---\n\n * comes on top of --since/--until patch which in turn comes on\n   top of janitorial \"remove merge-order\" change.\n\n git.c      |   70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n rev-list.c |    5 ++--\n revision.h |    1 +\n 3 files changed, 72 insertions(+), 4 deletions(-)\n\n4e365d2558356cd091ebf57f689c477a86822d53\ndiff --git a/git.c b/git.c\nindex b0da6b1..bf68dac 100644\n--- a/git.c\n+++ b/git.c\n@@ -256,12 +256,80 @@ static int cmd_log(int argc, char **argv\n \tstruct rev_info rev;\n \tstruct commit *commit;\n \tchar *buf = xmalloc(LOGSIZE);\n+\tstatic enum cmit_fmt commit_format = CMIT_FMT_DEFAULT;\n+\tint abbrev = DEFAULT_ABBREV;\n+\tint show_parents = 0;\n+\tconst char *commit_prefix = \"commit \";\n \n \targc = setup_revisions(argc, argv, &rev, \"HEAD\");\n+\twhile (1 < argc) {\n+\t\tchar *arg = argv[1];\n+\t\t/* accept -<digit>, like traditilnal \"head\" */\n+\t\tif ((*arg == '-') && isdigit(arg[1])) {\n+\t\t\trev.max_count = atoi(arg + 1);\n+\t\t}\n+\t\telse if (!strcmp(arg, \"-n\")) {\n+\t\t\tif (argc < 2)\n+\t\t\t\tdie(\"-n requires an argument\");\n+\t\t\trev.max_count = atoi(argv[2]);\n+\t\t\targc--; argv++;\n+\t\t}\n+\t\telse if (!strncmp(arg,\"-n\",2)) {\n+\t\t\trev.max_count = atoi(arg + 2);\n+\t\t}\n+\t\telse if (!strncmp(arg, \"--pretty\", 8)) {\n+\t\t\tcommit_format = get_commit_format(arg + 8);\n+\t\t\tif (commit_format == CMIT_FMT_ONELINE)\n+\t\t\t\tcommit_prefix = \"\";\n+\t\t}\n+\t\telse if (!strcmp(arg, \"--parents\")) {\n+\t\t\tshow_parents = 1;\n+\t\t}\n+\t\telse if (!strcmp(arg, \"--no-abbrev\")) {\n+\t\t\tabbrev = 0;\n+\t\t}\n+\t\telse if (!strncmp(arg, \"--abbrev=\", 9)) {\n+\t\t\tabbrev = strtoul(arg + 9, NULL, 10);\n+\t\t\tif (abbrev && abbrev < MINIMUM_ABBREV)\n+\t\t\t\tabbrev = MINIMUM_ABBREV;\n+\t\t\telse if (40 < abbrev)\n+\t\t\t\tabbrev = 40;\n+\t\t}\n+\t\telse\n+\t\t\tdie(\"unrecognized argument: %s\", arg);\n+\t\targc--; argv++;\n+\t}\n+\n \tprepare_revision_walk(&rev);\n \tsetup_pager();\n \twhile ((commit = get_revision(&rev)) != NULL) {\n-\t\tpretty_print_commit(CMIT_FMT_DEFAULT, commit, ~0, buf, LOGSIZE, 18);\n+\t\tprintf(\"%s%s\", commit_prefix,\n+\t\t       sha1_to_hex(commit->object.sha1));\n+\t\tif (show_parents) {\n+\t\t\tstruct commit_list *parents = commit->parents;\n+\t\t\twhile (parents) {\n+\t\t\t\tstruct object *o = &(parents->item->object);\n+\t\t\t\tparents = parents->next;\n+\t\t\t\tif (o->flags & TMP_MARK)\n+\t\t\t\t\tcontinue;\n+\t\t\t\tprintf(\" %s\", sha1_to_hex(o->sha1));\n+\t\t\t\to->flags |= TMP_MARK;\n+\t\t\t}\n+\t\t\t/* TMP_MARK is a general purpose flag that can\n+\t\t\t * be used locally, but the user should clean\n+\t\t\t * things up after it is done with them.\n+\t\t\t */\n+\t\t\tfor (parents = commit->parents;\n+\t\t\t     parents;\n+\t\t\t     parents = parents->next)\n+\t\t\t\tparents->item->object.flags &= ~TMP_MARK;\n+\t\t}\n+\t\tif (commit_format == CMIT_FMT_ONELINE)\n+\t\t\tputchar(' ');\n+\t\telse\n+\t\t\tputchar('\\n');\n+\t\tpretty_print_commit(commit_format, commit, ~0, buf,\n+\t\t\t\t    LOGSIZE, abbrev);\n \t\tprintf(\"%s\\n\", buf);\n \t}\n \tfree(buf);\ndiff --git a/rev-list.c b/rev-list.c\nindex 6af8d86..8e4d83e 100644\n--- a/rev-list.c\n+++ b/rev-list.c\n@@ -7,10 +7,9 @@\n #include \"diff.h\"\n #include \"revision.h\"\n \n-/* bits #0-3 in revision.h */\n+/* bits #0-4 in revision.h */\n \n-#define COUNTED\t\t(1u << 4)\n-#define TMP_MARK\t(1u << 5) /* for isolated cases; clean after use */\n+#define COUNTED\t\t(1u<<5)\n \n static const char rev_list_usage[] =\n \"git-rev-list [OPTION] <commit-id>... [ -- paths... ]\\n\"\ndiff --git a/revision.h b/revision.h\nindex 0043c16..31e8f61 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -5,6 +5,7 @@\n #define UNINTERESTING   (1u<<1)\n #define TREECHANGE\t(1u<<2)\n #define SHOWN\t\t(1u<<3)\n+#define TMP_MARK\t(1u<<4) /* for isolated cases; clean after use */\n \n struct rev_info {\n \t/* Starting list */\n-- \n1.2.3.g9425\n"},{"id":"16947","messageId":"Pine.LNX.4.64.0603010730520.22647@g5.osdl.org","threadId":"3498","inReplyTo":"7vbqwqgxo8.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 2/2] git-log (internal): more options.","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-03-01T15:43:26Z","receivedAt":"2006-03-01T15:43:26Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 1 Mar 2006, Junio C Hamano wrote:\n>\n> This ports the following options from rev-list based git-log\n> implementation:\n> \n>  * -<n>, -n<n>, and -n <n>.  I am still wondering if we want\n>     this natively supported by setup_revisions(), which already\n>     takes --max-count.  We may want to move them in the next\n>     round.  Also I am not sure if we can get away with not\n>     setting revs->limited when we set max-count.  The latest\n>     rev-list.c and revision.c in this series do not, so I left\n>     them as they are.\n> \n>  * --pretty and --pretty=<fmt>.\n> \n>  * --abbrev=<n> and --no-abbrev.\n\nLooks good.\n\nI _suspect_ that we want to handle them all in setup_revision(), but I \nwasn't sure, so I left them in rev-list.c originally.\n\nMost helpers that want a list of commits probably want the printing \noptions too, and the ones that do not probably simply don't care (ie if \nthey silently pass a \"--pretty=raw\" without it affecting anything, who \nreally cares?)\n\n> The previous commit already handles time-based limiters\n> (--since, --until and friends).  The remaining things that\n> rev-list based git-log happens to do are not useful in a pure\n> log-viewing purposes, and not ported:\n> \n>  * --bisect (obviously).\n> \n>  * --header.  I am actually in favor of doing the NUL\n>    terminated record format, but rev-list based one always\n>    passed --pretty, which defeated this option.  Maybe next\n>    round.\n> \n>  * --parents.  I do not think of a reason a log viewer wants\n>    this.  The flag is primarily for feeding squashed history\n>    via pipe to downstream tools.\n\nI can actually imagine using \"--parents\" as a way of parsing both the \ncommit log and the history. Of course, any such use is likely in a script, \nat which point the script probably doesn't actually want \"git log\", but \njust a raw \"git-rev-list\".\n\nAfter all, the only _real_ difference between \"git log\" and \"git-rev-list\" \nis the purely syntactic one (things like defaulting to HEAD in \"git log\" \nand requiring revisions in git-rev-list), and the use of PAGER.\n\nTo me, the question whether a flag would be parsed in the \"revision.c\" \nlibrary or in the \"rev-list.c\" binary was more a question of whether that \nflag makes sense for other things than just \"git log\". \n\nFor example, \"git whatchanged\" and \"git diff\" could both use \nsetup_revision(), although \"git diff\" wouldn't actually _walk_ the \nrevisions (it would just look at the \"revs->commits\" list to see what was \npassed in).\n\n\"git whatchanged\" would obviously take all the same flags \"git log\" does, \nand \"git diff\" could take them and just test the values for sanity (ie \nerror out if min/max_date is not -1, for example).\n\n\"git show\" is like a \"git-whatchanged\" except it wouldn't walk the diffs \n(I considered adding a \"--nowalk\" option to setup_revisions(), which would \njust suppress the \"add_parents_to_list()\" entirely)\n\n\t\t\tLinus\n"},{"id":"16974","messageId":"7vwtfedj5j.fsf@assigned-by-dhcp.cox.net","threadId":"3498","inReplyTo":"Pine.LNX.4.64.0603010730520.22647@g5.osdl.org","subject":"Re: [PATCH 2/2] git-log (internal): more options.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-03-01T20:06:32Z","receivedAt":"2006-03-01T20:06:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> Most helpers that want a list of commits probably want the printing \n> options too, and the ones that do not probably simply don't care (ie if \n> they silently pass a \"--pretty=raw\" without it affecting anything, who \n> really cares?)\n\nPerhaps (meaning, agree in general but not 100% convinced and\nhaven't made up my mind yet).\n\n> I can actually imagine using \"--parents\" as a way of parsing both the \n> commit log and the history. Of course, any such use is likely in a script, \n> at which point the script probably doesn't actually want \"git log\", but \n> just a raw \"git-rev-list\".\n\nYes, that is exactly why I did not see why \"log viewer\" wants --parents.\n\n> To me, the question whether a flag would be parsed in the \"revision.c\" \n> library or in the \"rev-list.c\" binary was more a question of whether that \n> flag makes sense for other things than just \"git log\". \n\nGood to know we are in agreement (iow, I wasn't totally off the\nmark) that revision.c should handle things that are common.\nThat means:\n\n * --bisect and --parents are for scripted use only and do not\n   concern log viewer, so we would leave them in rev-list.\n\n * --header is good for anything that shows more than one\n   record, so it may be worthwhile to have it in generic.\n\n> For example, \"git whatchanged\" and \"git diff\" could both use \n> setup_revision(), although \"git diff\" wouldn't actually _walk_ the \n> revisions (it would just look at the \"revs->commits\" list to see what was \n> passed in).\n>\n> \"git whatchanged\" would obviously take all the same flags \"git log\" does, \n> and \"git diff\" could take them and just test the values for sanity (ie \n> error out if min/max_date is not -1, for example).\n\nPerhaps.\n\n> \"git show\" is like a \"git-whatchanged\" except it wouldn't walk the diffs \n> (I considered adding a \"--nowalk\" option to setup_revisions(), which would \n> just suppress the \"add_parents_to_list()\" entirely)\n\nUmm.  The current \"git show -4\" walks and I find the behaviour\nuseful.  They are the same program with different defaults.\n"}]}