Introduce the ls-refs server command. In protocol v2, the ls-refs
command is used to request the ref advertisement from the server. Since
it is a command which can be requested (as opposed to manditory in v1),
a clinet can sent a number of parameters in its request to limit the ref
advertisement based on provided ref-patterns.
Signed-off-by: Brandon Williams
---
Makefile | 1 +
ls-refs.c | 96 +++
ls-refs.h | 9 ++
serve.c | 8 ++
4 files changed, 114 insertions(+)
create mode 100644 ls-refs.c
create mode 100644 ls-refs.h
diff --git a/Makefile b/Makefile
index 710672cf4..be3c2f98b 100644
--- a/Makefile
+++ b/Makefile
@@ -807,6 +807,7 @@ LIB_OBJS += list-objects.o
LIB_OBJS += ll-merge.o
LIB_OBJS += lockfile.o
LIB_OBJS += log-tree.o
+LIB_OBJS += ls-refs.o
LIB_OBJS += mailinfo.o
LIB_OBJS += mailmap.o
LIB_OBJS += match-trees.o
diff --git a/ls-refs.c b/ls-refs.c
new file mode 100644
index 0..591dd105d
--- /dev/null
+++ b/ls-refs.c
@@ -0,0 +1,96 @@
+#include "cache.h"
+#include "repository.h"
+#include "refs.h"
+#include "remote.h"
+#include "argv-array.h"
+#include "ls-refs.h"
+#include "pkt-line.h"
+
+struct ls_refs_data {
+ unsigned peel;
+ unsigned symrefs;
+ struct argv_array patterns;
+};
+
+/*
+ * Is there one among the list of patterns that match the tail part
+ * of the path?
+ */
+static int tail_match(const char **pattern, const char *path)
+{
+ const char *p;
+ char *pathbuf;
+
+ if (!pattern)
+ return 1; /* no restriction */
+
+ pathbuf = xstrfmt("/%s", path);
+ while ((p = *(pattern++)) != NULL) {
+ if (!wildmatch(p, pathbuf, 0)) {
+ free(pathbuf);
+ return 1;
+ }
+ }
+ free(pathbuf);
+ return 0;
+}
+
+static int send_ref(const char *refname, const struct object_id *oid,
+ int flag, void *cb_data)
+{
+ struct ls_refs_data *data = cb_data;
+ const char *refname_nons = strip_namespace(refname);
+ struct strbuf refline = STRBUF_INIT;
+
+ if (data->patterns.argc && !tail_match(data->patterns.argv, refname))
+ return 0;
+
+ strbuf_addf(&refline, "%s %s", oid_to_hex(oid), refname_nons);
+ if (data->symrefs && flag & REF_ISSYMREF) {
+ struct object_id unused;
+ const char *symref_target = resolve_ref_unsafe(refname, 0,
+ unused.hash,
+ &flag);
+
+ if (!symref_target)
+ die("'%s' is a symref but it is not?", refname);
+
+ strbuf_addf(&refline, " %s", symref_target);
+ }
+
+ strbuf_addch(&refline, '\n');
+
+ packet_write(1, refline.buf, refline.len);
+ if (data->peel) {
+ struct object_id peeled;
+ if (!peel_ref(refname, peeled.hash))
+ packet_write_fmt(1, "%s %s^{}\n", oid_to_hex(&peeled),
+refname_nons);
+ }
+
+ strbuf_release(&refline);
+ return 0;
+}
+
+int ls_refs(struct repository *r, struct argv_array *keys, struct argv_array
*args)
+{
+ int i;
+ struct ls_refs_data data = { 0, 0, ARGV_ARRAY_INIT };
+
+ for (i = 0; i < args->argc; i++) {
+ if (!strcmp("--peeled", args->argv[i]))
+ data.peel = 1;
+ else if (!strcmp("--symrefs", args->argv[i]))
+ data.symrefs = 1;
+ else
+ /* Pattern */
+ argv_array_pushf(&data.patterns, "*/%s", args->argv[i]);
+
+ }
+
+ head_ref_namespaced(send_ref, &data);
+ for_each_namespaced_ref(send_ref, &data);
+ packet_flush(1);
+ argv_array_clear(&data.patterns);
+ return 0;
+}
diff --git a/ls-refs.h b/ls-refs.h
new file mode 100644
index 0..9e4c57bfe
--- /dev/null
+++ b/ls-refs.h
@@ -0,0 +1,9 @@
+#ifndef LS_REFS_H
+#define LS_REFS_H
+
+struct repository;
+struct argv_array;
+extern int ls_refs(struct repository *r, struct argv_array *keys,
+ struct argv_array *args);
+
+#endif /* LS_REFS_H */
diff --git a/serve.c b/serve.c
index 476e73b54..36f77c365 100644
--- a/serve.c
+++ b/serve.c
@@ -4,8 +4,15 @@
#include "pkt-line.h"
#include "version.h"
#include "argv-array.h"
+#include "ls-refs.h"
#include "serve.h"
+static int always_advertise(struct repository *r,
+ struct strbuf *value)
+{
+ return 1;
+}
+
static int agent_advertise(struct repository *r,
struct strbuf *value)
{
@@ -26,6 +33,7 @@ struct protocol_capability {
static struct protocol_capability capabilities[] = {
{ "agent", 0, agent_advertise, NULL },
+ { "ls-refs", 0, always_advertise, ls_refs }