summaryrefslogtreecommitdiff
path: root/bundle.c
diff options
context:
space:
mode:
Diffstat (limited to 'bundle.c')
-rw-r--r--bundle.c254
1 files changed, 152 insertions, 102 deletions
diff --git a/bundle.c b/bundle.c
index e99065ce42..bbf4efa0a0 100644
--- a/bundle.c
+++ b/bundle.c
@@ -1,4 +1,5 @@
#include "cache.h"
+#include "lockfile.h"
#include "bundle.h"
#include "object.h"
#include "commit.h"
@@ -14,12 +15,8 @@ static const char bundle_signature[] = "# v2 git bundle\n";
static void add_to_ref_list(const unsigned char *sha1, const char *name,
struct ref_list *list)
{
- if (list->nr + 1 >= list->alloc) {
- list->alloc = alloc_nr(list->nr + 1);
- list->list = xrealloc(list->list,
- list->alloc * sizeof(list->list[0]));
- }
- memcpy(list->list[list->nr].sha1, sha1, 20);
+ ALLOC_GROW(list->list, list->nr + 1, list->alloc);
+ hashcpy(list->list[list->nr].sha1, sha1);
list->list[list->nr].name = xstrdup(name);
list->nr++;
}
@@ -124,6 +121,7 @@ static int list_refs(struct ref_list *r, int argc, const char **argv)
return 0;
}
+/* Remember to update object flag allocation in object.h */
#define PREREQ_MARK (1u<<16)
int verify_bundle(struct bundle_header *header, int verbose)
@@ -173,7 +171,7 @@ int verify_bundle(struct bundle_header *header, int verbose)
if (!(refs.objects[i].item->flags & SHOWN)) {
if (++ret == 1)
error("%s", message);
- error("%s %s", sha1_to_hex(refs.objects[i].item->sha1),
+ error("%s %s", oid_to_hex(&refs.objects[i].item->oid),
refs.objects[i].name);
}
@@ -212,65 +210,75 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
{
unsigned long size;
enum object_type type;
- char *buf, *line, *lineend;
+ char *buf = NULL, *line, *lineend;
unsigned long date;
+ int result = 1;
if (revs->max_age == -1 && revs->min_age == -1)
- return 1;
+ goto out;
- buf = read_sha1_file(tag->sha1, &type, &size);
+ buf = read_sha1_file(tag->oid.hash, &type, &size);
if (!buf)
- return 1;
+ goto out;
line = memmem(buf, size, "\ntagger ", 8);
if (!line++)
- return 1;
- lineend = memchr(line, buf + size - line, '\n');
- line = memchr(line, lineend ? lineend - line : buf + size - line, '>');
+ goto out;
+ lineend = memchr(line, '\n', buf + size - line);
+ line = memchr(line, '>', lineend ? lineend - line : buf + size - line);
if (!line++)
- return 1;
+ goto out;
date = strtoul(line, NULL, 10);
- free(buf);
- return (revs->max_age == -1 || revs->max_age < date) &&
+ result = (revs->max_age == -1 || revs->max_age < date) &&
(revs->min_age == -1 || revs->min_age > date);
+out:
+ free(buf);
+ return result;
}
-int create_bundle(struct bundle_header *header, const char *path,
- int argc, const char **argv)
+
+/* Write the pack data to bundle_fd, then close it if it is > 1. */
+static int write_pack_data(int bundle_fd, struct rev_info *revs)
{
- static struct lock_file lock;
- int bundle_fd = -1;
- int bundle_to_stdout;
- struct argv_array argv_boundary = ARGV_ARRAY_INIT;
- struct argv_array argv_pack = ARGV_ARRAY_INIT;
- int i, ref_count = 0;
- struct strbuf buf = STRBUF_INIT;
- struct rev_info revs;
- struct child_process rls;
- FILE *rls_fout;
+ struct child_process pack_objects = CHILD_PROCESS_INIT;
+ int i;
- bundle_to_stdout = !strcmp(path, "-");
- if (bundle_to_stdout)
- bundle_fd = 1;
- else
- bundle_fd = hold_lock_file_for_update(&lock, path,
- LOCK_DIE_ON_ERROR);
+ argv_array_pushl(&pack_objects.args,
+ "pack-objects", "--all-progress-implied",
+ "--stdout", "--thin", "--delta-base-offset",
+ NULL);
+ pack_objects.in = -1;
+ pack_objects.out = bundle_fd;
+ pack_objects.git_cmd = 1;
+ if (start_command(&pack_objects))
+ return error(_("Could not spawn pack-objects"));
- /* write signature */
- write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature));
+ for (i = 0; i < revs->pending.nr; i++) {
+ struct object *object = revs->pending.objects[i].item;
+ if (object->flags & UNINTERESTING)
+ write_or_die(pack_objects.in, "^", 1);
+ write_or_die(pack_objects.in, oid_to_hex(&object->oid), GIT_SHA1_HEXSZ);
+ write_or_die(pack_objects.in, "\n", 1);
+ }
+ close(pack_objects.in);
+ if (finish_command(&pack_objects))
+ return error(_("pack-objects died"));
+ return 0;
+}
- /* init revs to list objects for pack-objects later */
- save_commit_buffer = 0;
- init_revisions(&revs, NULL);
+static int compute_and_write_prerequisites(int bundle_fd,
+ struct rev_info *revs,
+ int argc, const char **argv)
+{
+ struct child_process rls = CHILD_PROCESS_INIT;
+ struct strbuf buf = STRBUF_INIT;
+ FILE *rls_fout;
+ int i;
- /* write prerequisites */
- argv_array_pushl(&argv_boundary,
+ argv_array_pushl(&rls.args,
"rev-list", "--boundary", "--pretty=oneline",
NULL);
for (i = 1; i < argc; i++)
- argv_array_push(&argv_boundary, argv[i]);
-
- memset(&rls, 0, sizeof(rls));
- rls.argv = argv_boundary.argv;
+ argv_array_push(&rls.args, argv[i]);
rls.out = -1;
rls.git_cmd = 1;
if (start_command(&rls))
@@ -283,7 +291,7 @@ int create_bundle(struct bundle_header *header, const char *path,
if (!get_sha1_hex(buf.buf + 1, sha1)) {
struct object *object = parse_object_or_die(sha1, buf.buf);
object->flags |= UNINTERESTING;
- add_pending_object(&revs, object, buf.buf);
+ add_pending_object(revs, object, buf.buf);
}
} else if (!get_sha1_hex(buf.buf, sha1)) {
struct object *object = parse_object_or_die(sha1, buf.buf);
@@ -294,34 +302,42 @@ int create_bundle(struct bundle_header *header, const char *path,
fclose(rls_fout);
if (finish_command(&rls))
return error(_("rev-list died"));
+ return 0;
+}
- /* write references */
- argc = setup_revisions(argc, argv, &revs, NULL);
-
- if (argc > 1)
- return error(_("unrecognized argument: %s"), argv[1]);
-
- object_array_remove_duplicates(&revs.pending);
+/*
+ * Write out bundle refs based on the tips already
+ * parsed into revs.pending. As a side effect, may
+ * manipulate revs.pending to include additional
+ * necessary objects (like tags).
+ *
+ * Returns the number of refs written, or negative
+ * on error.
+ */
+static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
+{
+ int i;
+ int ref_count = 0;
- for (i = 0; i < revs.pending.nr; i++) {
- struct object_array_entry *e = revs.pending.objects + i;
- unsigned char sha1[20];
+ for (i = 0; i < revs->pending.nr; i++) {
+ struct object_array_entry *e = revs->pending.objects + i;
+ struct object_id oid;
char *ref;
const char *display_ref;
int flag;
if (e->item->flags & UNINTERESTING)
continue;
- if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
- continue;
- if (read_ref_full(e->name, sha1, 1, &flag))
+ if (dwim_ref(e->name, strlen(e->name), oid.hash, &ref) != 1)
+ goto skip_write_ref;
+ if (read_ref_full(e->name, RESOLVE_REF_READING, oid.hash, &flag))
flag = 0;
display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
if (e->item->type == OBJ_TAG &&
- !is_tag_in_date_range(e->item, &revs)) {
+ !is_tag_in_date_range(e->item, revs)) {
e->item->flags |= UNINTERESTING;
- continue;
+ goto skip_write_ref;
}
/*
@@ -336,8 +352,7 @@ int create_bundle(struct bundle_header *header, const char *path,
if (!(e->item->flags & SHOWN) && e->item->type == OBJ_COMMIT) {
warning(_("ref '%s' is excluded by the rev-list options"),
e->name);
- free(ref);
- continue;
+ goto skip_write_ref;
}
/*
* If you run "git bundle create bndl v1.0..v2.0", the
@@ -345,13 +360,13 @@ int create_bundle(struct bundle_header *header, const char *path,
* commit that is referenced by the tag, and not the tag
* itself.
*/
- if (hashcmp(sha1, e->item->sha1)) {
+ if (oidcmp(&oid, &e->item->oid)) {
/*
* Is this the positive end of a range expressed
* in terms of a tag (e.g. v2.0 from the range
* "v1.0..v2.0")?
*/
- struct commit *one = lookup_commit_reference(sha1);
+ struct commit *one = lookup_commit_reference(oid.hash);
struct object *obj;
if (e->item == &(one->object)) {
@@ -363,76 +378,111 @@ int create_bundle(struct bundle_header *header, const char *path,
* end up triggering "empty bundle"
* error.
*/
- obj = parse_object_or_die(sha1, e->name);
+ obj = parse_object_or_die(oid.hash, e->name);
obj->flags |= SHOWN;
- add_pending_object(&revs, obj, e->name);
+ add_pending_object(revs, obj, e->name);
}
- free(ref);
- continue;
+ goto skip_write_ref;
}
ref_count++;
- write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40);
+ write_or_die(bundle_fd, oid_to_hex(&e->item->oid), 40);
write_or_die(bundle_fd, " ", 1);
write_or_die(bundle_fd, display_ref, strlen(display_ref));
write_or_die(bundle_fd, "\n", 1);
+ skip_write_ref:
free(ref);
}
- if (!ref_count)
- die(_("Refusing to create empty bundle."));
/* end header */
write_or_die(bundle_fd, "\n", 1);
+ return ref_count;
+}
- /* write pack */
- argv_array_pushl(&argv_pack,
- "pack-objects", "--all-progress-implied",
- "--stdout", "--thin", "--delta-base-offset",
- NULL);
- memset(&rls, 0, sizeof(rls));
- rls.argv = argv_pack.argv;
- rls.in = -1;
- rls.out = bundle_fd;
- rls.git_cmd = 1;
- if (start_command(&rls))
- return error(_("Could not spawn pack-objects"));
+int create_bundle(struct bundle_header *header, const char *path,
+ int argc, const char **argv)
+{
+ static struct lock_file lock;
+ int bundle_fd = -1;
+ int bundle_to_stdout;
+ int ref_count = 0;
+ struct rev_info revs;
- /*
- * start_command closed bundle_fd if it was > 1
- * so set the lock fd to -1 so commit_lock_file()
- * won't fail trying to close it.
- */
- lock.fd = -1;
+ bundle_to_stdout = !strcmp(path, "-");
+ if (bundle_to_stdout)
+ bundle_fd = 1;
+ else {
+ bundle_fd = hold_lock_file_for_update(&lock, path,
+ LOCK_DIE_ON_ERROR);
- for (i = 0; i < revs.pending.nr; i++) {
- struct object *object = revs.pending.objects[i].item;
- if (object->flags & UNINTERESTING)
- write_or_die(rls.in, "^", 1);
- write_or_die(rls.in, sha1_to_hex(object->sha1), 40);
- write_or_die(rls.in, "\n", 1);
+ /*
+ * write_pack_data() will close the fd passed to it,
+ * but commit_lock_file() will also try to close the
+ * lockfile's fd. So make a copy of the file
+ * descriptor to avoid trying to close it twice.
+ */
+ bundle_fd = dup(bundle_fd);
+ if (bundle_fd < 0)
+ die_errno("unable to dup file descriptor");
}
- close(rls.in);
- if (finish_command(&rls))
- return error(_("pack-objects died"));
+
+ /* write signature */
+ write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature));
+
+ /* init revs to list objects for pack-objects later */
+ save_commit_buffer = 0;
+ init_revisions(&revs, NULL);
+
+ /* write prerequisites */
+ if (compute_and_write_prerequisites(bundle_fd, &revs, argc, argv))
+ goto err;
+
+ argc = setup_revisions(argc, argv, &revs, NULL);
+
+ if (argc > 1) {
+ error(_("unrecognized argument: %s"), argv[1]);
+ goto err;
+ }
+
+ object_array_remove_duplicates(&revs.pending);
+
+ ref_count = write_bundle_refs(bundle_fd, &revs);
+ if (!ref_count)
+ die(_("Refusing to create empty bundle."));
+ else if (ref_count < 0)
+ goto err;
+
+ /* write pack */
+ if (write_pack_data(bundle_fd, &revs)) {
+ bundle_fd = -1; /* already closed by the above call */
+ goto err;
+ }
+
if (!bundle_to_stdout) {
if (commit_lock_file(&lock))
die_errno(_("cannot create '%s'"), path);
}
return 0;
+err:
+ if (!bundle_to_stdout) {
+ if (0 <= bundle_fd)
+ close(bundle_fd);
+ rollback_lock_file(&lock);
+ }
+ return -1;
}
int unbundle(struct bundle_header *header, int bundle_fd, int flags)
{
const char *argv_index_pack[] = {"index-pack",
"--fix-thin", "--stdin", NULL, NULL};
- struct child_process ip;
+ struct child_process ip = CHILD_PROCESS_INIT;
if (flags & BUNDLE_VERBOSE)
argv_index_pack[3] = "-v";
if (verify_bundle(header, 0))
return -1;
- memset(&ip, 0, sizeof(ip));
ip.argv = argv_index_pack;
ip.in = bundle_fd;
ip.no_stdout = 1;