@matheusbsbs / matheus-bagliano / commits / 979f792951
Fork 0

get_sha1: support relative path ":path" syntax

Currently :path and ref:path can be used to refer to a specific object in index or ref respectively. "path" component is absolute path. This patch allows "path" to be written as "./path" or "../path", which is relative to user's original cwd. This does not work in commands for which startup_info is NULL (i.e. non-builtin ones, it seems none of them needs this anyway). Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Nov 28, 2010 at 10:37 UTC 979f792951913d75f992f87022b75610303a614f
3 files changed +100 -3
Documentation/revisions.txt
+4
@@ -121,6 +121,10 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
121 ':path' (with an empty part before the colon, e.g. `:README`)
122 is a special case of the syntax described next: content
123 recorded in the index at the given path.
124 + A path starting with './' or '../' is relative to current working directory.
125 + The given path will be converted to be relative to working tree's root directory.
126 + This is most useful to address a blob or tree from a commit or tree that has
127 + the same tree structure with the working tree.
128
129 * A colon, optionally followed by a stage number (0 to 3) and a
130 colon, followed by a path (e.g. `:0:README`); this names a blob object in the
sha1_name.c
+34 -3
@@ -1046,6 +1046,23 @@ int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,
1046 return ret;
1047 }
1048
1049 +static char *resolve_relative_path(const char *rel)
1050 +{
1051 + if (prefixcmp(rel, "./") && prefixcmp(rel, "../"))
1052 + return NULL;
1053 +
1054 + if (!startup_info)
1055 + die("BUG: startup_info struct is not initialized.");
1056 +
1057 + if (!is_inside_work_tree())
1058 + die("relative path syntax can't be used outside working tree.");
1059 +
1060 + /* die() inside prefix_path() if resolved path is outside worktree */
1061 + return prefix_path(startup_info->prefix,
1062 + startup_info->prefix ? strlen(startup_info->prefix) : 0,
1063 + rel);
1064 +}
1065 +
1066 int get_sha1_with_context_1(const char *name, unsigned char *sha1,
1067 struct object_context *oc,
1068 int gently, const char *prefix)
@@ -1060,25 +1077,31 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
1077 if (!ret)
1078 return ret;
1079 /* sha1:path --> object name of path in ent sha1
1063 - * :path -> object name of path in index
1080 + * :path -> object name of absolute path in index
1081 + * :./path -> object name of path relative to cwd in index
1082 * :[0-3]:path -> object name of path in index at stage
1083 * :/foo -> recent commit matching foo
1084 */
1085 if (name[0] == ':') {
1086 int stage = 0;
1087 struct cache_entry *ce;
1088 + char *new_path = NULL;
1089 int pos;
1090 if (namelen > 2 && name[1] == '/')
1091 return get_sha1_oneline(name + 2, sha1);
1092 if (namelen < 3 ||
1093 name[2] != ':' ||
1075 - name[1] < '0' || '3' < name[1])
1094 + name[1] < '0' || '3' < name[1]) {
1095 cp = name + 1;
1096 + new_path = resolve_relative_path(cp);
1097 + if (new_path)
1098 + cp = new_path;
1099 + }
1100 else {
1101 stage = name[1] - '0';
1102 cp = name + 3;
1103 }
1081 - namelen = namelen - (cp - name);
1104 + namelen = strlen(cp);
1105
1106 strncpy(oc->path, cp,
1107 sizeof(oc->path));
@@ -1096,12 +1119,14 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
1119 break;
1120 if (ce_stage(ce) == stage) {
1121 hashcpy(sha1, ce->sha1);
1122 + free(new_path);
1123 return 0;
1124 }
1125 pos++;
1126 }
1127 if (!gently)
1128 diagnose_invalid_index_path(stage, prefix, cp);
1129 + free(new_path);
1130 return -1;
1131 }
1132 for (cp = name, bracket_depth = 0; *cp; cp++) {
@@ -1122,6 +1147,11 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
1147 }
1148 if (!get_sha1_1(name, cp-name, tree_sha1)) {
1149 const char *filename = cp+1;
1150 + char *new_filename = NULL;
1151 +
1152 + new_filename = resolve_relative_path(filename);
1153 + if (new_filename)
1154 + filename = new_filename;
1155 ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
1156 if (!gently) {
1157 diagnose_invalid_sha1_path(prefix, filename,
@@ -1133,6 +1163,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
1163 sizeof(oc->path));
1164 oc->path[sizeof(oc->path)-1] = '\0';
1165
1166 + free(new_filename);
1167 return ret;
1168 } else {
1169 if (!gently)
t/t1506-rev-parse-diagnosis.sh
+62
@@ -31,6 +31,43 @@ test_expect_success 'correct file objects' '
31 test $HASH_file = $(git rev-parse :0:file.txt) )
32 '
33
34 +test_expect_success 'correct relative file objects (0)' '
35 + git rev-parse :file.txt >expected &&
36 + git rev-parse :./file.txt >result &&
37 + test_cmp expected result
38 +'
39 +
40 +test_expect_success 'correct relative file objects (1)' '
41 + git rev-parse HEAD:file.txt >expected &&
42 + git rev-parse HEAD:./file.txt >result &&
43 + test_cmp expected result
44 +'
45 +
46 +test_expect_success 'correct relative file objects (2)' '
47 + (
48 + cd subdir &&
49 + git rev-parse HEAD:../file.txt >result &&
50 + test_cmp ../expected result
51 + )
52 +'
53 +
54 +test_expect_success 'correct relative file objects (3)' '
55 + (
56 + cd subdir &&
57 + git rev-parse HEAD:../subdir/../file.txt >result &&
58 + test_cmp ../expected result
59 + )
60 +'
61 +
62 +test_expect_success 'correct relative file objects (4)' '
63 + git rev-parse HEAD:subdir/file.txt >expected &&
64 + (
65 + cd subdir &&
66 + git rev-parse HEAD:./file.txt >result &&
67 + test_cmp ../expected result
68 + )
69 +'
70 +
71 test_expect_success 'incorrect revision id' '
72 test_must_fail git rev-parse foobar:file.txt 2>error &&
73 grep "Invalid object name '"'"'foobar'"'"'." error &&
@@ -75,4 +112,29 @@ test_expect_success 'invalid @{n} reference' '
112 grep "fatal: Log for [^ ]* only has [0-9][0-9]* entries." error
113 '
114
115 +test_expect_success 'relative path not found' '
116 + (
117 + cd subdir &&
118 + test_must_fail git rev-parse HEAD:./nonexistent.txt 2>error &&
119 + grep subdir/nonexistent.txt error
120 + )
121 +'
122 +
123 +test_expect_success 'relative path outside worktree' '
124 + test_must_fail git rev-parse HEAD:../file.txt >output 2>error &&
125 + test -z "$(cat output)" &&
126 + grep "outside repository" error
127 +'
128 +
129 +test_expect_success 'relative path when cwd is outside worktree' '
130 + test_must_fail git --git-dir=.git --work-tree=subdir rev-parse HEAD:./file.txt >output 2>error &&
131 + test -z "$(cat output)" &&
132 + grep "relative path syntax can.t be used outside working tree." error
133 +'
134 +
135 +test_expect_success 'relative path when startup_info is NULL' '
136 + test_must_fail test-match-trees HEAD:./file.txt HEAD:./file.txt 2>error &&
137 + grep "BUG: startup_info struct is not initialized." error
138 +'
139 +
140 test_done