Skip to content

Commit 4d5fb93

Browse files
peffgitster
authored andcommitted
revision: make handle_dotdot() interface less confusing
There are two very subtle bits to the way we parse ".." (and "...") range operators: 1. In handle_dotdot_1(), we assume that the incoming arguments "dotdot" and "arg" are part of the same string, with the first digit of the range-operator blanked to a NUL. Then when we want the full name (e.g., to report an error), we replace the NUL with a dot to restore the original string. 2. In handle_dotdot(), we take in a const string, but then we modify it by overwriting the range operator with a NUL. This has worked OK in practice since we tend to pass in buffers that are actually writeable (including argv), but segfaults with something like: handle_revision_arg("..HEAD", &revs, 0, 0); On top of that, building with recent versions of glibc causes the compiler to complain, because it notices when we use strchr() or strstr() to launder away constness (basically detecting the possibility of the segfault above via the type system). Instead of munging the buffer, let's instead make a temporary copy of the left-hand side of the range operator. That avoids any const violations, and lets us pass around the parsed elements independently: the left-hand side, the right-hand side, the number of dots (via the "symmetric" flag), and the original full string for error messages. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 41688c1 commit 4d5fb93

1 file changed

Lines changed: 19 additions & 23 deletions

File tree

revision.c

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2038,41 +2038,32 @@ static void prepare_show_merge(struct rev_info *revs)
20382038
free(prune);
20392039
}
20402040

2041-
static int dotdot_missing(const char *arg, char *dotdot,
2041+
static int dotdot_missing(const char *full_name,
20422042
struct rev_info *revs, int symmetric)
20432043
{
20442044
if (revs->ignore_missing)
20452045
return 0;
2046-
/* de-munge so we report the full argument */
2047-
*dotdot = '.';
20482046
die(symmetric
20492047
? "Invalid symmetric difference expression %s"
2050-
: "Invalid revision range %s", arg);
2048+
: "Invalid revision range %s", full_name);
20512049
}
20522050

2053-
static int handle_dotdot_1(const char *arg, char *dotdot,
2051+
static int handle_dotdot_1(const char *a_name, const char *b_name,
2052+
const char *full_name, int symmetric,
20542053
struct rev_info *revs, int flags,
20552054
int cant_be_filename,
20562055
struct object_context *a_oc,
20572056
struct object_context *b_oc)
20582057
{
2059-
const char *a_name, *b_name;
20602058
struct object_id a_oid, b_oid;
20612059
struct object *a_obj, *b_obj;
20622060
unsigned int a_flags, b_flags;
2063-
int symmetric = 0;
20642061
unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
20652062
unsigned int oc_flags = GET_OID_COMMITTISH | GET_OID_RECORD_PATH;
20662063

2067-
a_name = arg;
20682064
if (!*a_name)
20692065
a_name = "HEAD";
20702066

2071-
b_name = dotdot + 2;
2072-
if (*b_name == '.') {
2073-
symmetric = 1;
2074-
b_name++;
2075-
}
20762067
if (!*b_name)
20772068
b_name = "HEAD";
20782069

@@ -2081,15 +2072,13 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
20812072
return -1;
20822073

20832074
if (!cant_be_filename) {
2084-
*dotdot = '.';
2085-
verify_non_filename(revs->prefix, arg);
2086-
*dotdot = '\0';
2075+
verify_non_filename(revs->prefix, full_name);
20872076
}
20882077

20892078
a_obj = parse_object(revs->repo, &a_oid);
20902079
b_obj = parse_object(revs->repo, &b_oid);
20912080
if (!a_obj || !b_obj)
2092-
return dotdot_missing(arg, dotdot, revs, symmetric);
2081+
return dotdot_missing(full_name, revs, symmetric);
20932082

20942083
if (!symmetric) {
20952084
/* just A..B */
@@ -2103,7 +2092,7 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
21032092
a = lookup_commit_reference(revs->repo, &a_obj->oid);
21042093
b = lookup_commit_reference(revs->repo, &b_obj->oid);
21052094
if (!a || !b)
2106-
return dotdot_missing(arg, dotdot, revs, symmetric);
2095+
return dotdot_missing(full_name, revs, symmetric);
21072096

21082097
if (repo_get_merge_bases(the_repository, a, b, &exclude) < 0) {
21092098
commit_list_free(exclude);
@@ -2132,16 +2121,23 @@ static int handle_dotdot(const char *arg,
21322121
int cant_be_filename)
21332122
{
21342123
struct object_context a_oc = {0}, b_oc = {0};
2135-
char *dotdot = strstr(arg, "..");
2124+
const char *dotdot = strstr(arg, "..");
2125+
char *tmp;
2126+
int symmetric = 0;
21362127
int ret;
21372128

21382129
if (!dotdot)
21392130
return -1;
21402131

2141-
*dotdot = '\0';
2142-
ret = handle_dotdot_1(arg, dotdot, revs, flags, cant_be_filename,
2143-
&a_oc, &b_oc);
2144-
*dotdot = '.';
2132+
tmp = xmemdupz(arg, dotdot - arg);
2133+
dotdot += 2;
2134+
if (*dotdot == '.') {
2135+
symmetric = 1;
2136+
dotdot++;
2137+
}
2138+
ret = handle_dotdot_1(tmp, dotdot, arg, symmetric, revs, flags,
2139+
cant_be_filename, &a_oc, &b_oc);
2140+
free(tmp);
21452141

21462142
object_context_release(&a_oc);
21472143
object_context_release(&b_oc);

0 commit comments

Comments
 (0)