Merge branch 'jc/boundary'
* jc/boundary: git-bundle: prevent overwriting existing bundles git-bundle: die if a given ref is not included in bundle git-bundle: handle thin packs in subcommand "unbundle" git-bundle: Make thin packs git-bundle: avoid packing objects which are in the prerequisites bundle: fix wrong check of read_header()'s return value & add tests revision --boundary: fix uncounted case. revision --boundary: fix stupid typo git-bundle: make verify a bit more chatty. revision traversal: SHOWN means shown git-bundle: various fixups revision traversal: retire BOUNDARY_SHOW revision walker: Fix --boundary when limited
This commit is contained in:
180
builtin-bundle.c
180
builtin-bundle.c
@ -160,7 +160,28 @@ static int fork_with_pipe(const char **argv, int *in, int *out)
|
|||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int verify_bundle(struct bundle_header *header)
|
static int list_refs(struct ref_list *r, int argc, const char **argv)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < r->nr; i++) {
|
||||||
|
if (argc > 1) {
|
||||||
|
int j;
|
||||||
|
for (j = 1; j < argc; j++)
|
||||||
|
if (!strcmp(r->list[i].name, argv[j]))
|
||||||
|
break;
|
||||||
|
if (j == argc)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
printf("%s %s\n", sha1_to_hex(r->list[i].sha1),
|
||||||
|
r->list[i].name);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PREREQ_MARK (1u<<16)
|
||||||
|
|
||||||
|
static int verify_bundle(struct bundle_header *header, int verbose)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Do fast check, then if any prereqs are missing then go line by line
|
* Do fast check, then if any prereqs are missing then go line by line
|
||||||
@ -179,7 +200,7 @@ static int verify_bundle(struct bundle_header *header)
|
|||||||
struct ref_list_entry *e = p->list + i;
|
struct ref_list_entry *e = p->list + i;
|
||||||
struct object *o = parse_object(e->sha1);
|
struct object *o = parse_object(e->sha1);
|
||||||
if (o) {
|
if (o) {
|
||||||
o->flags |= BOUNDARY_SHOW;
|
o->flags |= PREREQ_MARK;
|
||||||
add_pending_object(&revs, o, e->name);
|
add_pending_object(&revs, o, e->name);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -187,7 +208,7 @@ static int verify_bundle(struct bundle_header *header)
|
|||||||
error(message);
|
error(message);
|
||||||
error("%s %s", sha1_to_hex(e->sha1), e->name);
|
error("%s %s", sha1_to_hex(e->sha1), e->name);
|
||||||
}
|
}
|
||||||
if (revs.pending.nr == 0)
|
if (revs.pending.nr != p->nr)
|
||||||
return ret;
|
return ret;
|
||||||
req_nr = revs.pending.nr;
|
req_nr = revs.pending.nr;
|
||||||
setup_revisions(2, argv, &revs, NULL);
|
setup_revisions(2, argv, &revs, NULL);
|
||||||
@ -202,7 +223,7 @@ static int verify_bundle(struct bundle_header *header)
|
|||||||
|
|
||||||
i = req_nr;
|
i = req_nr;
|
||||||
while (i && (commit = get_revision(&revs)))
|
while (i && (commit = get_revision(&revs)))
|
||||||
if (commit->object.flags & BOUNDARY_SHOW)
|
if (commit->object.flags & PREREQ_MARK)
|
||||||
i--;
|
i--;
|
||||||
|
|
||||||
for (i = 0; i < req_nr; i++)
|
for (i = 0; i < req_nr; i++)
|
||||||
@ -216,56 +237,24 @@ static int verify_bundle(struct bundle_header *header)
|
|||||||
for (i = 0; i < refs.nr; i++)
|
for (i = 0; i < refs.nr; i++)
|
||||||
clear_commit_marks((struct commit *)refs.objects[i].item, -1);
|
clear_commit_marks((struct commit *)refs.objects[i].item, -1);
|
||||||
|
|
||||||
|
if (verbose) {
|
||||||
|
struct ref_list *r;
|
||||||
|
|
||||||
|
r = &header->references;
|
||||||
|
printf("The bundle contains %d ref%s\n",
|
||||||
|
r->nr, (1 < r->nr) ? "s" : "");
|
||||||
|
list_refs(r, 0, NULL);
|
||||||
|
r = &header->prerequisites;
|
||||||
|
printf("The bundle requires these %d ref%s\n",
|
||||||
|
r->nr, (1 < r->nr) ? "s" : "");
|
||||||
|
list_refs(r, 0, NULL);
|
||||||
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int list_heads(struct bundle_header *header, int argc, const char **argv)
|
static int list_heads(struct bundle_header *header, int argc, const char **argv)
|
||||||
{
|
{
|
||||||
int i;
|
return list_refs(&header->references, argc, argv);
|
||||||
struct ref_list *r = &header->references;
|
|
||||||
|
|
||||||
for (i = 0; i < r->nr; i++) {
|
|
||||||
if (argc > 1) {
|
|
||||||
int j;
|
|
||||||
for (j = 1; j < argc; j++)
|
|
||||||
if (!strcmp(r->list[i].name, argv[j]))
|
|
||||||
break;
|
|
||||||
if (j == argc)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
printf("%s %s\n", sha1_to_hex(r->list[i].sha1),
|
|
||||||
r->list[i].name);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void show_commit(struct commit *commit)
|
|
||||||
{
|
|
||||||
write_or_die(1, sha1_to_hex(commit->object.sha1), 40);
|
|
||||||
write_or_die(1, "\n", 1);
|
|
||||||
if (commit->parents) {
|
|
||||||
free_commit_list(commit->parents);
|
|
||||||
commit->parents = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void show_object(struct object_array_entry *p)
|
|
||||||
{
|
|
||||||
/* An object with name "foo\n0000000..." can be used to
|
|
||||||
* confuse downstream git-pack-objects very badly.
|
|
||||||
*/
|
|
||||||
const char *ep = strchr(p->name, '\n');
|
|
||||||
int len = ep ? ep - p->name : strlen(p->name);
|
|
||||||
write_or_die(1, sha1_to_hex(p->item->sha1), 40);
|
|
||||||
write_or_die(1, " ", 1);
|
|
||||||
if (len)
|
|
||||||
write_or_die(1, p->name, len);
|
|
||||||
write_or_die(1, "\n", 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void show_edge(struct commit *commit)
|
|
||||||
{
|
|
||||||
; /* nothing to do */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int create_bundle(struct bundle_header *header, const char *path,
|
static int create_bundle(struct bundle_header *header, const char *path,
|
||||||
@ -273,19 +262,23 @@ static int create_bundle(struct bundle_header *header, const char *path,
|
|||||||
{
|
{
|
||||||
int bundle_fd = -1;
|
int bundle_fd = -1;
|
||||||
const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *));
|
const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *));
|
||||||
const char **argv_pack = xmalloc(4 * sizeof(const char *));
|
const char **argv_pack = xmalloc(5 * sizeof(const char *));
|
||||||
int pid, in, out, i, status;
|
int pid, in, out, i, status;
|
||||||
char buffer[1024];
|
char buffer[1024];
|
||||||
struct rev_info revs;
|
struct rev_info revs;
|
||||||
|
|
||||||
bundle_fd = (!strcmp(path, "-") ? 1 :
|
bundle_fd = (!strcmp(path, "-") ? 1 :
|
||||||
open(path, O_CREAT | O_WRONLY, 0666));
|
open(path, O_CREAT | O_EXCL | O_WRONLY, 0666));
|
||||||
if (bundle_fd < 0)
|
if (bundle_fd < 0)
|
||||||
return error("Could not write to '%s'", path);
|
return error("Could not create '%s': %s", path, strerror(errno));
|
||||||
|
|
||||||
/* write signature */
|
/* write signature */
|
||||||
write_or_die(bundle_fd, bundle_signature, strlen(bundle_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 */
|
/* write prerequisites */
|
||||||
memcpy(argv_boundary + 3, argv + 1, argc * sizeof(const char *));
|
memcpy(argv_boundary + 3, argv + 1, argc * sizeof(const char *));
|
||||||
argv_boundary[0] = "rev-list";
|
argv_boundary[0] = "rev-list";
|
||||||
@ -296,9 +289,20 @@ static int create_bundle(struct bundle_header *header, const char *path,
|
|||||||
pid = fork_with_pipe(argv_boundary, NULL, &out);
|
pid = fork_with_pipe(argv_boundary, NULL, &out);
|
||||||
if (pid < 0)
|
if (pid < 0)
|
||||||
return -1;
|
return -1;
|
||||||
while ((i = read_string(out, buffer, sizeof(buffer))) > 0)
|
while ((i = read_string(out, buffer, sizeof(buffer))) > 0) {
|
||||||
if (buffer[0] == '-')
|
unsigned char sha1[20];
|
||||||
|
if (buffer[0] == '-') {
|
||||||
write_or_die(bundle_fd, buffer, i);
|
write_or_die(bundle_fd, buffer, i);
|
||||||
|
if (!get_sha1_hex(buffer + 1, sha1)) {
|
||||||
|
struct object *object = parse_object(sha1);
|
||||||
|
object->flags |= UNINTERESTING;
|
||||||
|
add_pending_object(&revs, object, buffer);
|
||||||
|
}
|
||||||
|
} else if (!get_sha1_hex(buffer, sha1)) {
|
||||||
|
struct object *object = parse_object(sha1);
|
||||||
|
object->flags |= SHOWN;
|
||||||
|
}
|
||||||
|
}
|
||||||
while ((i = waitpid(pid, &status, 0)) < 0)
|
while ((i = waitpid(pid, &status, 0)) < 0)
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
return error("rev-list died");
|
return error("rev-list died");
|
||||||
@ -306,27 +310,32 @@ static int create_bundle(struct bundle_header *header, const char *path,
|
|||||||
return error("rev-list died %d", WEXITSTATUS(status));
|
return error("rev-list died %d", WEXITSTATUS(status));
|
||||||
|
|
||||||
/* write references */
|
/* write references */
|
||||||
save_commit_buffer = 0;
|
|
||||||
init_revisions(&revs, NULL);
|
|
||||||
revs.tag_objects = 1;
|
|
||||||
revs.tree_objects = 1;
|
|
||||||
revs.blob_objects = 1;
|
|
||||||
argc = setup_revisions(argc, argv, &revs, NULL);
|
argc = setup_revisions(argc, argv, &revs, NULL);
|
||||||
if (argc > 1)
|
if (argc > 1)
|
||||||
return error("unrecognized argument: %s'", argv[1]);
|
return error("unrecognized argument: %s'", argv[1]);
|
||||||
|
|
||||||
for (i = 0; i < revs.pending.nr; i++) {
|
for (i = 0; i < revs.pending.nr; i++) {
|
||||||
struct object_array_entry *e = revs.pending.objects + i;
|
struct object_array_entry *e = revs.pending.objects + i;
|
||||||
if (!(e->item->flags & UNINTERESTING)) {
|
unsigned char sha1[20];
|
||||||
unsigned char sha1[20];
|
char *ref;
|
||||||
char *ref;
|
|
||||||
if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
|
if (e->item->flags & UNINTERESTING)
|
||||||
continue;
|
continue;
|
||||||
write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40);
|
if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
|
||||||
write_or_die(bundle_fd, " ", 1);
|
continue;
|
||||||
write_or_die(bundle_fd, ref, strlen(ref));
|
/*
|
||||||
write_or_die(bundle_fd, "\n", 1);
|
* Make sure the refs we wrote out is correct; --max-count and
|
||||||
free(ref);
|
* other limiting options could have prevented all the tips
|
||||||
}
|
* from getting output.
|
||||||
|
*/
|
||||||
|
if (!(e->item->flags & SHOWN))
|
||||||
|
die("ref '%s' is excluded by the rev-list options",
|
||||||
|
e->name);
|
||||||
|
write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40);
|
||||||
|
write_or_die(bundle_fd, " ", 1);
|
||||||
|
write_or_die(bundle_fd, ref, strlen(ref));
|
||||||
|
write_or_die(bundle_fd, "\n", 1);
|
||||||
|
free(ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* end header */
|
/* end header */
|
||||||
@ -336,36 +345,42 @@ static int create_bundle(struct bundle_header *header, const char *path,
|
|||||||
argv_pack[0] = "pack-objects";
|
argv_pack[0] = "pack-objects";
|
||||||
argv_pack[1] = "--all-progress";
|
argv_pack[1] = "--all-progress";
|
||||||
argv_pack[2] = "--stdout";
|
argv_pack[2] = "--stdout";
|
||||||
argv_pack[3] = NULL;
|
argv_pack[3] = "--thin";
|
||||||
|
argv_pack[4] = NULL;
|
||||||
in = -1;
|
in = -1;
|
||||||
out = bundle_fd;
|
out = bundle_fd;
|
||||||
pid = fork_with_pipe(argv_pack, &in, &out);
|
pid = fork_with_pipe(argv_pack, &in, &out);
|
||||||
if (pid < 0)
|
if (pid < 0)
|
||||||
return error("Could not spawn pack-objects");
|
return error("Could not spawn pack-objects");
|
||||||
close(1);
|
for (i = 0; i < revs.pending.nr; i++) {
|
||||||
dup2(in, 1);
|
struct object *object = revs.pending.objects[i].item;
|
||||||
|
if (object->flags & UNINTERESTING)
|
||||||
|
write(in, "^", 1);
|
||||||
|
write(in, sha1_to_hex(object->sha1), 40);
|
||||||
|
write(in, "\n", 1);
|
||||||
|
}
|
||||||
close(in);
|
close(in);
|
||||||
prepare_revision_walk(&revs);
|
|
||||||
mark_edges_uninteresting(revs.commits, &revs, show_edge);
|
|
||||||
traverse_commit_list(&revs, show_commit, show_object);
|
|
||||||
close(1);
|
|
||||||
while (waitpid(pid, &status, 0) < 0)
|
while (waitpid(pid, &status, 0) < 0)
|
||||||
if (errno != EINTR)
|
if (errno != EINTR)
|
||||||
return -1;
|
return -1;
|
||||||
if (!WIFEXITED(status) || WEXITSTATUS(status))
|
if (!WIFEXITED(status) || WEXITSTATUS(status))
|
||||||
return error ("pack-objects died");
|
return error ("pack-objects died");
|
||||||
return 0;
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unbundle(struct bundle_header *header, int bundle_fd,
|
static int unbundle(struct bundle_header *header, int bundle_fd,
|
||||||
int argc, const char **argv)
|
int argc, const char **argv)
|
||||||
{
|
{
|
||||||
const char *argv_index_pack[] = {"index-pack", "--stdin", NULL};
|
const char *argv_index_pack[] = {"index-pack",
|
||||||
|
"--fix-thin", "--stdin", NULL};
|
||||||
int pid, status, dev_null;
|
int pid, status, dev_null;
|
||||||
|
|
||||||
if (verify_bundle(header))
|
if (verify_bundle(header, 0))
|
||||||
return -1;
|
return -1;
|
||||||
dev_null = open("/dev/null", O_WRONLY);
|
dev_null = open("/dev/null", O_WRONLY);
|
||||||
|
if (dev_null < 0)
|
||||||
|
return error("Could not open /dev/null");
|
||||||
pid = fork_with_pipe(argv_index_pack, &bundle_fd, &dev_null);
|
pid = fork_with_pipe(argv_index_pack, &bundle_fd, &dev_null);
|
||||||
if (pid < 0)
|
if (pid < 0)
|
||||||
return error("Could not spawn index-pack");
|
return error("Could not spawn index-pack");
|
||||||
@ -402,12 +417,12 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
|
|||||||
|
|
||||||
memset(&header, 0, sizeof(header));
|
memset(&header, 0, sizeof(header));
|
||||||
if (strcmp(cmd, "create") &&
|
if (strcmp(cmd, "create") &&
|
||||||
!(bundle_fd = read_header(bundle_file, &header)))
|
(bundle_fd = read_header(bundle_file, &header)) < 0)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (!strcmp(cmd, "verify")) {
|
if (!strcmp(cmd, "verify")) {
|
||||||
close(bundle_fd);
|
close(bundle_fd);
|
||||||
if (verify_bundle(&header))
|
if (verify_bundle(&header, 1))
|
||||||
return 1;
|
return 1;
|
||||||
fprintf(stderr, "%s is okay\n", bundle_file);
|
fprintf(stderr, "%s is okay\n", bundle_file);
|
||||||
return 0;
|
return 0;
|
||||||
@ -427,4 +442,3 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
|
|||||||
} else
|
} else
|
||||||
usage(bundle_usage);
|
usage(bundle_usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
219
revision.c
219
revision.c
@ -437,36 +437,6 @@ static void limit_list(struct rev_info *revs)
|
|||||||
continue;
|
continue;
|
||||||
p = &commit_list_insert(commit, p)->next;
|
p = &commit_list_insert(commit, p)->next;
|
||||||
}
|
}
|
||||||
if (revs->boundary) {
|
|
||||||
/* mark the ones that are on the result list first */
|
|
||||||
for (list = newlist; list; list = list->next) {
|
|
||||||
struct commit *commit = list->item;
|
|
||||||
commit->object.flags |= TMP_MARK;
|
|
||||||
}
|
|
||||||
for (list = newlist; list; list = list->next) {
|
|
||||||
struct commit *commit = list->item;
|
|
||||||
struct object *obj = &commit->object;
|
|
||||||
struct commit_list *parent;
|
|
||||||
if (obj->flags & UNINTERESTING)
|
|
||||||
continue;
|
|
||||||
for (parent = commit->parents;
|
|
||||||
parent;
|
|
||||||
parent = parent->next) {
|
|
||||||
struct commit *pcommit = parent->item;
|
|
||||||
if (!(pcommit->object.flags & UNINTERESTING))
|
|
||||||
continue;
|
|
||||||
pcommit->object.flags |= BOUNDARY;
|
|
||||||
if (pcommit->object.flags & TMP_MARK)
|
|
||||||
continue;
|
|
||||||
pcommit->object.flags |= TMP_MARK;
|
|
||||||
p = &commit_list_insert(pcommit, p)->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (list = newlist; list; list = list->next) {
|
|
||||||
struct commit *commit = list->item;
|
|
||||||
commit->object.flags &= ~TMP_MARK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
revs->commits = newlist;
|
revs->commits = newlist;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1193,17 +1163,6 @@ static void rewrite_parents(struct rev_info *revs, struct commit *commit)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mark_boundary_to_show(struct commit *commit)
|
|
||||||
{
|
|
||||||
struct commit_list *p = commit->parents;
|
|
||||||
while (p) {
|
|
||||||
commit = p->item;
|
|
||||||
p = p->next;
|
|
||||||
if (commit->object.flags & BOUNDARY)
|
|
||||||
commit->object.flags |= BOUNDARY_SHOW;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int commit_match(struct commit *commit, struct rev_info *opt)
|
static int commit_match(struct commit *commit, struct rev_info *opt)
|
||||||
{
|
{
|
||||||
if (!opt->grep_filter)
|
if (!opt->grep_filter)
|
||||||
@ -1235,15 +1194,9 @@ static struct commit *get_revision_1(struct rev_info *revs)
|
|||||||
*/
|
*/
|
||||||
if (!revs->limited) {
|
if (!revs->limited) {
|
||||||
if (revs->max_age != -1 &&
|
if (revs->max_age != -1 &&
|
||||||
(commit->date < revs->max_age)) {
|
(commit->date < revs->max_age))
|
||||||
if (revs->boundary)
|
continue;
|
||||||
commit->object.flags |=
|
add_parents_to_list(revs, commit, &revs->commits);
|
||||||
BOUNDARY_SHOW | BOUNDARY;
|
|
||||||
else
|
|
||||||
continue;
|
|
||||||
} else
|
|
||||||
add_parents_to_list(revs, commit,
|
|
||||||
&revs->commits);
|
|
||||||
}
|
}
|
||||||
if (commit->object.flags & SHOWN)
|
if (commit->object.flags & SHOWN)
|
||||||
continue;
|
continue;
|
||||||
@ -1252,18 +1205,6 @@ static struct commit *get_revision_1(struct rev_info *revs)
|
|||||||
revs->ignore_packed))
|
revs->ignore_packed))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* We want to show boundary commits only when their
|
|
||||||
* children are shown. When path-limiter is in effect,
|
|
||||||
* rewrite_parents() drops some commits from getting shown,
|
|
||||||
* and there is no point showing boundary parents that
|
|
||||||
* are not shown. After rewrite_parents() rewrites the
|
|
||||||
* parents of a commit that is shown, we mark the boundary
|
|
||||||
* parents with BOUNDARY_SHOW.
|
|
||||||
*/
|
|
||||||
if (commit->object.flags & BOUNDARY_SHOW) {
|
|
||||||
commit->object.flags |= SHOWN;
|
|
||||||
return commit;
|
|
||||||
}
|
|
||||||
if (commit->object.flags & UNINTERESTING)
|
if (commit->object.flags & UNINTERESTING)
|
||||||
continue;
|
continue;
|
||||||
if (revs->min_age != -1 && (commit->date > revs->min_age))
|
if (revs->min_age != -1 && (commit->date > revs->min_age))
|
||||||
@ -1286,80 +1227,136 @@ static struct commit *get_revision_1(struct rev_info *revs)
|
|||||||
if (revs->parents)
|
if (revs->parents)
|
||||||
rewrite_parents(revs, commit);
|
rewrite_parents(revs, commit);
|
||||||
}
|
}
|
||||||
if (revs->boundary)
|
|
||||||
mark_boundary_to_show(commit);
|
|
||||||
commit->object.flags |= SHOWN;
|
|
||||||
return commit;
|
return commit;
|
||||||
} while (revs->commits);
|
} while (revs->commits);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gc_boundary(struct object_array *array)
|
||||||
|
{
|
||||||
|
unsigned nr = array->nr;
|
||||||
|
unsigned alloc = array->alloc;
|
||||||
|
struct object_array_entry *objects = array->objects;
|
||||||
|
|
||||||
|
if (alloc <= nr) {
|
||||||
|
unsigned i, j;
|
||||||
|
for (i = j = 0; i < nr; i++) {
|
||||||
|
if (objects[i].item->flags & SHOWN)
|
||||||
|
continue;
|
||||||
|
if (i != j)
|
||||||
|
objects[j] = objects[i];
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
for (i = j; i < nr; i++)
|
||||||
|
objects[i].item = NULL;
|
||||||
|
array->nr = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct commit *get_revision(struct rev_info *revs)
|
struct commit *get_revision(struct rev_info *revs)
|
||||||
{
|
{
|
||||||
struct commit *c = NULL;
|
struct commit *c = NULL;
|
||||||
|
struct commit_list *l;
|
||||||
|
|
||||||
if (revs->reverse) {
|
if (revs->boundary == 2) {
|
||||||
struct commit_list *list;
|
unsigned i;
|
||||||
|
struct object_array *array = &revs->boundary_commits;
|
||||||
/*
|
struct object_array_entry *objects = array->objects;
|
||||||
* rev_info.reverse is used to note the fact that we
|
for (i = 0; i < array->nr; i++) {
|
||||||
* want to output the list of revisions in reverse
|
c = (struct commit *)(objects[i].item);
|
||||||
* order. To accomplish this goal, reverse can have
|
if (!c)
|
||||||
* different values:
|
continue;
|
||||||
*
|
if (!(c->object.flags & CHILD_SHOWN))
|
||||||
* 0 do nothing
|
continue;
|
||||||
* 1 reverse the list
|
if (!(c->object.flags & SHOWN))
|
||||||
* 2 internal use: we have already obtained and
|
break;
|
||||||
* reversed the list, now we only need to yield
|
|
||||||
* its items.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (revs->reverse == 1) {
|
|
||||||
revs->reverse = 0;
|
|
||||||
list = NULL;
|
|
||||||
while ((c = get_revision(revs)))
|
|
||||||
commit_list_insert(c, &list);
|
|
||||||
revs->commits = list;
|
|
||||||
revs->reverse = 2;
|
|
||||||
}
|
}
|
||||||
|
if (array->nr <= i)
|
||||||
if (!revs->commits)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
c = revs->commits->item;
|
|
||||||
list = revs->commits->next;
|
c->object.flags |= SHOWN | BOUNDARY;
|
||||||
free(revs->commits);
|
|
||||||
revs->commits = list;
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 < revs->skip_count) {
|
if (revs->reverse) {
|
||||||
while ((c = get_revision_1(revs)) != NULL) {
|
int limit = -1;
|
||||||
if (revs->skip_count-- <= 0)
|
|
||||||
|
if (0 <= revs->max_count) {
|
||||||
|
limit = revs->max_count;
|
||||||
|
if (0 < revs->skip_count)
|
||||||
|
limit += revs->skip_count;
|
||||||
|
}
|
||||||
|
l = NULL;
|
||||||
|
while ((c = get_revision_1(revs))) {
|
||||||
|
commit_list_insert(c, &l);
|
||||||
|
if ((0 < limit) && !--limit)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
revs->commits = l;
|
||||||
|
revs->reverse = 0;
|
||||||
|
revs->max_count = -1;
|
||||||
|
c = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now pick up what they want to give us
|
||||||
|
*/
|
||||||
|
c = get_revision_1(revs);
|
||||||
|
if (c) {
|
||||||
|
while (0 < revs->skip_count) {
|
||||||
|
revs->skip_count--;
|
||||||
|
c = get_revision_1(revs);
|
||||||
|
if (!c)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check the max_count ... */
|
/*
|
||||||
|
* Check the max_count.
|
||||||
|
*/
|
||||||
switch (revs->max_count) {
|
switch (revs->max_count) {
|
||||||
case -1:
|
case -1:
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
if (revs->boundary) {
|
c = NULL;
|
||||||
struct commit_list *list = revs->commits;
|
break;
|
||||||
while (list) {
|
|
||||||
list->item->object.flags |=
|
|
||||||
BOUNDARY_SHOW | BOUNDARY;
|
|
||||||
list = list->next;
|
|
||||||
}
|
|
||||||
/* all remaining commits are boundary commits */
|
|
||||||
revs->max_count = -1;
|
|
||||||
revs->limited = 1;
|
|
||||||
} else
|
|
||||||
return NULL;
|
|
||||||
default:
|
default:
|
||||||
revs->max_count--;
|
revs->max_count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c)
|
if (c)
|
||||||
|
c->object.flags |= SHOWN;
|
||||||
|
|
||||||
|
if (!revs->boundary) {
|
||||||
return c;
|
return c;
|
||||||
return get_revision_1(revs);
|
}
|
||||||
|
|
||||||
|
if (!c) {
|
||||||
|
/*
|
||||||
|
* get_revision_1() runs out the commits, and
|
||||||
|
* we are done computing the boundaries.
|
||||||
|
* switch to boundary commits output mode.
|
||||||
|
*/
|
||||||
|
revs->boundary = 2;
|
||||||
|
return get_revision(revs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* boundary commits are the commits that are parents of the
|
||||||
|
* ones we got from get_revision_1() but they themselves are
|
||||||
|
* not returned from get_revision_1(). Before returning
|
||||||
|
* 'c', we need to mark its parents that they could be boundaries.
|
||||||
|
*/
|
||||||
|
|
||||||
|
for (l = c->parents; l; l = l->next) {
|
||||||
|
struct object *p;
|
||||||
|
p = &(l->item->object);
|
||||||
|
if (p->flags & (CHILD_SHOWN | SHOWN))
|
||||||
|
continue;
|
||||||
|
p->flags |= CHILD_SHOWN;
|
||||||
|
gc_boundary(&revs->boundary_commits);
|
||||||
|
add_object_array(p, NULL, &revs->boundary_commits);
|
||||||
|
}
|
||||||
|
|
||||||
|
return c;
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#define SHOWN (1u<<3)
|
#define SHOWN (1u<<3)
|
||||||
#define TMP_MARK (1u<<4) /* for isolated cases; clean after use */
|
#define TMP_MARK (1u<<4) /* for isolated cases; clean after use */
|
||||||
#define BOUNDARY (1u<<5)
|
#define BOUNDARY (1u<<5)
|
||||||
#define BOUNDARY_SHOW (1u<<6)
|
#define CHILD_SHOWN (1u<<6)
|
||||||
#define ADDED (1u<<7) /* Parents already parsed and added? */
|
#define ADDED (1u<<7) /* Parents already parsed and added? */
|
||||||
#define SYMMETRIC_LEFT (1u<<8)
|
#define SYMMETRIC_LEFT (1u<<8)
|
||||||
|
|
||||||
@ -21,6 +21,9 @@ struct rev_info {
|
|||||||
struct commit_list *commits;
|
struct commit_list *commits;
|
||||||
struct object_array pending;
|
struct object_array pending;
|
||||||
|
|
||||||
|
/* Parents of shown commits */
|
||||||
|
struct object_array boundary_commits;
|
||||||
|
|
||||||
/* Basic information */
|
/* Basic information */
|
||||||
const char *prefix;
|
const char *prefix;
|
||||||
void *prune_data;
|
void *prune_data;
|
||||||
@ -40,10 +43,10 @@ struct rev_info {
|
|||||||
edge_hint:1,
|
edge_hint:1,
|
||||||
limited:1,
|
limited:1,
|
||||||
unpacked:1, /* see also ignore_packed below */
|
unpacked:1, /* see also ignore_packed below */
|
||||||
boundary:1,
|
boundary:2,
|
||||||
left_right:1,
|
left_right:1,
|
||||||
parents:1,
|
parents:1,
|
||||||
reverse:2;
|
reverse:1;
|
||||||
|
|
||||||
/* Diff flags */
|
/* Diff flags */
|
||||||
unsigned int diff:1,
|
unsigned int diff:1,
|
||||||
|
@ -90,6 +90,13 @@ test_expect_success 'create bundle 1' '
|
|||||||
git bundle create bundle1 master^..master
|
git bundle create bundle1 master^..master
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'header of bundle looks right' '
|
||||||
|
head -n 1 "$D"/bundle1 | grep "^#" &&
|
||||||
|
head -n 2 "$D"/bundle1 | grep "^-[0-9a-f]\{40\} " &&
|
||||||
|
head -n 3 "$D"/bundle1 | grep "^[0-9a-f]\{40\} " &&
|
||||||
|
head -n 4 "$D"/bundle1 | grep "^$"
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'create bundle 2' '
|
test_expect_success 'create bundle 2' '
|
||||||
cd "$D" &&
|
cd "$D" &&
|
||||||
git bundle create bundle2 master~2..master
|
git bundle create bundle2 master~2..master
|
||||||
@ -101,10 +108,35 @@ test_expect_failure 'unbundle 1' '
|
|||||||
git fetch "$D/bundle1" master:master
|
git fetch "$D/bundle1" master:master
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'bundle 1 has only 3 files ' '
|
||||||
|
cd "$D" &&
|
||||||
|
(
|
||||||
|
while read x && test -n "$x"
|
||||||
|
do
|
||||||
|
:;
|
||||||
|
done
|
||||||
|
cat
|
||||||
|
) <bundle1 >bundle.pack &&
|
||||||
|
git index-pack bundle.pack &&
|
||||||
|
verify=$(git verify-pack -v bundle.pack) &&
|
||||||
|
test 4 = $(echo "$verify" | wc -l)
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'unbundle 2' '
|
test_expect_success 'unbundle 2' '
|
||||||
cd "$D/bundle" &&
|
cd "$D/bundle" &&
|
||||||
git fetch ../bundle2 master:master &&
|
git fetch ../bundle2 master:master &&
|
||||||
test "tip" = "$(git log -1 --pretty=oneline master | cut -b42-)"
|
test "tip" = "$(git log -1 --pretty=oneline master | cut -b42-)"
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'bundle does not prerequisite objects' '
|
||||||
|
cd "$D" &&
|
||||||
|
touch file2 &&
|
||||||
|
git add file2 &&
|
||||||
|
git commit -m add.file2 file2 &&
|
||||||
|
git bundle create bundle3 -1 HEAD &&
|
||||||
|
sed "1,4d" < bundle3 > bundle.pack &&
|
||||||
|
git index-pack bundle.pack &&
|
||||||
|
test 4 = $(git verify-pack -v bundle.pack | wc -l)
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Reference in New Issue
Block a user