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 <john@keeping.me.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>
John Keeping committed
Jun 16, 2013 at 15:18 UTC
12b9d32790b40bf3ea49134095619700191abf1f
3 files changed
+131
-5
Documentation/git-rev-parse.txt
+16
@@ -59,6 +59,22 @@ OPTIONS
59
If there is no parameter given by the user, use `<arg>`
60
instead.
61
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
+
78
--verify::
79
Verify that exactly one parameter is provided, and that it
80
can be turned into a raw 20-byte SHA-1 that can be used to
builtin/rev-parse.c
+19
-5
@@ -212,11 +212,17 @@ static void show_datestring(const char *flag, const char *datestr)
212
show(buffer);
213
}
214
215
-static int show_file(const char *arg)
215
+static int show_file(const char *arg, int output_prefix)
216
{
217
show_default();
218
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);
226
return 1;
227
}
228
return 0;
@@ -470,6 +476,7 @@ N_("git rev-parse --parseopt [options] -- [<args>...]\n"
476
int cmd_rev_parse(int argc, const char **argv, const char *prefix)
477
{
478
int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
479
+ int output_prefix = 0;
480
unsigned char sha1[20];
481
const char *name = NULL;
482
@@ -503,7 +510,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
510
const char *arg = argv[i];
511
512
if (as_is) {
506
- if (show_file(arg) && as_is < 2)
513
+ if (show_file(arg, output_prefix) && as_is < 2)
514
verify_filename(prefix, arg, 0);
515
continue;
516
}
@@ -527,7 +534,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
534
as_is = 2;
535
/* Pass on the "--" if we show anything but files.. */
536
if (filter & (DO_FLAGS | DO_REVS))
530
- show_file(arg);
537
+ show_file(arg, 0);
538
continue;
539
}
540
if (!strcmp(arg, "--default")) {
@@ -535,6 +542,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
542
i++;
543
continue;
544
}
545
+ if (!strcmp(arg, "--prefix")) {
546
+ prefix = argv[i+1];
547
+ startup_info->prefix = prefix;
548
+ output_prefix = 1;
549
+ i++;
550
+ continue;
551
+ }
552
if (!strcmp(arg, "--revs-only")) {
553
filter &= ~DO_NOREV;
554
continue;
@@ -754,7 +768,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
768
if (verify)
769
die_no_single_rev(quiet);
770
as_is = 1;
757
- if (!show_file(arg))
771
+ if (!show_file(arg, output_prefix))
772
continue;
773
verify_filename(prefix, arg, 1);
774
}
t/t1513-rev-parse-prefix.sh
new
+96
@@ -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