X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;ds=sidebyside;f=builtin-bundle.c;h=1b650069c929744c43f95e62ca49f8a542a70111;hb=0e06cc8b823144be16a9fc1f703126b68d20d3b5;hp=306ad29597dbf9002a44ba509c2e9d7a737b159d;hpb=b8336519450fdedc8f9cafebc73f7a0226e34e1f;p=git.git diff --git a/builtin-bundle.c b/builtin-bundle.c index 306ad2959..1b650069c 100644 --- a/builtin-bundle.c +++ b/builtin-bundle.c @@ -1,3 +1,4 @@ +#include "builtin.h" #include "cache.h" #include "object.h" #include "commit.h" @@ -43,38 +44,21 @@ struct bundle_header { struct ref_list references; }; -/* this function returns the length of the string */ -static int read_string(int fd, char *buffer, int size) -{ - int i; - for (i = 0; i < size - 1; i++) { - ssize_t count = xread(fd, buffer + i, 1); - if (count < 0) - return error("Read error: %s", strerror(errno)); - if (count == 0) { - i--; - break; - } - if (buffer[i] == '\n') - break; - } - buffer[i + 1] = '\0'; - return i + 1; -} - /* returns an fd */ static int read_header(const char *path, struct bundle_header *header) { char buffer[1024]; - int fd = open(path, O_RDONLY); + int fd; + long fpos; + FILE *ffd = fopen(path, "rb"); - if (fd < 0) + if (!ffd) return error("could not open '%s'", path); - if (read_string(fd, buffer, sizeof(buffer)) < 0 || + if (!fgets(buffer, sizeof(buffer), ffd) || strcmp(buffer, bundle_signature)) { - close(fd); + fclose(ffd); return error("'%s' does not look like a v2 bundle file", path); } - while (read_string(fd, buffer, sizeof(buffer)) > 0 + while (fgets(buffer, sizeof(buffer), ffd) && buffer[0] != '\n') { int is_prereq = buffer[0] == '-'; int offset = is_prereq ? 1 : 0; @@ -96,6 +80,12 @@ static int read_header(const char *path, struct bundle_header *header) { add_to_ref_list(sha1, isspace(delim) ? buffer + 41 + offset : "", list); } + fpos = ftell(ffd); + fclose(ffd); + fd = open(path, O_RDONLY); + if (fd < 0) + return error("could not open '%s'", path); + lseek(fd, fpos, SEEK_SET); return fd; } @@ -199,18 +189,22 @@ static int list_heads(struct bundle_header *header, int argc, const char **argv) static 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; const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *)); const char **argv_pack = xmalloc(5 * sizeof(const char *)); int i, ref_count = 0; char buffer[1024]; struct rev_info revs; struct child_process rls; + FILE *rls_fout; - bundle_fd = (!strcmp(path, "-") ? 1 : - open(path, O_CREAT | O_EXCL | O_WRONLY, 0666)); - if (bundle_fd < 0) - return error("Could not create '%s': %s", path, strerror(errno)); + bundle_to_stdout = !strcmp(path, "-"); + if (bundle_to_stdout) + bundle_fd = 1; + else + bundle_fd = hold_lock_file_for_update(&lock, path, 1); /* write signature */ write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature)); @@ -231,10 +225,11 @@ static int create_bundle(struct bundle_header *header, const char *path, rls.git_cmd = 1; if (start_command(&rls)) return -1; - while ((i = read_string(rls.out, buffer, sizeof(buffer))) > 0) { + rls_fout = fdopen(rls.out, "r"); + while (fgets(buffer, sizeof(buffer), rls_fout)) { unsigned char sha1[20]; if (buffer[0] == '-') { - write_or_die(bundle_fd, buffer, i); + write_or_die(bundle_fd, buffer, strlen(buffer)); if (!get_sha1_hex(buffer + 1, sha1)) { struct object *object = parse_object(sha1); object->flags |= UNINTERESTING; @@ -245,6 +240,7 @@ static int create_bundle(struct bundle_header *header, const char *path, object->flags |= SHOWN; } } + fclose(rls_fout); if (finish_command(&rls)) return error("rev-list died"); @@ -266,12 +262,49 @@ static int create_bundle(struct bundle_header *header, const char *path, * Make sure the refs we wrote out is correct; --max-count and * other limiting options could have prevented all the tips * from getting output. + * + * Non commit objects such as tags and blobs do not have + * this issue as they are not affected by those extra + * constraints. */ - if (!(e->item->flags & SHOWN)) { + 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; + } + /* + * If you run "git bundle create bndl v1.0..v2.0", the + * name of the positive ref is "v2.0" but that is the + * commit that is referenced by the tag, and not the tag + * itself. + */ + if (hashcmp(sha1, e->item->sha1)) { + /* + * 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 object *obj; + + if (e->item == &(one->object)) { + /* + * Need to include e->name as an + * independent ref to the pack-objects + * input, so that the tag is included + * in the output; otherwise we would + * end up triggering "empty bundle" + * error. + */ + obj = parse_object(sha1); + obj->flags |= SHOWN; + add_pending_object(&revs, obj, e->name); + } + free(ref); continue; } + ref_count++; write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40); write_or_die(bundle_fd, " ", 1); @@ -307,6 +340,9 @@ static int create_bundle(struct bundle_header *header, const char *path, } if (finish_command(&rls)) return error ("pack-objects died"); + close(bundle_fd); + if (!bundle_to_stdout) + commit_lock_file(&lock); return 0; }