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 <bmw...@google.com>
---
 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 000000000..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 000000000..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 },
 };
 
 static void advertise_capabilities(void)
-- 
2.15.1.424.g9478a66081-goog

Reply via email to