From ff74f7f1187cbf1b46e85ed1bde1f194991f919c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 12 Oct 2011 10:35:38 -0700 Subject: [PATCH 1/2] refs.c: move dwim_ref()/dwim_log() from sha1_name.c Both dwim_ref()/dwim_log() functions are intimately related to the ref parsing rules defined in refs.c and better fits there. Move them together with substitute_branch_name(), a file scope static helper function. Signed-off-by: Junio C Hamano --- refs.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ sha1_name.c | 85 ----------------------------------------------------- 2 files changed, 85 insertions(+), 85 deletions(-) diff --git a/refs.c b/refs.c index 832a52f781..e3692bd3d8 100644 --- a/refs.c +++ b/refs.c @@ -1067,6 +1067,91 @@ static int is_refname_available(const char *ref, const char *oldref, return 1; } +/* + * *string and *len will only be substituted, and *string returned (for + * later free()ing) if the string passed in is a magic short-hand form + * to name a branch. + */ +static char *substitute_branch_name(const char **string, int *len) +{ + struct strbuf buf = STRBUF_INIT; + int ret = interpret_branch_name(*string, &buf); + + if (ret == *len) { + size_t size; + *string = strbuf_detach(&buf, &size); + *len = size; + return (char *)*string; + } + + return NULL; +} + +int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) +{ + char *last_branch = substitute_branch_name(&str, &len); + const char **p, *r; + int refs_found = 0; + + *ref = NULL; + for (p = ref_rev_parse_rules; *p; p++) { + char fullref[PATH_MAX]; + unsigned char sha1_from_ref[20]; + unsigned char *this_result; + int flag; + + this_result = refs_found ? sha1_from_ref : sha1; + mksnpath(fullref, sizeof(fullref), *p, len, str); + r = resolve_ref(fullref, this_result, 1, &flag); + if (r) { + if (!refs_found++) + *ref = xstrdup(r); + if (!warn_ambiguous_refs) + break; + } else if ((flag & REF_ISSYMREF) && strcmp(fullref, "HEAD")) + warning("ignoring dangling symref %s.", fullref); + } + free(last_branch); + return refs_found; +} + +int dwim_log(const char *str, int len, unsigned char *sha1, char **log) +{ + char *last_branch = substitute_branch_name(&str, &len); + const char **p; + int logs_found = 0; + + *log = NULL; + for (p = ref_rev_parse_rules; *p; p++) { + struct stat st; + unsigned char hash[20]; + char path[PATH_MAX]; + const char *ref, *it; + + mksnpath(path, sizeof(path), *p, len, str); + ref = resolve_ref(path, hash, 1, NULL); + if (!ref) + continue; + if (!stat(git_path("logs/%s", path), &st) && + S_ISREG(st.st_mode)) + it = path; + else if (strcmp(ref, path) && + !stat(git_path("logs/%s", ref), &st) && + S_ISREG(st.st_mode)) + it = ref; + else + continue; + if (!logs_found++) { + *log = xstrdup(it); + hashcpy(sha1, hash); + } + if (!warn_ambiguous_refs) + break; + } + free(last_branch); + return logs_found; +} + static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int flags, int *type_p) { char *ref_file; diff --git a/sha1_name.c b/sha1_name.c index 143fd97ede..d423635962 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -241,91 +241,6 @@ static int ambiguous_path(const char *path, int len) return slash; } -/* - * *string and *len will only be substituted, and *string returned (for - * later free()ing) if the string passed in is a magic short-hand form - * to name a branch. - */ -static char *substitute_branch_name(const char **string, int *len) -{ - struct strbuf buf = STRBUF_INIT; - int ret = interpret_branch_name(*string, &buf); - - if (ret == *len) { - size_t size; - *string = strbuf_detach(&buf, &size); - *len = size; - return (char *)*string; - } - - return NULL; -} - -int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) -{ - char *last_branch = substitute_branch_name(&str, &len); - const char **p, *r; - int refs_found = 0; - - *ref = NULL; - for (p = ref_rev_parse_rules; *p; p++) { - char fullref[PATH_MAX]; - unsigned char sha1_from_ref[20]; - unsigned char *this_result; - int flag; - - this_result = refs_found ? sha1_from_ref : sha1; - mksnpath(fullref, sizeof(fullref), *p, len, str); - r = resolve_ref(fullref, this_result, 1, &flag); - if (r) { - if (!refs_found++) - *ref = xstrdup(r); - if (!warn_ambiguous_refs) - break; - } else if ((flag & REF_ISSYMREF) && strcmp(fullref, "HEAD")) - warning("ignoring dangling symref %s.", fullref); - } - free(last_branch); - return refs_found; -} - -int dwim_log(const char *str, int len, unsigned char *sha1, char **log) -{ - char *last_branch = substitute_branch_name(&str, &len); - const char **p; - int logs_found = 0; - - *log = NULL; - for (p = ref_rev_parse_rules; *p; p++) { - struct stat st; - unsigned char hash[20]; - char path[PATH_MAX]; - const char *ref, *it; - - mksnpath(path, sizeof(path), *p, len, str); - ref = resolve_ref(path, hash, 1, NULL); - if (!ref) - continue; - if (!stat(git_path("logs/%s", path), &st) && - S_ISREG(st.st_mode)) - it = path; - else if (strcmp(ref, path) && - !stat(git_path("logs/%s", ref), &st) && - S_ISREG(st.st_mode)) - it = ref; - else - continue; - if (!logs_found++) { - *log = xstrdup(it); - hashcpy(sha1, hash); - } - if (!warn_ambiguous_refs) - break; - } - free(last_branch); - return logs_found; -} - static inline int upstream_mark(const char *string, int len) { const char *suffix[] = { "@{upstream}", "@{u}" }; From 2f6af3820eb61f30cf102b345d35028047300b74 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 12 Oct 2011 10:39:01 -0700 Subject: [PATCH 2/2] Restrict ref-like names immediately below $GIT_DIR We have always dwimmed the user input $string into a ref by first looking directly inside $GIT_DIR, and then in $GIT_DIR/refs, $GIT_DIR/refs/tags, etc., and that is what made git log HEAD..MERGE_HEAD work correctly. This however means that git rev-parse config git log index would look at $GIT_DIR/config and $GIT_DIR/index and see if they are valid refs. To reduce confusion, let's not dwim a path immediately below $GIT_DIR that is not all-caps. Helped-by: Jeff King Signed-off-by: Junio C Hamano --- refs.c | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/refs.c b/refs.c index e3692bd3d8..2eb9c04443 100644 --- a/refs.c +++ b/refs.c @@ -994,12 +994,33 @@ const char *ref_fetch_rules[] = { NULL }; +static int refname_ok_at_root_level(const char *str, int len) +{ + if (len >= 5 && !memcmp(str, "refs/", 5)) + return 1; + + while (len--) { + char ch = *str++; + + /* + * Only accept likes of .git/HEAD, .git/MERGE_HEAD at + * the root level as a ref. + */ + if (ch != '_' && (ch < 'A' || 'Z' < ch)) + return 0; + } + return 1; +} + int refname_match(const char *abbrev_name, const char *full_name, const char **rules) { const char **p; const int abbrev_name_len = strlen(abbrev_name); for (p = rules; *p; p++) { + if (p == rules && + !refname_ok_at_root_level(abbrev_name, abbrev_name_len)) + continue; if (!strcmp(full_name, mkpath(*p, abbrev_name_len, abbrev_name))) { return 1; } @@ -1100,6 +1121,8 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref) unsigned char *this_result; int flag; + if (p == ref_rev_parse_rules && !refname_ok_at_root_level(str, len)) + continue; this_result = refs_found ? sha1_from_ref : sha1; mksnpath(fullref, sizeof(fullref), *p, len, str); r = resolve_ref(fullref, this_result, 1, &flag); @@ -1128,6 +1151,8 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log) char path[PATH_MAX]; const char *ref, *it; + if (p == ref_rev_parse_rules && !refname_ok_at_root_level(str, len)) + continue; mksnpath(path, sizeof(path), *p, len, str); ref = resolve_ref(path, hash, 1, NULL); if (!ref) @@ -2045,12 +2070,14 @@ char *shorten_unambiguous_ref(const char *ref, int strict) /* buffer for scanf result, at most ref must fit */ short_name = xstrdup(ref); - /* skip first rule, it will always match */ - for (i = nr_rules - 1; i > 0 ; --i) { + for (i = nr_rules - 1; i >= 0; i--) { int j; int rules_to_fail = i; int short_name_len; + if (!i && !refname_ok_at_root_level(ref, strlen(ref))) + continue; + if (1 != sscanf(ref, scanf_fmts[i], short_name)) continue; @@ -2076,6 +2103,10 @@ char *shorten_unambiguous_ref(const char *ref, int strict) if (i == j) continue; + if (!j && + !refname_ok_at_root_level(short_name, short_name_len)) + continue; + /* * the short name is ambiguous, if it resolves * (with this previous rule) to a valid ref