Skip to content

Commit 12b9d32

Browse files
johnkeepinggitster
authored andcommitted
rev-parse: add --prefix option
This makes 'git rev-parse' behave as if it were invoked from the specified subdirectory of a repository, with the difference that any file paths which it prints are prefixed with the full path from the top of the working tree. This is useful for shell scripts where we may want to cd to the top of the working tree but need to handle relative paths given by the user on the command line. Signed-off-by: John Keeping <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1ae2e19 commit 12b9d32

File tree

3 files changed

+131
-5
lines changed

3 files changed

+131
-5
lines changed

Documentation/git-rev-parse.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,22 @@ OPTIONS
5959
If there is no parameter given by the user, use `<arg>`
6060
instead.
6161

62+
--prefix <arg>::
63+
Behave as if 'git rev-parse' was invoked from the `<arg>`
64+
subdirectory of the working tree. Any relative filenames are
65+
resolved as if they are prefixed by `<arg>` and will be printed
66+
in that form.
67+
+
68+
This can be used to convert arguments to a command run in a subdirectory
69+
so that they can still be used after moving to the top-level of the
70+
repository. For example:
71+
+
72+
----
73+
prefix=$(git rev-parse --show-prefix)
74+
cd "$(git rev-parse --show-toplevel)"
75+
eval "set -- $(git rev-parse --sq --prefix "$prefix" "$@")"
76+
----
77+
6278
--verify::
6379
Verify that exactly one parameter is provided, and that it
6480
can be turned into a raw 20-byte SHA-1 that can be used to

builtin/rev-parse.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,17 @@ static void show_datestring(const char *flag, const char *datestr)
212212
show(buffer);
213213
}
214214

215-
static int show_file(const char *arg)
215+
static int show_file(const char *arg, int output_prefix)
216216
{
217217
show_default();
218218
if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
219-
show(arg);
219+
if (output_prefix) {
220+
const char *prefix = startup_info->prefix;
221+
show(prefix_filename(prefix,
222+
prefix ? strlen(prefix) : 0,
223+
arg));
224+
} else
225+
show(arg);
220226
return 1;
221227
}
222228
return 0;
@@ -470,6 +476,7 @@ N_("git rev-parse --parseopt [options] -- [<args>...]\n"
470476
int cmd_rev_parse(int argc, const char **argv, const char *prefix)
471477
{
472478
int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
479+
int output_prefix = 0;
473480
unsigned char sha1[20];
474481
const char *name = NULL;
475482

@@ -503,7 +510,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
503510
const char *arg = argv[i];
504511

505512
if (as_is) {
506-
if (show_file(arg) && as_is < 2)
513+
if (show_file(arg, output_prefix) && as_is < 2)
507514
verify_filename(prefix, arg, 0);
508515
continue;
509516
}
@@ -527,14 +534,21 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
527534
as_is = 2;
528535
/* Pass on the "--" if we show anything but files.. */
529536
if (filter & (DO_FLAGS | DO_REVS))
530-
show_file(arg);
537+
show_file(arg, 0);
531538
continue;
532539
}
533540
if (!strcmp(arg, "--default")) {
534541
def = argv[i+1];
535542
i++;
536543
continue;
537544
}
545+
if (!strcmp(arg, "--prefix")) {
546+
prefix = argv[i+1];
547+
startup_info->prefix = prefix;
548+
output_prefix = 1;
549+
i++;
550+
continue;
551+
}
538552
if (!strcmp(arg, "--revs-only")) {
539553
filter &= ~DO_NOREV;
540554
continue;
@@ -754,7 +768,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
754768
if (verify)
755769
die_no_single_rev(quiet);
756770
as_is = 1;
757-
if (!show_file(arg))
771+
if (!show_file(arg, output_prefix))
758772
continue;
759773
verify_filename(prefix, arg, 1);
760774
}

t/t1513-rev-parse-prefix.sh

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
#!/bin/sh
2+
3+
test_description='Tests for rev-parse --prefix'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success 'setup' '
8+
mkdir -p sub1/sub2 &&
9+
echo top >top &&
10+
echo file1 >sub1/file1 &&
11+
echo file2 >sub1/sub2/file2 &&
12+
git add top sub1/file1 sub1/sub2/file2 &&
13+
git commit -m commit
14+
'
15+
16+
test_expect_success 'empty prefix -- file' '
17+
git rev-parse --prefix "" -- top sub1/file1 >actual &&
18+
cat <<-\EOF >expected &&
19+
--
20+
top
21+
sub1/file1
22+
EOF
23+
test_cmp expected actual
24+
'
25+
26+
test_expect_success 'valid prefix -- file' '
27+
git rev-parse --prefix sub1/ -- file1 sub2/file2 >actual &&
28+
cat <<-\EOF >expected &&
29+
--
30+
sub1/file1
31+
sub1/sub2/file2
32+
EOF
33+
test_cmp expected actual
34+
'
35+
36+
test_expect_success 'valid prefix -- ../file' '
37+
git rev-parse --prefix sub1/ -- ../top sub2/file2 >actual &&
38+
cat <<-\EOF >expected &&
39+
--
40+
sub1/../top
41+
sub1/sub2/file2
42+
EOF
43+
test_cmp expected actual
44+
'
45+
46+
test_expect_success 'empty prefix HEAD:./path' '
47+
git rev-parse --prefix "" HEAD:./top >actual &&
48+
git rev-parse HEAD:top >expected &&
49+
test_cmp expected actual
50+
'
51+
52+
test_expect_success 'valid prefix HEAD:./path' '
53+
git rev-parse --prefix sub1/ HEAD:./file1 >actual &&
54+
git rev-parse HEAD:sub1/file1 >expected &&
55+
test_cmp expected actual
56+
'
57+
58+
test_expect_success 'valid prefix HEAD:../path' '
59+
git rev-parse --prefix sub1/ HEAD:../top >actual &&
60+
git rev-parse HEAD:top >expected &&
61+
test_cmp expected actual
62+
'
63+
64+
test_expect_success 'prefix ignored with HEAD:top' '
65+
git rev-parse --prefix sub1/ HEAD:top >actual &&
66+
git rev-parse HEAD:top >expected &&
67+
test_cmp expected actual
68+
'
69+
70+
test_expect_success 'disambiguate path with valid prefix' '
71+
git rev-parse --prefix sub1/ file1 >actual &&
72+
cat <<-\EOF >expected &&
73+
sub1/file1
74+
EOF
75+
test_cmp expected actual
76+
'
77+
78+
test_expect_success 'file and refs with prefix' '
79+
git rev-parse --prefix sub1/ master file1 >actual &&
80+
cat <<-EOF >expected &&
81+
$(git rev-parse master)
82+
sub1/file1
83+
EOF
84+
test_cmp expected actual
85+
'
86+
87+
test_expect_success 'two-levels deep' '
88+
git rev-parse --prefix sub1/sub2/ -- file2 >actual &&
89+
cat <<-\EOF >expected &&
90+
--
91+
sub1/sub2/file2
92+
EOF
93+
test_cmp expected actual
94+
'
95+
96+
test_done

0 commit comments

Comments
 (0)