From: Junio C Hamano Date: Tue, 21 Dec 2010 22:30:19 +0000 (-0800) Subject: Merge branch 'nd/oneline-sha1-name-from-specific-ref' X-Git-Tag: v1.7.4-rc0~19 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/41cd7974b0b67b78c39bccc86718dbe19390f768?ds=inline;hp=-c Merge branch 'nd/oneline-sha1-name-from-specific-ref' * nd/oneline-sha1-name-from-specific-ref: get_sha1: handle special case $commit^{/} get_sha1: support $commit^{/regex} syntax get_sha1_oneline: make callers prepare the commit list to traverse get_sha1_oneline: fix lifespan rule of temp_commit_buffer variable --- 41cd7974b0b67b78c39bccc86718dbe19390f768 diff --combined Documentation/revisions.txt index 8b519d7448,174fa8e90d..9e92734bc1 --- a/Documentation/revisions.txt +++ b/Documentation/revisions.txt @@@ -106,6 -106,12 +106,12 @@@ the `$GIT_DIR/refs` directory or from t and dereference the tag recursively until a non-tag object is found. + * A suffix '{caret}' to a revision parameter followed by a brace + pair that contains a text led by a slash (e.g. `HEAD^{/fix nasty bug}`): + this is the same as `:/fix nasty bug` syntax below except that + it returns the youngest matching commit which is reachable from + the ref before '{caret}'. + * A colon, followed by a slash, followed by a text (e.g. `:/fix nasty bug`): this names a commit whose commit message matches the specified regular expression. This name returns the youngest matching commit which is @@@ -121,10 -127,6 +127,10 @@@ ':path' (with an empty part before the colon, e.g. `:README`) is a special case of the syntax described next: content recorded in the index at the given path. + A path starting with './' or '../' is relative to current working directory. + The given path will be converted to be relative to working tree's root directory. + This is most useful to address a blob or tree from a commit or tree that has + the same tree structure with the working tree. * A colon, optionally followed by a stage number (0 to 3) and a colon, followed by a path (e.g. `:0:README`); this names a blob object in the diff --combined sha1_name.c index 8f49279642,c5c59ced7f..ceb9cdd860 --- a/sha1_name.c +++ b/sha1_name.c @@@ -7,6 -7,8 +7,8 @@@ #include "refs.h" #include "remote.h" + static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *); + static int find_short_object_filename(int len, const char *name, unsigned char *sha1) { struct alternate_object_database *alt; @@@ -562,6 -564,8 +564,8 @@@ static int peel_onion(const char *name expected_type = OBJ_BLOB; else if (sp[0] == '}') expected_type = OBJ_NONE; + else if (sp[0] == '/') + expected_type = OBJ_COMMIT; else return -1; @@@ -576,19 -580,37 +580,37 @@@ if (!o || (!o->parsed && !parse_object(o->sha1))) return -1; hashcpy(sha1, o->sha1); + return 0; } - else { + + /* + * At this point, the syntax look correct, so + * if we do not get the needed object, we should + * barf. + */ + o = peel_to_type(name, len, o, expected_type); + if (!o) + return -1; + + hashcpy(sha1, o->sha1); + if (sp[0] == '/') { + /* "$commit^{/foo}" */ + char *prefix; + int ret; + struct commit_list *list = NULL; + /* - * At this point, the syntax look correct, so - * if we do not get the needed object, we should - * barf. + * $commit^{/}. Some regex implementation may reject. + * We don't need regex anyway. '' pattern always matches. */ - o = peel_to_type(name, len, o, expected_type); - if (o) { - hashcpy(sha1, o->sha1); + if (sp[1] == '}') return 0; - } - return -1; + + prefix = xstrndup(sp + 1, name + len - 1 - (sp + 1)); + commit_list_insert((struct commit *)o, &list); + ret = get_sha1_oneline(prefix, sha1, list); + free(prefix); + return ret; } return 0; } @@@ -686,15 -708,14 +708,14 @@@ static int handle_one_ref(const char *p if (object->type != OBJ_COMMIT) return 0; insert_by_date((struct commit *)object, list); - object->flags |= ONELINE_SEEN; return 0; } - static int get_sha1_oneline(const char *prefix, unsigned char *sha1) + static int get_sha1_oneline(const char *prefix, unsigned char *sha1, + struct commit_list *list) { - struct commit_list *list = NULL, *backup = NULL, *l; - int retval = -1; - char *temp_commit_buffer = NULL; + struct commit_list *backup = NULL, *l; + int found = 0; regex_t regex; if (prefix[0] == '!') { @@@ -706,41 -727,45 +727,45 @@@ if (regcomp(®ex, prefix, REG_EXTENDED)) die("Invalid search pattern: %s", prefix); - for_each_ref(handle_one_ref, &list); - for (l = list; l; l = l->next) + for (l = list; l; l = l->next) { + l->item->object.flags |= ONELINE_SEEN; commit_list_insert(l->item, &backup); + } while (list) { - char *p; + char *p, *to_free = NULL; struct commit *commit; enum object_type type; unsigned long size; + int matches; commit = pop_most_recent_commit(&list, ONELINE_SEEN); if (!parse_object(commit->object.sha1)) continue; - free(temp_commit_buffer); if (commit->buffer) p = commit->buffer; else { p = read_sha1_file(commit->object.sha1, &type, &size); if (!p) continue; - temp_commit_buffer = p; + to_free = p; } - if (!(p = strstr(p, "\n\n"))) - continue; - if (!regexec(®ex, p + 2, 0, NULL, 0)) { + + p = strstr(p, "\n\n"); + matches = p && !regexec(®ex, p + 2, 0, NULL, 0); + free(to_free); + + if (matches) { hashcpy(sha1, commit->object.sha1); - retval = 0; + found = 1; break; } } regfree(®ex); - free(temp_commit_buffer); free_commit_list(list); for (l = backup; l; l = l->next) clear_commit_marks(l->item, ONELINE_SEEN); - return retval; + free_commit_list(backup); + return found ? 0 : -1; } struct grab_nth_branch_switch_cbdata { @@@ -1066,23 -1091,6 +1091,23 @@@ int get_sha1_with_mode_1(const char *na return ret; } +static char *resolve_relative_path(const char *rel) +{ + if (prefixcmp(rel, "./") && prefixcmp(rel, "../")) + return NULL; + + if (!startup_info) + die("BUG: startup_info struct is not initialized."); + + if (!is_inside_work_tree()) + die("relative path syntax can't be used outside working tree."); + + /* die() inside prefix_path() if resolved path is outside worktree */ + return prefix_path(startup_info->prefix, + startup_info->prefix ? strlen(startup_info->prefix) : 0, + rel); +} + int get_sha1_with_context_1(const char *name, unsigned char *sha1, struct object_context *oc, int gently, const char *prefix) @@@ -1097,19 -1105,19 +1122,21 @@@ if (!ret) return ret; /* sha1:path --> object name of path in ent sha1 - * :path -> object name of path in index + * :path -> object name of absolute path in index + * :./path -> object name of path relative to cwd in index * :[0-3]:path -> object name of path in index at stage * :/foo -> recent commit matching foo */ if (name[0] == ':') { int stage = 0; struct cache_entry *ce; + char *new_path = NULL; int pos; - if (namelen > 2 && name[1] == '/') - /* don't need mode for commit */ - return get_sha1_oneline(name + 2, sha1); + if (namelen > 2 && name[1] == '/') { + struct commit_list *list = NULL; + for_each_ref(handle_one_ref, &list); + return get_sha1_oneline(name + 2, sha1, list); + } if (namelen < 3 || name[2] != ':' || name[1] < '0' || '3' < name[1]) @@@ -1118,13 -1126,7 +1145,13 @@@ stage = name[1] - '0'; cp = name + 3; } - namelen = namelen - (cp - name); + new_path = resolve_relative_path(cp); + if (!new_path) { + namelen = namelen - (cp - name); + } else { + cp = new_path; + namelen = strlen(cp); + } strncpy(oc->path, cp, sizeof(oc->path)); @@@ -1143,14 -1145,12 +1170,14 @@@ if (ce_stage(ce) == stage) { hashcpy(sha1, ce->sha1); oc->mode = ce->ce_mode; + free(new_path); return 0; } pos++; } if (!gently) diagnose_invalid_index_path(stage, prefix, cp); + free(new_path); return -1; } for (cp = name, bracket_depth = 0; *cp; cp++) { @@@ -1171,11 -1171,6 +1198,11 @@@ } if (!get_sha1_1(name, cp-name, tree_sha1)) { const char *filename = cp+1; + char *new_filename = NULL; + + new_filename = resolve_relative_path(filename); + if (new_filename) + filename = new_filename; ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode); if (!gently) { diagnose_invalid_sha1_path(prefix, filename, @@@ -1187,7 -1182,6 +1214,7 @@@ sizeof(oc->path)); oc->path[sizeof(oc->path)-1] = '\0'; + free(new_filename); return ret; } else { if (!gently)