Most commands accept relative paths, but this is not true of arguments in ent:path format. This patch makes all of the following git-show commands work in the git source tree (not just the first): % cd xdiff % git-show v1.5.2-rc0:xdiff/xemit.h % git-show v1.5.2-rc0:./xemit.h % git-show v1.5.2-rc0:../sha1_name.c It also adds ent:?string as a synonym for ent:/string . This makes the following changes possible later: ent:/path is an absolute path and ent:path is relative. Signed-off-by: Dana L. How <danahow@xxxxxxxxx> --- cache.h | 1 + setup.c | 5 +++- sha1_name.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 79 insertions(+), 5 deletions(-) diff --git a/cache.h b/cache.h index 8e76152..53507d9 100644 --- a/cache.h +++ b/cache.h @@ -215,6 +215,7 @@ extern char *get_graft_file(void); #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" +extern const char *prefix_to_cwd; extern const char **get_pathspec(const char *prefix, const char **pathspec); extern const char *setup_git_directory_gently(int *); extern const char *setup_git_directory(void); diff --git a/setup.c b/setup.c index a45ea83..46ae6e3 100644 --- a/setup.c +++ b/setup.c @@ -1,5 +1,7 @@ #include "cache.h" +const char *prefix_to_cwd; + const char *prefix_path(const char *prefix, int len, const char *path) { const char *orig = path; @@ -252,7 +254,8 @@ const char *setup_git_directory_gently(int *nongit_ok) cwd[len++] = '/'; cwd[len] = 0; inside_git_dir = !prefixcmp(cwd + offset, ".git/"); - return cwd + offset; + prefix_to_cwd = cwd + offset; + return prefix_to_cwd; } int git_config_perm(const char *var, const char *value) diff --git a/sha1_name.c b/sha1_name.c index 55f25a2..dd415c1 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -593,6 +593,67 @@ static int handle_one_ref(const char *path, } /* + * Future: nonzero to assume relative paths + */ +#define ENT_COLON_ASSUME_RELATIVE 0 + +/* + * Modify path into a full absolute path with no leading "/" + * and no "." or "..". If we can't, + * leave input unaltered for client's error message. + */ +static void prepend_prefix(const char **cp, int *namelen) +{ + char *t, *t2; + const char *cp2 = *cp; + int type, namelen2 = *namelen; + static char fullpath[PATH_MAX]; + int fixup = strstr(cp2, "/./") || strstr(cp2, "/../"); + for (type = 4; --type > 0; ) + if (namelen2 > type && !memcmp(cp2, "../" + 3 - type, type)) + break; + + /* handle simple cases else create absolute path */ + fullpath[0] = 0; + if ((type == 0 && !ENT_COLON_ASSUME_RELATIVE) || type == 1) { + if (!fixup) goto done; + } else + if (prefix_to_cwd) { + namelen2 += strlen(prefix_to_cwd); + if (namelen2 >= PATH_MAX) + die("path too long"); + strcpy(fullpath, prefix_to_cwd); + type = 0; + } else + if (type == 2) { + if (!fixup) goto done; + } else + if (type == 3) + return; /* client will complain */ + cp2 = strcat(fullpath, cp2); + + while ((t = strstr(cp2, "/./")) != NULL) + memmove(t, t + 2, (namelen2 -= 2) - (t - cp2) + 1); + + while ((t = strstr(cp2, "/../")) != NULL) { + if (t == cp2 || (t == cp2 + 1 && t[-1] == '.')) + return; /* client will complain */ + for (t2 = t; --t2 >= cp2 && *t2 != '/'; ) + /* nothing */; + if (t2 < cp2) { + namelen2 -= t + 4 - cp2; + cp2 = t + 4; + } else { + memmove(t2, t + 3, namelen2 - (t + 3 - cp2) + 1); + namelen2 -= t + 3 - t2; + } + } + +done: *cp = cp2 + type; + *namelen = namelen2 - type; +} + +/* * This interprets names like ':/Initial revision of "git"' by searching * through history and returning the first commit whose message starts * with the given string. @@ -647,6 +708,11 @@ int get_sha1(const char *name, unsigned char *sha1) return get_sha1_with_mode(name, sha1, &unused); } +/* + * Future: change to '?' + */ +#define ENT_COLON_OLD_SEARCH '/' + int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode) { int ret, bracket_depth; @@ -666,7 +732,8 @@ int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode) int stage = 0; struct cache_entry *ce; int pos; - if (namelen > 2 && name[1] == '/') + if (namelen > 2 && + (name[1] == ENT_COLON_OLD_SEARCH || name[1] == '?')) return get_sha1_oneline(name + 2, sha1); if (namelen < 3 || name[2] != ':' || @@ -681,6 +748,7 @@ int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode) read_cache(); if (active_nr < 0) return -1; + prepend_prefix(&cp, &namelen); pos = cache_name_pos(cp, namelen); if (pos < 0) pos = -pos - 1; @@ -708,9 +776,11 @@ int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode) } if (*cp == ':') { unsigned char tree_sha1[20]; - if (!get_sha1_1(name, cp-name, tree_sha1)) - return get_tree_entry(tree_sha1, cp+1, sha1, - mode); + if (!get_sha1_1(name, cp - name, tree_sha1)) { + namelen -= ++cp - name; + prepend_prefix(&cp, &namelen); + return get_tree_entry(tree_sha1, cp, sha1, mode); + } } return ret; } -- 1.5.2.rc0.787.g0014 - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html