1 #include "cache.h"
2 #include "repository.h"
3 #include "refs.h"
4 #include "remote.h"
5 #include "argv-array.h"
6 #include "ls-refs.h"
7 #include "pkt-line.h"
8 #include "config.h"
9 
10 /*
11  * Check if one of the prefixes is a prefix of the ref.
12  * If no prefixes were provided, all refs match.
13  */
ref_match(const struct argv_array * prefixes,const char * refname)14 static int ref_match(const struct argv_array *prefixes, const char *refname)
15 {
16 	int i;
17 
18 	if (!prefixes->argc)
19 		return 1; /* no restriction */
20 
21 	for (i = 0; i < prefixes->argc; i++) {
22 		const char *prefix = prefixes->argv[i];
23 
24 		if (starts_with(refname, prefix))
25 			return 1;
26 	}
27 
28 	return 0;
29 }
30 
31 struct ls_refs_data {
32 	unsigned peel;
33 	unsigned symrefs;
34 	struct argv_array prefixes;
35 };
36 
send_ref(const char * refname,const struct object_id * oid,int flag,void * cb_data)37 static int send_ref(const char *refname, const struct object_id *oid,
38 		    int flag, void *cb_data)
39 {
40 	struct ls_refs_data *data = cb_data;
41 	const char *refname_nons = strip_namespace(refname);
42 	struct strbuf refline = STRBUF_INIT;
43 
44 	if (ref_is_hidden(refname_nons, refname))
45 		return 0;
46 
47 	if (!ref_match(&data->prefixes, refname_nons))
48 		return 0;
49 
50 	strbuf_addf(&refline, "%s %s", oid_to_hex(oid), refname_nons);
51 	if (data->symrefs && flag & REF_ISSYMREF) {
52 		struct object_id unused;
53 		const char *symref_target = resolve_ref_unsafe(refname, 0,
54 							       &unused,
55 							       &flag);
56 
57 		if (!symref_target)
58 			die("'%s' is a symref but it is not?", refname);
59 
60 		strbuf_addf(&refline, " symref-target:%s",
61 			    strip_namespace(symref_target));
62 	}
63 
64 	if (data->peel) {
65 		struct object_id peeled;
66 		if (!peel_ref(refname, &peeled))
67 			strbuf_addf(&refline, " peeled:%s", oid_to_hex(&peeled));
68 	}
69 
70 	strbuf_addch(&refline, '\n');
71 	packet_write(1, refline.buf, refline.len);
72 
73 	strbuf_release(&refline);
74 	return 0;
75 }
76 
ls_refs_config(const char * var,const char * value,void * data)77 static int ls_refs_config(const char *var, const char *value, void *data)
78 {
79 	/*
80 	 * We only serve fetches over v2 for now, so respect only "uploadpack"
81 	 * config. This may need to eventually be expanded to "receive", but we
82 	 * don't yet know how that information will be passed to ls-refs.
83 	 */
84 	return parse_hide_refs_config(var, value, "uploadpack");
85 }
86 
ls_refs(struct repository * r,struct argv_array * keys,struct packet_reader * request)87 int ls_refs(struct repository *r, struct argv_array *keys,
88 	    struct packet_reader *request)
89 {
90 	struct ls_refs_data data;
91 
92 	memset(&data, 0, sizeof(data));
93 
94 	git_config(ls_refs_config, NULL);
95 
96 	while (packet_reader_read(request) != PACKET_READ_FLUSH) {
97 		const char *arg = request->line;
98 		const char *out;
99 
100 		if (!strcmp("peel", arg))
101 			data.peel = 1;
102 		else if (!strcmp("symrefs", arg))
103 			data.symrefs = 1;
104 		else if (skip_prefix(arg, "ref-prefix ", &out))
105 			argv_array_push(&data.prefixes, out);
106 	}
107 
108 	head_ref_namespaced(send_ref, &data);
109 	for_each_namespaced_ref(send_ref, &data);
110 	packet_flush(1);
111 	argv_array_clear(&data.prefixes);
112 	return 0;
113 }
114