Skip to content

Commit 07f2947

Browse files
committed
Merge branch 'ms/refs-exists'
"git refs exists" that works like "git show-ref --exists" has been added. * ms/refs-exists: t: add test for git refs exists subcommand t1422: refactor tests to be shareable t1403: split 'show-ref --exists' tests into a separate file builtin/refs: add 'exists' subcommand
2 parents c31a276 + ef94b3e commit 07f2947

File tree

7 files changed

+154
-66
lines changed

7 files changed

+154
-66
lines changed

Documentation/git-refs.adoc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ git refs list [--count=<count>] [--shell|--perl|--python|--tcl]
1818
[--contains[=<object>]] [--no-contains[=<object>]]
1919
[(--exclude=<pattern>)...] [--start-after=<marker>]
2020
[ --stdin | (<pattern>...)]
21+
git refs exists <ref>
2122

2223
DESCRIPTION
2324
-----------
@@ -38,6 +39,12 @@ list::
3839
formatting, and sorting. This subcommand is an alias for
3940
linkgit:git-for-each-ref[1] and offers identical functionality.
4041

42+
exists::
43+
Check whether the given reference exists. Returns an exit code of 0 if
44+
it does, 2 if it is missing, and 1 in case looking up the reference
45+
failed with an error other than the reference being missing. This does
46+
not verify whether the reference resolves to an actual object.
47+
4148
OPTIONS
4249
-------
4350

builtin/refs.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@
77
#include "strbuf.h"
88
#include "worktree.h"
99
#include "for-each-ref.h"
10+
#include "refs/refs-internal.h"
1011

1112
#define REFS_MIGRATE_USAGE \
1213
N_("git refs migrate --ref-format=<format> [--no-reflog] [--dry-run]")
1314

1415
#define REFS_VERIFY_USAGE \
1516
N_("git refs verify [--strict] [--verbose]")
1617

18+
#define REFS_EXISTS_USAGE \
19+
N_("git refs exists <ref>")
20+
1721
static int cmd_refs_migrate(int argc, const char **argv, const char *prefix,
1822
struct repository *repo UNUSED)
1923
{
@@ -113,6 +117,48 @@ static int cmd_refs_list(int argc, const char **argv, const char *prefix,
113117
return for_each_ref_core(argc, argv, prefix, repo, refs_list_usage);
114118
}
115119

120+
static int cmd_refs_exists(int argc, const char **argv, const char *prefix,
121+
struct repository *repo UNUSED)
122+
{
123+
struct strbuf unused_referent = STRBUF_INIT;
124+
struct object_id unused_oid;
125+
unsigned int unused_type;
126+
int failure_errno = 0;
127+
const char *ref;
128+
int ret = 0;
129+
const char * const exists_usage[] = {
130+
REFS_EXISTS_USAGE,
131+
NULL,
132+
};
133+
struct option options[] = {
134+
OPT_END(),
135+
};
136+
137+
argc = parse_options(argc, argv, prefix, options, exists_usage, 0);
138+
if (argc != 1)
139+
die(_("'git refs exists' requires a reference"));
140+
141+
ref = *argv++;
142+
if (refs_read_raw_ref(get_main_ref_store(the_repository), ref,
143+
&unused_oid, &unused_referent, &unused_type,
144+
&failure_errno)) {
145+
if (failure_errno == ENOENT || failure_errno == EISDIR) {
146+
error(_("reference does not exist"));
147+
ret = 2;
148+
} else {
149+
errno = failure_errno;
150+
error_errno(_("failed to look up reference"));
151+
ret = 1;
152+
}
153+
154+
goto out;
155+
}
156+
157+
out:
158+
strbuf_release(&unused_referent);
159+
return ret;
160+
}
161+
116162
int cmd_refs(int argc,
117163
const char **argv,
118164
const char *prefix,
@@ -122,13 +168,15 @@ int cmd_refs(int argc,
122168
REFS_MIGRATE_USAGE,
123169
REFS_VERIFY_USAGE,
124170
"git refs list " COMMON_USAGE_FOR_EACH_REF,
171+
REFS_EXISTS_USAGE,
125172
NULL,
126173
};
127174
parse_opt_subcommand_fn *fn = NULL;
128175
struct option opts[] = {
129176
OPT_SUBCOMMAND("migrate", &fn, cmd_refs_migrate),
130177
OPT_SUBCOMMAND("verify", &fn, cmd_refs_verify),
131178
OPT_SUBCOMMAND("list", &fn, cmd_refs_list),
179+
OPT_SUBCOMMAND("exists", &fn, cmd_refs_exists),
132180
OPT_END(),
133181
};
134182

t/meson.build

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,13 @@ integration_tests = [
206206
't1419-exclude-refs.sh',
207207
't1420-lost-found.sh',
208208
't1421-reflog-write.sh',
209+
't1422-show-ref-exists.sh',
209210
't1430-bad-ref-name.sh',
210211
't1450-fsck.sh',
211212
't1451-fsck-buffer.sh',
212213
't1460-refs-migrate.sh',
213214
't1461-refs-list.sh',
215+
't1462-refs-exists.sh',
214216
't1500-rev-parse.sh',
215217
't1501-work-tree.sh',
216218
't1502-rev-parse-parseopt.sh',
@@ -1221,4 +1223,4 @@ if perl.found() and time.found()
12211223
timeout: 0,
12221224
)
12231225
endforeach
1224-
endif
1226+
endif

t/show-ref-exists-tests.sh

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
git_show_ref_exists=${git_show_ref_exists:-git show-ref --exists}
2+
3+
test_expect_success setup '
4+
test_commit --annotate A &&
5+
git checkout -b side &&
6+
test_commit --annotate B &&
7+
git checkout main &&
8+
test_commit C &&
9+
git branch B A^0
10+
'
11+
12+
test_expect_success '--exists with existing reference' '
13+
${git_show_ref_exists} refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
14+
'
15+
16+
test_expect_success '--exists with missing reference' '
17+
test_expect_code 2 ${git_show_ref_exists} refs/heads/does-not-exist
18+
'
19+
20+
test_expect_success '--exists does not use DWIM' '
21+
test_expect_code 2 ${git_show_ref_exists} $GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 2>err &&
22+
grep "reference does not exist" err
23+
'
24+
25+
test_expect_success '--exists with HEAD' '
26+
${git_show_ref_exists} HEAD
27+
'
28+
29+
test_expect_success '--exists with bad reference name' '
30+
test_when_finished "git update-ref -d refs/heads/bad...name" &&
31+
new_oid=$(git rev-parse HEAD) &&
32+
test-tool ref-store main update-ref msg refs/heads/bad...name $new_oid $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
33+
${git_show_ref_exists} refs/heads/bad...name
34+
'
35+
36+
test_expect_success '--exists with arbitrary symref' '
37+
test_when_finished "git symbolic-ref -d refs/symref" &&
38+
git symbolic-ref refs/symref refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
39+
${git_show_ref_exists} refs/symref
40+
'
41+
42+
test_expect_success '--exists with dangling symref' '
43+
test_when_finished "git symbolic-ref -d refs/heads/dangling" &&
44+
git symbolic-ref refs/heads/dangling refs/heads/does-not-exist &&
45+
${git_show_ref_exists} refs/heads/dangling
46+
'
47+
48+
test_expect_success '--exists with nonexistent object ID' '
49+
test-tool ref-store main update-ref msg refs/heads/missing-oid $(test_oid 001) $ZERO_OID REF_SKIP_OID_VERIFICATION &&
50+
${git_show_ref_exists} refs/heads/missing-oid
51+
'
52+
53+
test_expect_success '--exists with non-commit object' '
54+
tree_oid=$(git rev-parse HEAD^{tree}) &&
55+
test-tool ref-store main update-ref msg refs/heads/tree ${tree_oid} $ZERO_OID REF_SKIP_OID_VERIFICATION &&
56+
${git_show_ref_exists} refs/heads/tree
57+
'
58+
59+
test_expect_success '--exists with directory fails with generic error' '
60+
cat >expect <<-EOF &&
61+
error: reference does not exist
62+
EOF
63+
test_expect_code 2 ${git_show_ref_exists} refs/heads 2>err &&
64+
test_cmp expect err
65+
'
66+
67+
test_expect_success '--exists with non-existent special ref' '
68+
test_expect_code 2 ${git_show_ref_exists} FETCH_HEAD
69+
'
70+
71+
test_expect_success '--exists with existing special ref' '
72+
test_when_finished "rm .git/FETCH_HEAD" &&
73+
git rev-parse HEAD >.git/FETCH_HEAD &&
74+
${git_show_ref_exists} FETCH_HEAD
75+
'
76+
77+
test_done

t/t1403-show-ref.sh

Lines changed: 0 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -228,69 +228,4 @@ test_expect_success 'show-ref sub-modes are mutually exclusive' '
228228
grep "cannot be used together" err
229229
'
230230

231-
test_expect_success '--exists with existing reference' '
232-
git show-ref --exists refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
233-
'
234-
235-
test_expect_success '--exists with missing reference' '
236-
test_expect_code 2 git show-ref --exists refs/heads/does-not-exist
237-
'
238-
239-
test_expect_success '--exists does not use DWIM' '
240-
test_expect_code 2 git show-ref --exists $GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 2>err &&
241-
grep "reference does not exist" err
242-
'
243-
244-
test_expect_success '--exists with HEAD' '
245-
git show-ref --exists HEAD
246-
'
247-
248-
test_expect_success '--exists with bad reference name' '
249-
test_when_finished "git update-ref -d refs/heads/bad...name" &&
250-
new_oid=$(git rev-parse HEAD) &&
251-
test-tool ref-store main update-ref msg refs/heads/bad...name $new_oid $ZERO_OID REF_SKIP_REFNAME_VERIFICATION &&
252-
git show-ref --exists refs/heads/bad...name
253-
'
254-
255-
test_expect_success '--exists with arbitrary symref' '
256-
test_when_finished "git symbolic-ref -d refs/symref" &&
257-
git symbolic-ref refs/symref refs/heads/$GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME &&
258-
git show-ref --exists refs/symref
259-
'
260-
261-
test_expect_success '--exists with dangling symref' '
262-
test_when_finished "git symbolic-ref -d refs/heads/dangling" &&
263-
git symbolic-ref refs/heads/dangling refs/heads/does-not-exist &&
264-
git show-ref --exists refs/heads/dangling
265-
'
266-
267-
test_expect_success '--exists with nonexistent object ID' '
268-
test-tool ref-store main update-ref msg refs/heads/missing-oid $(test_oid 001) $ZERO_OID REF_SKIP_OID_VERIFICATION &&
269-
git show-ref --exists refs/heads/missing-oid
270-
'
271-
272-
test_expect_success '--exists with non-commit object' '
273-
tree_oid=$(git rev-parse HEAD^{tree}) &&
274-
test-tool ref-store main update-ref msg refs/heads/tree ${tree_oid} $ZERO_OID REF_SKIP_OID_VERIFICATION &&
275-
git show-ref --exists refs/heads/tree
276-
'
277-
278-
test_expect_success '--exists with directory fails with generic error' '
279-
cat >expect <<-EOF &&
280-
error: reference does not exist
281-
EOF
282-
test_expect_code 2 git show-ref --exists refs/heads 2>err &&
283-
test_cmp expect err
284-
'
285-
286-
test_expect_success '--exists with non-existent special ref' '
287-
test_expect_code 2 git show-ref --exists FETCH_HEAD
288-
'
289-
290-
test_expect_success '--exists with existing special ref' '
291-
test_when_finished "rm .git/FETCH_HEAD" &&
292-
git rev-parse HEAD >.git/FETCH_HEAD &&
293-
git show-ref --exists FETCH_HEAD
294-
'
295-
296231
test_done

t/t1422-show-ref-exists.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#!/bin/sh
2+
3+
test_description='show-ref --exists'
4+
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
5+
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
6+
7+
. ./test-lib.sh
8+
9+
. "$TEST_DIRECTORY"/show-ref-exists-tests.sh

t/t1462-refs-exists.sh

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/bin/sh
2+
3+
test_description='refs exists'
4+
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
5+
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
6+
7+
. ./test-lib.sh
8+
9+
git_show_ref_exists='git refs exists'
10+
. "$TEST_DIRECTORY"/show-ref-exists-tests.sh

0 commit comments

Comments
 (0)