summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--bundle-uri.c104
-rw-r--r--bundle-uri.h14
-rwxr-xr-xt/t5557-http-get.sh2
4 files changed, 121 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index 1624471bad..7d5f48069e 100644
--- a/Makefile
+++ b/Makefile
@@ -906,6 +906,7 @@ LIB_OBJS += blob.o
LIB_OBJS += bloom.o
LIB_OBJS += branch.o
LIB_OBJS += bulk-checkin.o
+LIB_OBJS += bundle-uri.o
LIB_OBJS += bundle.o
LIB_OBJS += cache-tree.o
LIB_OBJS += cbtree.o
diff --git a/bundle-uri.c b/bundle-uri.c
new file mode 100644
index 0000000000..b35babc36a
--- /dev/null
+++ b/bundle-uri.c
@@ -0,0 +1,104 @@
+#include "cache.h"
+#include "bundle-uri.h"
+#include "bundle.h"
+#include "object-store.h"
+#include "refs.h"
+#include "run-command.h"
+
+static int find_temp_filename(struct strbuf *name)
+{
+ int fd;
+ /*
+ * Find a temporary filename that is available. This is briefly
+ * racy, but unlikely to collide.
+ */
+ fd = odb_mkstemp(name, "bundles/tmp_uri_XXXXXX");
+ if (fd < 0) {
+ warning(_("failed to create temporary file"));
+ return -1;
+ }
+
+ close(fd);
+ unlink(name->buf);
+ return 0;
+}
+
+static int copy_uri_to_file(const char *file, const char *uri)
+{
+ /* File-based URIs only for now. */
+ return copy_file(file, uri, 0);
+}
+
+static int unbundle_from_file(struct repository *r, const char *file)
+{
+ int result = 0;
+ int bundle_fd;
+ struct bundle_header header = BUNDLE_HEADER_INIT;
+ struct string_list_item *refname;
+ struct strbuf bundle_ref = STRBUF_INIT;
+ size_t bundle_prefix_len;
+
+ if ((bundle_fd = read_bundle_header(file, &header)) < 0)
+ return 1;
+
+ if ((result = unbundle(r, &header, bundle_fd, NULL)))
+ return 1;
+
+ /*
+ * Convert all refs/heads/ from the bundle into refs/bundles/
+ * in the local repository.
+ */
+ strbuf_addstr(&bundle_ref, "refs/bundles/");
+ bundle_prefix_len = bundle_ref.len;
+
+ for_each_string_list_item(refname, &header.references) {
+ struct object_id *oid = refname->util;
+ struct object_id old_oid;
+ const char *branch_name;
+ int has_old;
+
+ if (!skip_prefix(refname->string, "refs/heads/", &branch_name))
+ continue;
+
+ strbuf_setlen(&bundle_ref, bundle_prefix_len);
+ strbuf_addstr(&bundle_ref, branch_name);
+
+ has_old = !read_ref(bundle_ref.buf, &old_oid);
+ update_ref("fetched bundle", bundle_ref.buf, oid,
+ has_old ? &old_oid : NULL,
+ REF_SKIP_OID_VERIFICATION,
+ UPDATE_REFS_MSG_ON_ERR);
+ }
+
+ bundle_header_release(&header);
+ return result;
+}
+
+int fetch_bundle_uri(struct repository *r, const char *uri)
+{
+ int result = 0;
+ struct strbuf filename = STRBUF_INIT;
+
+ if ((result = find_temp_filename(&filename)))
+ goto cleanup;
+
+ if ((result = copy_uri_to_file(filename.buf, uri))) {
+ warning(_("failed to download bundle from URI '%s'"), uri);
+ goto cleanup;
+ }
+
+ if ((result = !is_bundle(filename.buf, 0))) {
+ warning(_("file at URI '%s' is not a bundle"), uri);
+ goto cleanup;
+ }
+
+ if ((result = unbundle_from_file(r, filename.buf))) {
+ warning(_("failed to unbundle bundle from URI '%s'"), uri);
+ goto cleanup;
+ }
+
+cleanup:
+ unlink(filename.buf);
+ strbuf_release(&filename);
+ return result;
+}
diff --git a/bundle-uri.h b/bundle-uri.h
new file mode 100644
index 0000000000..8a152f1ef1
--- /dev/null
+++ b/bundle-uri.h
@@ -0,0 +1,14 @@
+#ifndef BUNDLE_URI_H
+#define BUNDLE_URI_H
+
+struct repository;
+
+/**
+ * Fetch data from the given 'uri' and unbundle the bundle data found
+ * based on that information.
+ *
+ * Returns non-zero if no bundle information is found at the given 'uri'.
+ */
+int fetch_bundle_uri(struct repository *r, const char *uri);
+
+#endif
diff --git a/t/t5557-http-get.sh b/t/t5557-http-get.sh
index 09fb0bd9a8..76a4bbd16a 100755
--- a/t/t5557-http-get.sh
+++ b/t/t5557-http-get.sh
@@ -2,6 +2,8 @@
test_description='test downloading a file by URL'
+TEST_PASSES_SANITIZE_LEAK=true
+
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-httpd.sh