Merge branch 'nd/extended-sha1-relpath'
* nd/extended-sha1-relpath: get_sha1: teach ":$n:<path>" the same relative path logic get_sha1: support relative path ":path" syntax Make prefix_path() return char* without const Conflicts: sha1_name.c
This commit is contained in:
commit
620b89cd98
@ -121,6 +121,10 @@ the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
|
|||||||
':path' (with an empty part before the colon, e.g. `:README`)
|
':path' (with an empty part before the colon, e.g. `:README`)
|
||||||
is a special case of the syntax described next: content
|
is a special case of the syntax described next: content
|
||||||
recorded in the index at the given path.
|
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
|
* 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
|
colon, followed by a path (e.g. `:0:README`); this names a blob object in the
|
||||||
|
2
cache.h
2
cache.h
@ -428,7 +428,7 @@ extern const char **get_pathspec(const char *prefix, const char **pathspec);
|
|||||||
extern void setup_work_tree(void);
|
extern void setup_work_tree(void);
|
||||||
extern const char *setup_git_directory_gently(int *);
|
extern const char *setup_git_directory_gently(int *);
|
||||||
extern const char *setup_git_directory(void);
|
extern const char *setup_git_directory(void);
|
||||||
extern const char *prefix_path(const char *prefix, int len, const char *path);
|
extern char *prefix_path(const char *prefix, int len, const char *path);
|
||||||
extern const char *prefix_filename(const char *prefix, int len, const char *path);
|
extern const char *prefix_filename(const char *prefix, int len, const char *path);
|
||||||
extern int check_filename(const char *prefix, const char *name);
|
extern int check_filename(const char *prefix, const char *name);
|
||||||
extern void verify_filename(const char *prefix, const char *name);
|
extern void verify_filename(const char *prefix, const char *name);
|
||||||
|
2
setup.c
2
setup.c
@ -4,7 +4,7 @@
|
|||||||
static int inside_git_dir = -1;
|
static int inside_git_dir = -1;
|
||||||
static int inside_work_tree = -1;
|
static int inside_work_tree = -1;
|
||||||
|
|
||||||
const char *prefix_path(const char *prefix, int len, const char *path)
|
char *prefix_path(const char *prefix, int len, const char *path)
|
||||||
{
|
{
|
||||||
const char *orig = path;
|
const char *orig = path;
|
||||||
char *sanitized = xmalloc(len + strlen(path) + 1);
|
char *sanitized = xmalloc(len + strlen(path) + 1);
|
||||||
|
37
sha1_name.c
37
sha1_name.c
@ -1066,6 +1066,23 @@ int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode,
|
|||||||
return ret;
|
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,
|
int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
||||||
struct object_context *oc,
|
struct object_context *oc,
|
||||||
int gently, const char *prefix)
|
int gently, const char *prefix)
|
||||||
@ -1080,13 +1097,15 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
|||||||
if (!ret)
|
if (!ret)
|
||||||
return ret;
|
return ret;
|
||||||
/* sha1:path --> object name of path in ent sha1
|
/* 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
|
* :[0-3]:path -> object name of path in index at stage
|
||||||
* :/foo -> recent commit matching foo
|
* :/foo -> recent commit matching foo
|
||||||
*/
|
*/
|
||||||
if (name[0] == ':') {
|
if (name[0] == ':') {
|
||||||
int stage = 0;
|
int stage = 0;
|
||||||
struct cache_entry *ce;
|
struct cache_entry *ce;
|
||||||
|
char *new_path = NULL;
|
||||||
int pos;
|
int pos;
|
||||||
if (namelen > 2 && name[1] == '/')
|
if (namelen > 2 && name[1] == '/')
|
||||||
/* don't need mode for commit */
|
/* don't need mode for commit */
|
||||||
@ -1099,7 +1118,13 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
|||||||
stage = name[1] - '0';
|
stage = name[1] - '0';
|
||||||
cp = name + 3;
|
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,
|
strncpy(oc->path, cp,
|
||||||
sizeof(oc->path));
|
sizeof(oc->path));
|
||||||
@ -1118,12 +1143,14 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
|||||||
if (ce_stage(ce) == stage) {
|
if (ce_stage(ce) == stage) {
|
||||||
hashcpy(sha1, ce->sha1);
|
hashcpy(sha1, ce->sha1);
|
||||||
oc->mode = ce->ce_mode;
|
oc->mode = ce->ce_mode;
|
||||||
|
free(new_path);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
pos++;
|
pos++;
|
||||||
}
|
}
|
||||||
if (!gently)
|
if (!gently)
|
||||||
diagnose_invalid_index_path(stage, prefix, cp);
|
diagnose_invalid_index_path(stage, prefix, cp);
|
||||||
|
free(new_path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
for (cp = name, bracket_depth = 0; *cp; cp++) {
|
for (cp = name, bracket_depth = 0; *cp; cp++) {
|
||||||
@ -1144,6 +1171,11 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
|||||||
}
|
}
|
||||||
if (!get_sha1_1(name, cp-name, tree_sha1)) {
|
if (!get_sha1_1(name, cp-name, tree_sha1)) {
|
||||||
const char *filename = cp+1;
|
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);
|
ret = get_tree_entry(tree_sha1, filename, sha1, &oc->mode);
|
||||||
if (!gently) {
|
if (!gently) {
|
||||||
diagnose_invalid_sha1_path(prefix, filename,
|
diagnose_invalid_sha1_path(prefix, filename,
|
||||||
@ -1155,6 +1187,7 @@ int get_sha1_with_context_1(const char *name, unsigned char *sha1,
|
|||||||
sizeof(oc->path));
|
sizeof(oc->path));
|
||||||
oc->path[sizeof(oc->path)-1] = '\0';
|
oc->path[sizeof(oc->path)-1] = '\0';
|
||||||
|
|
||||||
|
free(new_filename);
|
||||||
return ret;
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
if (!gently)
|
if (!gently)
|
||||||
|
@ -31,6 +31,67 @@ test_expect_success 'correct file objects' '
|
|||||||
test $HASH_file = $(git rev-parse :0:file.txt) )
|
test $HASH_file = $(git rev-parse :0:file.txt) )
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'correct relative file objects (0)' '
|
||||||
|
git rev-parse :file.txt >expected &&
|
||||||
|
git rev-parse :./file.txt >result &&
|
||||||
|
test_cmp expected result &&
|
||||||
|
git rev-parse :0:./file.txt >result &&
|
||||||
|
test_cmp expected result
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'correct relative file objects (1)' '
|
||||||
|
git rev-parse HEAD:file.txt >expected &&
|
||||||
|
git rev-parse HEAD:./file.txt >result &&
|
||||||
|
test_cmp expected result
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'correct relative file objects (2)' '
|
||||||
|
(
|
||||||
|
cd subdir &&
|
||||||
|
git rev-parse HEAD:../file.txt >result &&
|
||||||
|
test_cmp ../expected result
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'correct relative file objects (3)' '
|
||||||
|
(
|
||||||
|
cd subdir &&
|
||||||
|
git rev-parse HEAD:../subdir/../file.txt >result &&
|
||||||
|
test_cmp ../expected result
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'correct relative file objects (4)' '
|
||||||
|
git rev-parse HEAD:subdir/file.txt >expected &&
|
||||||
|
(
|
||||||
|
cd subdir &&
|
||||||
|
git rev-parse HEAD:./file.txt >result &&
|
||||||
|
test_cmp ../expected result
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'correct relative file objects (5)' '
|
||||||
|
git rev-parse :subdir/file.txt >expected &&
|
||||||
|
(
|
||||||
|
cd subdir &&
|
||||||
|
git rev-parse :./file.txt >result &&
|
||||||
|
test_cmp ../expected result &&
|
||||||
|
git rev-parse :0:./file.txt >result &&
|
||||||
|
test_cmp ../expected result
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'correct relative file objects (6)' '
|
||||||
|
git rev-parse :file.txt >expected &&
|
||||||
|
(
|
||||||
|
cd subdir &&
|
||||||
|
git rev-parse :../file.txt >result &&
|
||||||
|
test_cmp ../expected result &&
|
||||||
|
git rev-parse :0:../file.txt >result &&
|
||||||
|
test_cmp ../expected result
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'incorrect revision id' '
|
test_expect_success 'incorrect revision id' '
|
||||||
test_must_fail git rev-parse foobar:file.txt 2>error &&
|
test_must_fail git rev-parse foobar:file.txt 2>error &&
|
||||||
grep "Invalid object name '"'"'foobar'"'"'." error &&
|
grep "Invalid object name '"'"'foobar'"'"'." error &&
|
||||||
@ -75,4 +136,29 @@ test_expect_success 'invalid @{n} reference' '
|
|||||||
grep "fatal: Log for [^ ]* only has [0-9][0-9]* entries." error
|
grep "fatal: Log for [^ ]* only has [0-9][0-9]* entries." error
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'relative path not found' '
|
||||||
|
(
|
||||||
|
cd subdir &&
|
||||||
|
test_must_fail git rev-parse HEAD:./nonexistent.txt 2>error &&
|
||||||
|
grep subdir/nonexistent.txt error
|
||||||
|
)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'relative path outside worktree' '
|
||||||
|
test_must_fail git rev-parse HEAD:../file.txt >output 2>error &&
|
||||||
|
test -z "$(cat output)" &&
|
||||||
|
grep "outside repository" error
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'relative path when cwd is outside worktree' '
|
||||||
|
test_must_fail git --git-dir=.git --work-tree=subdir rev-parse HEAD:./file.txt >output 2>error &&
|
||||||
|
test -z "$(cat output)" &&
|
||||||
|
grep "relative path syntax can.t be used outside working tree." error
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'relative path when startup_info is NULL' '
|
||||||
|
test_must_fail test-match-trees HEAD:./file.txt HEAD:./file.txt 2>error &&
|
||||||
|
grep "BUG: startup_info struct is not initialized." error
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user