revision: keep track of the end-user input from the command line

Given a complex set of revision specifiers on the command line, it is too late to look at the flags of the objects in the initial traversal list at the beginning of limit_list() in order to determine what the objects the end-user explicitly listed on the command line were. The process to move objects from the pending array to the traversal list may have marked objects that are not mentioned as UNINTERESTING, when handle_commit() marked the parents of UNINTERESTING commits mentioned on the command line by calling mark_parents_uninteresting(). This made "rev-list --ancestry-path ^A ..." to mistakenly list commits that are descendants of A's parents but that are not descendants of A itself, as ^A from the command line causes A and its parents marked as UNINTERESTING before coming to limit_list(), and we try to enumerate the commits that are descendants of these commits that are UNINTERESTING before we start walking the history. It actually is too late even if we inspected the pending object array before calling prepare_revision_walk(), as some of the same objects might have been mentioned twice, once as positive and another time as negative. The "rev-list --some-option A --not --all" command may want to notice, even if the resulting set is empty, that the user showed some interest in "A" and do something special about it. Prepare a separate array to keep track of what syntactic element was used to cause each object to appear in the pending array from the command line, and populate it as setup_revisions() parses the command line. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Aug 25, 2011 at 17:35 UTC 281eee473000e5bd46e59d636626132dc9ea85cb
2 files changed +53 -4
revision.c
+33 -4
@@ -797,6 +797,23 @@ static int limit_list(struct rev_info *revs)
797 return 0;
798 }
799
800 +static void add_rev_cmdline(struct rev_info *revs,
801 + struct object *item,
802 + const char *name,
803 + int whence,
804 + unsigned flags)
805 +{
806 + struct rev_cmdline_info *info = &revs->cmdline;
807 + int nr = info->nr;
808 +
809 + ALLOC_GROW(info->rev, nr + 1, info->alloc);
810 + info->rev[nr].item = item;
811 + info->rev[nr].name = name;
812 + info->rev[nr].whence = whence;
813 + info->rev[nr].flags = flags;
814 + info->nr++;
815 +}
816 +
817 struct all_refs_cb {
818 int all_flags;
819 int warned_bad_reflog;
@@ -809,6 +826,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, int flag,
826 struct all_refs_cb *cb = cb_data;
827 struct object *object = get_reference(cb->all_revs, path, sha1,
828 cb->all_flags);
829 + add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
830 add_pending_object(cb->all_revs, object, path);
831 return 0;
832 }
@@ -835,6 +853,7 @@ static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
853 struct object *o = parse_object(sha1);
854 if (o) {
855 o->flags |= cb->all_flags;
856 + /* ??? CMDLINEFLAGS ??? */
857 add_pending_object(cb->all_revs, o, "");
858 }
859 else if (!cb->warned_bad_reflog) {
@@ -871,12 +890,13 @@ static void handle_reflog(struct rev_info *revs, unsigned flags)
890 for_each_reflog(handle_one_reflog, &cb);
891 }
892
874 -static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
893 +static int add_parents_only(struct rev_info *revs, const char *arg_, int flags)
894 {
895 unsigned char sha1[20];
896 struct object *it;
897 struct commit *commit;
898 struct commit_list *parents;
899 + const char *arg = arg_;
900
901 if (*arg == '^') {
902 flags ^= UNINTERESTING;
@@ -898,6 +918,7 @@ static int add_parents_only(struct rev_info *revs, const char *arg, int flags)
918 for (parents = commit->parents; parents; parents = parents->next) {
919 it = &parents->item->object;
920 it->flags |= flags;
921 + add_rev_cmdline(revs, it, arg_, REV_CMD_PARENTS_ONLY, flags);
922 add_pending_object(revs, it, arg);
923 }
924 return 1;
@@ -987,7 +1008,7 @@ static void prepare_show_merge(struct rev_info *revs)
1008 revs->limited = 1;
1009 }
1010
990 -int handle_revision_arg(const char *arg, struct rev_info *revs,
1011 +int handle_revision_arg(const char *arg_, struct rev_info *revs,
1012 int flags,
1013 int cant_be_filename)
1014 {
@@ -996,6 +1017,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
1017 struct object *object;
1018 unsigned char sha1[20];
1019 int local_flags;
1020 + const char *arg = arg_;
1021
1022 dotdot = strstr(arg, "..");
1023 if (dotdot) {
@@ -1004,6 +1026,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
1026 const char *this = arg;
1027 int symmetric = *next == '.';
1028 unsigned int flags_exclude = flags ^ UNINTERESTING;
1029 + unsigned int a_flags;
1030
1031 *dotdot = 0;
1032 next += symmetric;
@@ -1036,10 +1059,15 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
1059 add_pending_commit_list(revs, exclude,
1060 flags_exclude);
1061 free_commit_list(exclude);
1039 - a->object.flags |= flags | SYMMETRIC_LEFT;
1062 + a_flags = flags | SYMMETRIC_LEFT;
1063 } else
1041 - a->object.flags |= flags_exclude;
1064 + a_flags = flags_exclude;
1065 + a->object.flags |= a_flags;
1066 b->object.flags |= flags;
1067 + add_rev_cmdline(revs, &a->object, this,
1068 + REV_CMD_LEFT, a_flags);
1069 + add_rev_cmdline(revs, &b->object, next,
1070 + REV_CMD_RIGHT, flags);
1071 add_pending_object(revs, &a->object, this);
1072 add_pending_object(revs, &b->object, next);
1073 return 0;
@@ -1070,6 +1098,7 @@ int handle_revision_arg(const char *arg, struct rev_info *revs,
1098 if (!cant_be_filename)
1099 verify_non_filename(revs->prefix, arg);
1100 object = get_reference(revs, arg, sha1, flags ^ local_flags);
1101 + add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
1102 add_pending_object_with_mode(revs, object, arg, mode);
1103 return 0;
1104 }
revision.h
+20
@@ -23,6 +23,23 @@ struct rev_info;
23 struct log_info;
24 struct string_list;
25
26 +struct rev_cmdline_info {
27 + unsigned int nr;
28 + unsigned int alloc;
29 + struct rev_cmdline_entry {
30 + struct object *item;
31 + const char *name;
32 + enum {
33 + REV_CMD_REF,
34 + REV_CMD_PARENTS_ONLY,
35 + REV_CMD_LEFT,
36 + REV_CMD_RIGHT,
37 + REV_CMD_REV
38 + } whence;
39 + unsigned flags;
40 + } *rev;
41 +};
42 +
43 struct rev_info {
44 /* Starting list */
45 struct commit_list *commits;
@@ -31,6 +48,9 @@ struct rev_info {
48 /* Parents of shown commits */
49 struct object_array boundary_commits;
50
51 + /* The end-points specified by the end user */
52 + struct rev_cmdline_info cmdline;
53 +
54 /* Basic information */
55 const char *prefix;
56 const char *def;