* [PATCH] Add git-bundle: move objects and references by archive
@ 2007-02-22 0:59 ` Johannes Schindelin
2007-02-22 3:28 ` Nicolas Pitre
` (3 more replies)
0 siblings, 4 replies; 28+ messages in thread
From: Johannes Schindelin @ 2007-02-22 0:59 UTC (permalink / raw)
To: git; +Cc: Mark Levedahl, junkio
Some workflows require use of repositories on machines that cannot be
connected, preventing use of git-fetch / git-push to transport objects and
references between the repositories.
git-bundle provides an alternate transport mechanism, effectively allowing
git-fetch and git-pull to operate using sneakernet transport. `git-bundle
create` allows the user to create a bundle containing one or more branches
or tags, but with specified basis assumed to exist on the target
repository. At the receiving end, git-bundle acts like git-fetch-pack,
allowing the user to invoke git-fetch or git-pull using the bundle file as
the URL. git-fetch and git-ls-remote determine they have a bundle URL by
checking that the URL points to a file, but are otherwise unchanged in
operation with bundles.
The original patch was done by Mark Levedahl <mdl123@verizon.net>.
It was updated to make git-bundle a builtin, and get rid of the tar
format: now, the first line is supposed to say "# v2 git bundle", the next
lines either contain a prerequisite ("-" followed by the hash of the
needed commit), or a ref (the hash of a commit, followed by the name of
the ref), and finally the pack. As a result, the bundle argument can be
"-" now.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
This patch should apply cleanly to "next".
Mark, could you test on cygwin?
.gitignore | 1 +
Documentation/cmd-list.perl | 1 +
Documentation/git-bundle.txt | 139 +++++++++++++++
Makefile | 1 +
builtin-bundle.c | 389 ++++++++++++++++++++++++++++++++++++++++++
builtin.h | 1 +
git-fetch.sh | 7 +
git-ls-remote.sh | 7 +-
git.c | 1 +
index-pack.c | 4 +-
t/t5510-fetch.sh | 28 +++-
11 files changed, 575 insertions(+), 4 deletions(-)
create mode 100644 Documentation/git-bundle.txt
create mode 100644 builtin-bundle.c
diff --git a/.gitignore b/.gitignore
index f15155d..4d3c66d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ git-archive
git-bisect
git-blame
git-branch
+git-bundle
git-cat-file
git-check-ref-format
git-checkout
diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
index a2d6268..f61c77a 100755
--- a/Documentation/cmd-list.perl
+++ b/Documentation/cmd-list.perl
@@ -70,6 +70,7 @@ git-archive mainporcelain
git-bisect mainporcelain
git-blame ancillaryinterrogators
git-branch mainporcelain
+git-bundle mainporcelain
git-cat-file plumbinginterrogators
git-checkout-index plumbingmanipulators
git-checkout mainporcelain
diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt
new file mode 100644
index 0000000..4ea9e85
--- /dev/null
+++ b/Documentation/git-bundle.txt
@@ -0,0 +1,139 @@
+git-bundle(1)
+=============
+
+NAME
+----
+git-bundle - Move objects and refs by archive
+
+
+SYNOPSIS
+--------
+'git-bundle' create <file> [git-rev-list args]
+'git-bundle' verify <file>
+'git-bundle' list-heads <file> [refname...]
+'git-bundle' unbundle <file> [refname...]
+
+DESCRIPTION
+-----------
+
+Some workflows require that one or more branches of development on one
+machine be replicated on another machine, but the two machines cannot
+be directly connected so the interactive git protocols (git, ssh,
+rsync, http) cannot be used. This command provides suport for
+git-fetch and git-pull to operate by packaging objects and references
+in an archive at the originating machine, then importing those into
+another repository using gitlink:git-fetch[1] and gitlink:git-pull[1]
+after moving the archive by some means (i.e., by sneakernet). As no
+direct connection between repositories exists, the user must specify a
+basis for the bundle that is held by the destination repository: the
+bundle assumes that all objects in the basis are already in the
+destination repository.
+
+OPTIONS
+-------
+
+create <file>::
+ Used to create a bundle named 'file'. This requires the
+ git-rev-list arguments to define the bundle contents.
+
+verify <file>::
+ Used to check that a bundle file is valid and will apply
+ cleanly to the current repository. This includes checks on the
+ bundle format itself as well as checking that the prerequisite
+ commits exist and are fully linked in the current repository.
+ git-bundle prints a list of missing commits, if any, and exits
+ with non-zero status.
+
+list-heads <file>::
+ Lists the references defined in the bundle. If followed by a
+ list of references, only references matching those given are
+ printed out.
+
+unbundle <file>::
+ Passes the objects in the bundle to gitlink:git-index-pack[1]
+ for storage in the repository, then prints the names of all
+ defined references. If a reflist is given, only references
+ matching those in the given list are printed. This command is
+ really plumbing, intended to be called only by
+ gitlink:git-fetch[1].
+
+[git-rev-list-args...]::
+ A list of arguments, accepatble to git-rev-parse and
+ git-rev-list, that specify the specific objects and references
+ to transport. For example, "master~10..master" causes the
+ current master reference to be packaged along with all objects
+ added since its 10th ancestor commit. There is no explicit
+ limit to the number of references and objects that may be
+ packaged.
+
+
+[refname...]::
+ A list of references used to limit the references reported as
+ available. This is principally of use to git-fetch, which
+ expects to recieve only those references asked for and not
+ necessarily everything in the pack (in this case, git-bundle is
+ acting like gitlink:git-fetch-pack[1]).
+
+SPECIFYING REFERENCES
+---------------------
+
+git-bundle will only package references that are shown by
+git-show-ref: this includes heads, tags, and remote heads. References
+such as master~1 cannot be packaged, but are perfectly suitable for
+defining the basis. More than one reference may be packaged, and more
+than one basis can be specified. The objects packaged are those not
+contained in the union of the given bases. Each basis can be
+specified explicitly (e.g., ^master~10), or implicitly (e.g.,
+master~10..master, master --since=10.days.ago).
+
+It is very important that the basis used be held by the destination.
+It is ok to err on the side of conservatism, causing the bundle file
+to contain objects already in the destination as these are ignored
+when unpacking at the destination.
+
+EXAMPLE
+-------
+
+Assume two repositories exist as R1 on machine A, and R2 on machine B.
+For whatever reason, direct connection between A and B is not allowed,
+but we can move data from A to B via some mechanism (CD, email, etc).
+We want to update R2 with developments made on branch master in R1.
+We set a tag in R1 (lastR2bundle) after the previous such transport,
+and move it afterwards to help build the bundle.
+
+in R1 on A:
+$ git-bundle create mybundle master ^lastR2bundle
+$ git tag -f lastR2bundle master
+
+(move mybundle from A to B by some mechanism)
+
+in R2 on B:
+$ git-bundle verify mybundle
+$ git-fetch mybundle refspec
+
+where refspec is refInBundle:localRef
+
+
+Also, with something like this in your config:
+
+[remote "bundle"]
+ url = /home/me/tmp/file.bdl
+ fetch = refs/heads/*:refs/remotes/origin/*
+
+You can first sneakernet the bundle file to ~/tmp/file.bdl and
+then these commands:
+
+$ git ls-remote bundle
+$ git fetch bundle
+$ git pull bundle
+
+would treat it as if it is talking with a remote side over the
+network.
+
+Author
+------
+Written by Mark Levedahl <mdl123@verizon.net>
+
+GIT
+---
+Part of the gitlink:git[7] suite
diff --git a/Makefile b/Makefile
index 56bb0b8..03a7f4a 100644
--- a/Makefile
+++ b/Makefile
@@ -276,6 +276,7 @@ BUILTIN_OBJS = \
builtin-archive.o \
builtin-blame.o \
builtin-branch.o \
+ builtin-bundle.o \
builtin-cat-file.o \
builtin-checkout-index.o \
builtin-check-ref-format.o \
diff --git a/builtin-bundle.c b/builtin-bundle.c
new file mode 100644
index 0000000..4bd596a
--- /dev/null
+++ b/builtin-bundle.c
@@ -0,0 +1,389 @@
+#include "cache.h"
+#include "object.h"
+#include "commit.h"
+#include "diff.h"
+#include "revision.h"
+#include "list-objects.h"
+#include "exec_cmd.h"
+
+/*
+ * Basic handler for bundle files to connect repositories via sneakernet.
+ * Invocation must include action.
+ * This function can create a bundle or provide information on an existing
+ * bundle supporting git-fetch, git-pull, and git-ls-remote
+ */
+
+static const char *bundle_usage="git-bundle (--create <bundle> <git-rev-list args> | --verify <bundle> | --list-heads <bundle> [refname]... | --unbundle <bundle> [refname]... )";
+
+static const char bundle_signature[] = "# v2 git bundle\n";
+
+struct ref_list {
+ unsigned int nr, alloc;
+ struct {
+ unsigned char sha1[20];
+ char *name;
+ } *list;
+};
+
+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);
+ list->list[list->nr].name = xstrdup(name);
+ list->nr++;
+}
+
+struct bundle_header {
+ struct ref_list prerequisites, 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++) {
+ int count = read(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);
+
+ if (fd < 0)
+ return error("could not open '%s'", path);
+ if (read_string(fd, buffer, sizeof(buffer)) < 0 ||
+ strcmp(buffer, bundle_signature)) {
+ close(fd);
+ return error("'%s' does not look like a v2 bundle file", path);
+ }
+ while (read_string(fd, buffer, sizeof(buffer)) > 0
+ && buffer[0] != '\n') {
+ int offset = buffer[0] == '-' ? 1 : 0;
+ int len = strlen(buffer);
+ unsigned char sha1[20];
+ struct ref_list *list = offset ? &header->prerequisites
+ : &header->references;
+ if (get_sha1_hex(buffer + offset, sha1)) {
+ close(fd);
+ return error("invalid SHA1: %s", buffer);
+ }
+ if (buffer[len - 1] == '\n')
+ buffer[len - 1] = '\0';
+ add_to_ref_list(sha1, buffer + 41 + offset, list);
+ }
+ return fd;
+}
+
+/* if in && *in >= 0, take that as input file descriptor instead */
+static int fork_with_pipe(const char **argv, int *in, int *out)
+{
+ int needs_in, needs_out;
+ int fdin[2], fdout[2], pid;
+
+ needs_in = in && *in < 0;
+ if (needs_in) {
+ if (pipe(fdin) < 0)
+ return error("could not setup pipe");
+ *in = fdin[1];
+ }
+
+ needs_out = out && *out < 0;
+ if (needs_out) {
+ if (pipe(fdout) < 0)
+ return error("could not setup pipe");
+ *out = fdout[0];
+ }
+
+ if ((pid = fork()) < 0) {
+ if (needs_in) {
+ close(fdin[0]);
+ close(fdin[1]);
+ }
+ if (needs_out) {
+ close(fdout[0]);
+ close(fdout[1]);
+ }
+ return error("could not fork");
+ }
+ if (!pid) {
+ if (needs_in) {
+ dup2(fdin[0], 0);
+ close(fdin[0]);
+ close(fdin[1]);
+ } else if (in)
+ dup2(*in, 0);
+ if (needs_out) {
+ dup2(fdout[1], 1);
+ close(fdout[0]);
+ close(fdout[1]);
+ } else if (out)
+ dup2(*out, 1);
+ exit(execv_git_cmd(argv));
+ }
+ if (needs_in)
+ close(fdin[0]);
+ if (needs_out)
+ close(fdout[1]);
+ return pid;
+}
+
+static int verify_bundle(struct bundle_header *header)
+{
+ /*
+ * Do fast check, then if any prereqs are missing then go line by line
+ * to be verbose about the errors
+ */
+ struct ref_list *p = &header->prerequisites;
+ const char *argv[5] = {"rev-list", "--stdin", "--not", "--all", NULL};
+ int pid, in, out, i, ret = 0;
+ char buffer[1024];
+
+ in = out = -1;
+ pid = fork_with_pipe(argv, &in, &out);
+ if (pid < 0)
+ return error("Could not fork rev-list");
+ if (!fork()) {
+ for (i = 0; i < p->nr; i++) {
+ write(in, sha1_to_hex(p->list[i].sha1), 40);
+ write(in, "\n", 1);
+ }
+ close(in);
+ exit(0);
+ }
+ close(in);
+ while (read_string(out, buffer, sizeof(buffer)) > 0) {
+ if (ret++ == 0)
+ error ("The bundle requires the following commits you lack:");
+ fprintf(stderr, "%s", buffer);
+ }
+ close(out);
+ while (waitpid(pid, &i, 0) < 0)
+ if (errno != EINTR)
+ return -1;
+ if (!ret && (!WIFEXITED(i) || WEXITSTATUS(i)))
+ return error("At least one prerequisite is lacking.");
+
+ return ret;
+}
+
+static int list_heads(struct bundle_header *header, int argc, const char **argv)
+{
+ int i;
+ 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(1, sha1_to_hex(commit->object.sha1), 40);
+ write(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(1, sha1_to_hex(p->item->sha1), 40);
+ write(1, " ", 1);
+ if (len)
+ write(1, p->name, len);
+ write(1, "\n", 1);
+}
+
+static int create_bundle(struct bundle_header *header, const char *path,
+ int argc, const char **argv)
+{
+ int bundle_fd = -1;
+ const char **argv_boundary = xmalloc((argc + 3) * sizeof(const char *));
+ const char **argv_pack = xmalloc(4 * sizeof(const char *));
+ int pid, in, out, i, status;
+ char buffer[1024];
+ struct rev_info revs;
+
+ bundle_fd = (!strcmp(path, "-") ? 1 :
+ open(path, O_CREAT | O_WRONLY, 0666));
+ if (bundle_fd < 0)
+ return error("Could not write to '%s'", path);
+
+ /* write signature */
+ write(bundle_fd, bundle_signature, strlen(bundle_signature));
+
+ /* write prerequisites */
+ memcpy(argv_boundary + 2, argv + 1, argc * sizeof(const char *));
+ argv_boundary[0] = "rev-list";
+ argv_boundary[1] = "--boundary";
+ argv_boundary[argc + 1] = NULL;
+ out = -1;
+ pid = fork_with_pipe(argv_boundary, NULL, &out);
+ if (pid < 0)
+ return -1;
+ while ((i = read_string(out, buffer, sizeof(buffer))) > 0)
+ if (buffer[0] == '-')
+ write(bundle_fd, buffer, i);
+ while ((i = waitpid(pid, &status, 0)) < 0)
+ if (errno != EINTR)
+ return error("rev-list died");
+ if (!WIFEXITED(status) || WEXITSTATUS(status))
+ return error("rev-list died %d", WEXITSTATUS(status));
+
+ /* 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);
+ if (argc > 1)
+ return error("unrecognized argument: %s'", argv[1]);
+ for (i = 0; i < revs.pending.nr; i++) {
+ struct object_array_entry *e = revs.pending.objects + i;
+ if (!(e->item->flags & UNINTERESTING)) {
+ unsigned char sha1[20];
+ char *ref;
+ if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
+ continue;
+ write(bundle_fd, sha1_to_hex(e->item->sha1), 40);
+ write(bundle_fd, " ", 1);
+ write(bundle_fd, ref, strlen(ref));
+ write(bundle_fd, "\n", 1);
+ free(ref);
+ }
+ }
+
+ /* end header */
+ write(bundle_fd, "\n", 1);
+
+ /* write pack */
+ argv_pack[0] = "pack-objects";
+ argv_pack[1] = "--all-progress";
+ argv_pack[2] = "--stdout";
+ argv_pack[3] = NULL;
+ in = -1;
+ out = bundle_fd;
+ pid = fork_with_pipe(argv_pack, &in, &out);
+ if (pid < 0)
+ return error("Could not spawn pack-objects");
+ close(1);
+ close(bundle_fd);
+ dup2(in, 1);
+ close(in);
+ prepare_revision_walk(&revs);
+ traverse_commit_list(&revs, show_commit, show_object);
+ close(1);
+ while (waitpid(pid, &status, 0) < 0)
+ if (errno != EINTR)
+ return -1;
+ if (!WIFEXITED(status) || WEXITSTATUS(status))
+ return error ("pack-objects died");
+ return 0;
+}
+
+static int unbundle(struct bundle_header *header, int bundle_fd,
+ int argc, const char **argv)
+{
+ const char *argv_index_pack[] = {"index-pack", "--stdin", NULL};
+ int pid, status, dev_null;
+
+ if (verify_bundle(header))
+ return -1;
+ dev_null = open("/dev/null", O_WRONLY);
+ pid = fork_with_pipe(argv_index_pack, &bundle_fd, &dev_null);
+ if (pid < 0)
+ return error("Could not spawn index-pack");
+ close(bundle_fd);
+ while (waitpid(pid, &status, 0) < 0)
+ if (errno != EINTR)
+ return error("index-pack died");
+ if (!WIFEXITED(status) || WEXITSTATUS(status))
+ return error("index-pack exited with status %d",
+ WEXITSTATUS(status));
+ return list_heads(header, argc, argv);
+}
+
+int cmd_bundle(int argc, const char **argv, const char *prefix)
+{
+ struct bundle_header header;
+ int nongit = 0;
+ const char *cmd, *bundle_file;
+ int bundle_fd = -1;
+ char buffer[PATH_MAX];
+
+ if (argc < 3)
+ usage(bundle_usage);
+
+ cmd = argv[1];
+ bundle_file = argv[2];
+ argc -= 2;
+ argv += 2;
+
+ prefix = setup_git_directory_gently(&nongit);
+ if (prefix && bundle_file[0] != '/') {
+ snprintf(buffer, sizeof(buffer), "%s/%s", prefix, bundle_file);
+ bundle_file = buffer;
+ }
+
+ memset(&header, 0, sizeof(header));
+ if (strcmp(cmd, "create") &&
+ !(bundle_fd = read_header(bundle_file, &header)))
+ return 1;
+
+ if (!strcmp(cmd, "verify")) {
+ close(bundle_fd);
+ if (verify_bundle(&header))
+ return 1;
+ fprintf(stderr, "%s is okay\n", bundle_file);
+ return 0;
+ }
+ if (!strcmp(cmd, "list-heads")) {
+ close(bundle_fd);
+ return !!list_heads(&header, argc, argv);
+ }
+ if (!strcmp(cmd, "create")) {
+ if (nongit)
+ die("Need a repository to create a bundle.");
+ return !!create_bundle(&header, bundle_file, argc, argv);
+ } else if (!strcmp(cmd, "unbundle")) {
+ if (nongit)
+ die("Need a repository to unbundle.");
+ return !!unbundle(&header, bundle_fd, argc, argv);
+ } else
+ usage(bundle_usage);
+}
+
diff --git a/builtin.h b/builtin.h
index 57e8741..528074b 100644
--- a/builtin.h
+++ b/builtin.h
@@ -19,6 +19,7 @@ extern int cmd_apply(int argc, const char **argv, const char *prefix);
extern int cmd_archive(int argc, const char **argv, const char *prefix);
extern int cmd_blame(int argc, const char **argv, const char *prefix);
extern int cmd_branch(int argc, const char **argv, const char *prefix);
+extern int cmd_bundle(int argc, const char **argv, const char *prefix);
extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
diff --git a/git-fetch.sh b/git-fetch.sh
index 5d3fec0..5ae0d28 100755
--- a/git-fetch.sh
+++ b/git-fetch.sh
@@ -388,9 +388,16 @@ fetch_main () {
( : subshell because we muck with IFS
IFS=" $LF"
(
+ if test -f "$remote" ; then
+ test -n "$shallow_depth" &&
+ die "shallow clone with bundle is not supported"
+ git-bundle unbundle "$remote" $rref ||
+ echo failed "$remote"
+ else
git-fetch-pack --thin $exec $keep $shallow_depth $no_progress \
"$remote" $rref ||
echo failed "$remote"
+ fi
) |
(
trap '
diff --git a/git-ls-remote.sh b/git-ls-remote.sh
index 8ea5c5e..a6ed99a 100755
--- a/git-ls-remote.sh
+++ b/git-ls-remote.sh
@@ -89,8 +89,13 @@ rsync://* )
;;
* )
- git-peek-remote $exec "$peek_repo" ||
+ if test -f "$peek_repo" ; then
+ git bundle list-heads "$peek_repo" ||
echo "failed slurping"
+ else
+ git-peek-remote $exec "$peek_repo" ||
+ echo "failed slurping"
+ fi
;;
esac |
sort -t ' ' -k 2 |
diff --git a/git.c b/git.c
index 83f3d90..a184d47 100644
--- a/git.c
+++ b/git.c
@@ -229,6 +229,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "archive", cmd_archive },
{ "blame", cmd_blame, RUN_SETUP },
{ "branch", cmd_branch, RUN_SETUP },
+ { "bundle", cmd_bundle },
{ "cat-file", cmd_cat_file, RUN_SETUP },
{ "checkout-index", cmd_checkout_index, RUN_SETUP },
{ "check-ref-format", cmd_check_ref_format },
diff --git a/index-pack.c b/index-pack.c
index fa9a0e7..5ccf4c4 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -457,8 +457,8 @@ static void parse_pack_objects(unsigned char *sha1)
/* If input_fd is a file, we should have reached its end now. */
if (fstat(input_fd, &st))
die("cannot fstat packfile: %s", strerror(errno));
- if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
- die("pack has junk at the end");
+ if (input_fd && S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
+ die("pack has junk at the end: 0%o, %d, %d %d", st.st_mode, (int)st.st_size, (int)consumed_bytes, input_fd);
if (!nr_deltas)
return;
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index 50c6485..fa76662 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -35,7 +35,9 @@ test_expect_success "clone and setup child repos" '
echo "URL: ../two/.git/"
echo "Pull: refs/heads/master:refs/heads/two"
echo "Pull: refs/heads/one:refs/heads/one"
- } >.git/remotes/two
+ } >.git/remotes/two &&
+ cd .. &&
+ git clone . bundle
'
test_expect_success "fetch test" '
@@ -81,4 +83,28 @@ test_expect_success 'fetch following tags' '
'
+test_expect_success 'create bundle 1' '
+ cd "$D" &&
+ echo >file updated again by origin &&
+ git commit -a -m "tip" &&
+ git bundle create bundle1 master^..master
+'
+
+test_expect_success 'create bundle 2' '
+ cd "$D" &&
+ git bundle create bundle2 master~2..master
+'
+
+test_expect_failure 'unbundle 1' '
+ cd "$D/bundle" &&
+ git checkout -b some-branch &&
+ git fetch "$D/bundle1" master:master
+'
+
+test_expect_success 'unbundle 2' '
+ cd "$D/bundle" &&
+ git fetch ../bundle2 master:master &&
+ test "tip" = "$(git log -1 --pretty=oneline master | cut -b42-)"
+'
+
test_done
--
1.5.0.1.616.gc6c4-dirty
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 0:59 ` [PATCH] Add git-bundle: move objects and references by archive Johannes Schindelin
@ 2007-02-22 3:28 ` Nicolas Pitre
2007-02-22 15:55 ` Johannes Schindelin
2007-02-22 6:56 ` Junio C Hamano
` (2 subsequent siblings)
3 siblings, 1 reply; 28+ messages in thread
From: Nicolas Pitre @ 2007-02-22 3:28 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git, Mark Levedahl, Junio C Hamano
On Thu, 22 Feb 2007, Johannes Schindelin wrote:
> diff --git a/index-pack.c b/index-pack.c
> index fa9a0e7..5ccf4c4 100644
> --- a/index-pack.c
> +++ b/index-pack.c
> @@ -457,8 +457,8 @@ static void parse_pack_objects(unsigned char *sha1)
> /* If input_fd is a file, we should have reached its end now. */
> if (fstat(input_fd, &st))
> die("cannot fstat packfile: %s", strerror(errno));
> - if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> - die("pack has junk at the end");
> + if (input_fd && S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> + die("pack has junk at the end: 0%o, %d, %d %d", st.st_mode, (int)st.st_size, (int)consumed_bytes, input_fd);
>
> if (!nr_deltas)
> return;
What is this supposed to mean?
Nicolas
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 0:59 ` [PATCH] Add git-bundle: move objects and references by archive Johannes Schindelin
2007-02-22 3:28 ` Nicolas Pitre
@ 2007-02-22 6:56 ` Junio C Hamano
2007-02-22 7:08 ` Junio C Hamano
` (2 more replies)
2007-02-22 9:31 ` Johannes Sixt
2007-02-22 18:14 ` [PATCH] git-bundle: assorted fixes Johannes Schindelin
3 siblings, 3 replies; 28+ messages in thread
From: Junio C Hamano @ 2007-02-22 6:56 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git, Mark Levedahl, junkio
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> It was updated to make git-bundle a builtin, and get rid of the tar
> format: now, the first line is supposed to say "# v2 git bundle", the next
> lines either contain a prerequisite ("-" followed by the hash of the
> needed commit), or a ref (the hash of a commit, followed by the name of
> the ref), and finally the pack. As a result, the bundle argument can be
> "-" now.
That in BNF would be?
bundle = signature prereq* ref* pack
signature = "# v2 git bundle\n"
prereq = "-" sha1 " " "\n"
ref = sha1 " " name "\n"
sha1 = [0-9a-f]{40}
name = <name of ref>
pack = <output stream from "pack-objects --stdout">
I suspect that we might want to possibly:
- have checksum protection over the part before pack data?
- give descriptive name to prereq, so that an error message can
say "you need v1.4.4" instead of "you need e267c2f6"?
- make the fields extensible without updating "v2"?
> diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
> index a2d6268..f61c77a 100755
> --- a/Documentation/cmd-list.perl
> +++ b/Documentation/cmd-list.perl
> @@ -70,6 +70,7 @@ git-archive mainporcelain
> git-bisect mainporcelain
> git-blame ancillaryinterrogators
> git-branch mainporcelain
> +git-bundle mainporcelain
> git-cat-file plumbinginterrogators
> git-checkout-index plumbingmanipulators
> git-checkout mainporcelain
Is this really a mainporcelain?
I would say ancillarymanipulators (or perhaps synchelpers).
> diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt
> new file mode 100644
> index 0000000..4ea9e85
> ...
> +Author
> +------
> +Written by Mark Levedahl <mdl123@verizon.net>
... and Dscho.
> diff --git a/builtin-bundle.c b/builtin-bundle.c
> new file mode 100644
> index 0000000..4bd596a
> --- /dev/null
> +++ b/builtin-bundle.c
> ...
> +static const char *bundle_usage="git-bundle (--create <bundle> <git-rev-list args> | --verify <bundle> | --list-heads <bundle> [refname]... | --unbundle <bundle> [refname]... )";
I thought you removed "--" prefixes from subcommands.
> +struct bundle_header {
> + struct ref_list prerequisites, references;
> +};
(minor style) I find these two members each on its own line
easier to read, as in:
struct bundle_header {
struct ref_list prerequisites;
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++) {
> + int count = read(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;
> +}
At least xread() please. I know the reason you read one byte at
a time is because you cannot over-read into pack area, but
somehow this makes me go "hmmmmmm". It's not performance
critical so let's leave it that way. Is bundle supposed to be
streamable? Otherwise we could probably seek back.
> +/* returns an fd */
> +static int read_header(const char *path, struct bundle_header *header) {
> + char buffer[1024];
> + int fd = open(path, O_RDONLY);
> +
> + if (fd < 0)
> + return error("could not open '%s'", path);
> + if (read_string(fd, buffer, sizeof(buffer)) < 0 ||
> + strcmp(buffer, bundle_signature)) {
> + close(fd);
> + return error("'%s' does not look like a v2 bundle file", path);
> + }
> + while (read_string(fd, buffer, sizeof(buffer)) > 0
> + && buffer[0] != '\n') {
> + int offset = buffer[0] == '-' ? 1 : 0;
> + int len = strlen(buffer);
> + unsigned char sha1[20];
> + struct ref_list *list = offset ? &header->prerequisites
> + : &header->references;
> + if (get_sha1_hex(buffer + offset, sha1)) {
> + close(fd);
> + return error("invalid SHA1: %s", buffer);
> + }
> + if (buffer[len - 1] == '\n')
> + buffer[len - 1] = '\0';
> + add_to_ref_list(sha1, buffer + 41 + offset, list);
> + }
> + return fd;
> +}
Don't you want to look at and validate buffer[40+offset] in any
way, other than what get_sha1_hex() does (which is issspace())?
> +/* if in && *in >= 0, take that as input file descriptor instead */
> +static int fork_with_pipe(const char **argv, int *in, int *out)
> +{
> + int needs_in, needs_out;
> + int fdin[2], fdout[2], pid;
> +
> + needs_in = in && *in < 0;
> + if (needs_in) {
> + if (pipe(fdin) < 0)
> + return error("could not setup pipe");
> + *in = fdin[1];
> + }
> +
> + needs_out = out && *out < 0;
> + if (needs_out) {
> + if (pipe(fdout) < 0)
> + return error("could not setup pipe");
> + *out = fdout[0];
> + }
> +
> + if ((pid = fork()) < 0) {
> + if (needs_in) {
> + close(fdin[0]);
> + close(fdin[1]);
> + }
> + if (needs_out) {
> + close(fdout[0]);
> + close(fdout[1]);
> + }
> + return error("could not fork");
> + }
> + if (!pid) {
> + if (needs_in) {
> + dup2(fdin[0], 0);
> + close(fdin[0]);
> + close(fdin[1]);
> + } else if (in)
> + dup2(*in, 0);
> + if (needs_out) {
> + dup2(fdout[1], 1);
> + close(fdout[0]);
> + close(fdout[1]);
> + } else if (out)
> + dup2(*out, 1);
> + exit(execv_git_cmd(argv));
> + }
Do you need to close *in and *out that are given by the caller
after dup2() in the child?
> +static int verify_bundle(struct bundle_header *header)
> +{
> + /*
> + * Do fast check, then if any prereqs are missing then go line by line
> + * to be verbose about the errors
> + */
> + struct ref_list *p = &header->prerequisites;
> + const char *argv[5] = {"rev-list", "--stdin", "--not", "--all", NULL};
> + int pid, in, out, i, ret = 0;
> + char buffer[1024];
> +
> + in = out = -1;
> + pid = fork_with_pipe(argv, &in, &out);
> + if (pid < 0)
> + return error("Could not fork rev-list");
> + if (!fork()) {
> + for (i = 0; i < p->nr; i++) {
> + write(in, sha1_to_hex(p->list[i].sha1), 40);
> + write(in, "\n", 1);
> + }
> + close(in);
> + exit(0);
> + }
> + close(in);
What if write() fails? That can happen when one of the objects
you feed here, or its parent objects, is missing from your
repository -- receiving rev-list would die() and the writing
child would sigpipe.
I also wonder who waits for this child...
> + while (read_string(out, buffer, sizeof(buffer)) > 0) {
> + if (ret++ == 0)
> + error ("The bundle requires the following commits you lack:");
> + fprintf(stderr, "%s", buffer);
> + }
> + close(out);
I do not think this error message would buy you anything. If
you say:
echo commit | rev-list --stdin --not --all
and you got commit back, that means you _do_ have that commit,
and that commit reaches some ref you have (objects associated to
that commit may be still missing). If commit is truly missing,
then you do not get commit output from rev-list as it cannot
even determine if it is "--not --all" or not -- it would error
out, which is the more important check to see if prereqs are
lacking.
> + while (waitpid(pid, &i, 0) < 0)
> + if (errno != EINTR)
> + return -1;
> + if (!ret && (!WIFEXITED(i) || WEXITSTATUS(i)))
> + return error("At least one prerequisite is lacking.");
So you would want to keep this error, perhaps even make it more
helpful by suggesting which ones are missing.
> +static int list_heads(struct bundle_header *header, int argc, const char **argv)
> +{
> + int i;
> + 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;
> +}
I wonder if we want to have a way to list prereqs in similar way.
> +static void show_commit(struct commit *commit)
> +{
> + write(1, sha1_to_hex(commit->object.sha1), 40);
> + write(1, "\n", 1);
> + if (commit->parents) {
> + free_commit_list(commit->parents);
> + commit->parents = NULL;
> + }
> +}
(everywhere) We would want write_in_full() with error handling
that dies even on EPIPE.
> +
> +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.
> + * */
An interesting way to wrap comments.
> + /* write prerequisites */
> + memcpy(argv_boundary + 2, argv + 1, argc * sizeof(const char *));
> + argv_boundary[0] = "rev-list";
> + argv_boundary[1] = "--boundary";
> + argv_boundary[argc + 1] = NULL;
> + out = -1;
> + pid = fork_with_pipe(argv_boundary, NULL, &out);
> + if (pid < 0)
> + return -1;
> + while ((i = read_string(out, buffer, sizeof(buffer))) > 0)
> + if (buffer[0] == '-')
> + write(bundle_fd, buffer, i);
It would be helpful for the recipient if you can append output
from git-describe (or name-rev) when the buffer lacks "name".
> +static int unbundle(struct bundle_header *header, int bundle_fd,
> + int argc, const char **argv)
> +{
> + const char *argv_index_pack[] = {"index-pack", "--stdin", NULL};
> + int pid, status, dev_null;
> +
> + if (verify_bundle(header))
> + return -1;
> + dev_null = open("/dev/null", O_WRONLY);
> + pid = fork_with_pipe(argv_index_pack, &bundle_fd, &dev_null);
> + if (pid < 0)
> + return error("Could not spawn index-pack");
> + close(bundle_fd);
> + while (waitpid(pid, &status, 0) < 0)
> + if (errno != EINTR)
> + return error("index-pack died");
> + if (!WIFEXITED(status) || WEXITSTATUS(status))
> + return error("index-pack exited with status %d",
> + WEXITSTATUS(status));
> + return list_heads(header, argc, argv);
> +}
We might want to later use --keep for this as well...
> diff --git a/index-pack.c b/index-pack.c
> index fa9a0e7..5ccf4c4 100644
> --- a/index-pack.c
> +++ b/index-pack.c
> @@ -457,8 +457,8 @@ static void parse_pack_objects(unsigned char *sha1)
> /* If input_fd is a file, we should have reached its end now. */
> if (fstat(input_fd, &st))
> die("cannot fstat packfile: %s", strerror(errno));
> - if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> - die("pack has junk at the end");
> + if (input_fd && S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> + die("pack has junk at the end: 0%o, %d, %d %d", st.st_mode, (int)st.st_size, (int)consumed_bytes, input_fd);
>
> if (!nr_deltas)
> return;
??
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 6:56 ` Junio C Hamano
@ 2007-02-22 7:08 ` Junio C Hamano
2007-02-22 16:20 ` Johannes Schindelin
2007-02-22 16:17 ` Johannes Schindelin
2007-02-23 2:32 ` Mark Levedahl
2 siblings, 1 reply; 28+ messages in thread
From: Junio C Hamano @ 2007-02-22 7:08 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git, Mark Levedahl, junkio
Junio C Hamano <junkio@cox.net> writes:
>> +static int verify_bundle(struct bundle_header *header)
>> +{
>> + /*
>> + * Do fast check, then if any prereqs are missing then go line by line
>> + * to be verbose about the errors
>> + */
>> + struct ref_list *p = &header->prerequisites;
>> + const char *argv[5] = {"rev-list", "--stdin", "--not", "--all", NULL};
>> + int pid, in, out, i, ret = 0;
>> + char buffer[1024];
>> +
>> + in = out = -1;
>> + pid = fork_with_pipe(argv, &in, &out);
>> + if (pid < 0)
>> + return error("Could not fork rev-list");
>> + if (!fork()) {
>> + for (i = 0; i < p->nr; i++) {
>> + write(in, sha1_to_hex(p->list[i].sha1), 40);
>> + write(in, "\n", 1);
>> + }
>> + close(in);
>> + exit(0);
>> + }
>> + close(in);
>
> What if write() fails? That can happen when one of the objects
> you feed here, or its parent objects, is missing from your
> repository -- receiving rev-list would die() and the writing
> child would sigpipe.
>
> I also wonder who waits for this child...
In general, fork() to avoid bidirectional pipe deadlock is a
good discipline, but in this particular case I think it would be
easier to handle errors if you don't (and it would save another
process). The other side "rev-list --stdin --not --all" is
running a limited traversal, and would not emit anything until
you stop feeding it from --stdin, or until it dies because you
fed it a commit that does not exist. So as long as you check
the error condition from write() for EPIPE to notice the other
end died, I think you are Ok.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 0:59 ` [PATCH] Add git-bundle: move objects and references by archive Johannes Schindelin
2007-02-22 3:28 ` Nicolas Pitre
2007-02-22 6:56 ` Junio C Hamano
@ 2007-02-22 9:31 ` Johannes Sixt
2007-02-22 18:14 ` [PATCH] git-bundle: assorted fixes Johannes Schindelin
3 siblings, 0 replies; 28+ messages in thread
From: Johannes Sixt @ 2007-02-22 9:31 UTC (permalink / raw)
To: git
Johannes Schindelin wrote:
> +/* if in && *in >= 0, take that as input file descriptor instead */
> +static int fork_with_pipe(const char **argv, int *in, int *out)
> +{
> + int needs_in, needs_out;
> + int fdin[2], fdout[2], pid;
> +
> + needs_in = in && *in < 0;
> + if (needs_in) {
> + if (pipe(fdin) < 0)
> + return error("could not setup pipe");
> + *in = fdin[1];
> + }
> +
> + needs_out = out && *out < 0;
> + if (needs_out) {
> + if (pipe(fdout) < 0)
> + return error("could not setup pipe");
> + *out = fdout[0];
> + }
> +
> + if ((pid = fork()) < 0) {
> + if (needs_in) {
> + close(fdin[0]);
> + close(fdin[1]);
> + }
> + if (needs_out) {
> + close(fdout[0]);
> + close(fdout[1]);
> + }
> + return error("could not fork");
> + }
> + if (!pid) {
> + if (needs_in) {
> + dup2(fdin[0], 0);
> + close(fdin[0]);
> + close(fdin[1]);
> + } else if (in)
> + dup2(*in, 0);
> + if (needs_out) {
> + dup2(fdout[1], 1);
> + close(fdout[0]);
> + close(fdout[1]);
> + } else if (out)
> + dup2(*out, 1);
> + exit(execv_git_cmd(argv));
> + }
> + if (needs_in)
> + close(fdin[0]);
> + if (needs_out)
> + close(fdout[1]);
> + return pid;
> +}
This function looks very similar to spawnvppe, which I use in the MinGW
port for a number of fork/exec pairs. Do you see a chance to make this
into a helper that can be used in more places (so that it reduces the
differences to the MinGW code)?
> + in = out = -1;
> + pid = fork_with_pipe(argv, &in, &out);
> + if (pid < 0)
> + return error("Could not fork rev-list");
> + if (!fork()) {
> + for (i = 0; i < p->nr; i++) {
> + write(in, sha1_to_hex(p->list[i].sha1), 40);
> + write(in, "\n", 1);
> + }
> + close(in);
> + exit(0);
> + }
> + close(in);
Oops, fork error check missing?
-- Hannes
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 3:28 ` Nicolas Pitre
@ 2007-02-22 15:55 ` Johannes Schindelin
2007-02-22 16:14 ` Simon 'corecode' Schubert
2007-02-22 16:24 ` Nicolas Pitre
0 siblings, 2 replies; 28+ messages in thread
From: Johannes Schindelin @ 2007-02-22 15:55 UTC (permalink / raw)
To: Nicolas Pitre; +Cc: git, Mark Levedahl, Junio C Hamano
Hi,
On Wed, 21 Feb 2007, Nicolas Pitre wrote:
> On Thu, 22 Feb 2007, Johannes Schindelin wrote:
>
> > diff --git a/index-pack.c b/index-pack.c
> > index fa9a0e7..5ccf4c4 100644
> > --- a/index-pack.c
> > +++ b/index-pack.c
> > @@ -457,8 +457,8 @@ static void parse_pack_objects(unsigned char *sha1)
> > /* If input_fd is a file, we should have reached its end now. */
> > if (fstat(input_fd, &st))
> > die("cannot fstat packfile: %s", strerror(errno));
> > - if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> > - die("pack has junk at the end");
> > + if (input_fd && S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> > + die("pack has junk at the end: 0%o, %d, %d %d", st.st_mode, (int)st.st_size, (int)consumed_bytes, input_fd);
> >
> > if (!nr_deltas)
> > return;
>
> What is this supposed to mean?
The funny thing is, if you stream part of the bundle file to index-pack,
S_ISREG(st.st_mode) is true, even if input_fd == 0.
Then, because it is only part of the bundle file, the size check fails.
Ciao,
Dscho
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 15:55 ` Johannes Schindelin
@ 2007-02-22 16:14 ` Simon 'corecode' Schubert
2007-02-22 16:28 ` Nicolas Pitre
2007-02-22 16:37 ` Johannes Schindelin
2007-02-22 16:24 ` Nicolas Pitre
1 sibling, 2 replies; 28+ messages in thread
From: Simon 'corecode' Schubert @ 2007-02-22 16:14 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Nicolas Pitre, git, Mark Levedahl, Junio C Hamano
[-- Attachment #1: Type: text/plain, Size: 1804 bytes --]
Johannes Schindelin wrote:
> Hi,
>
> On Wed, 21 Feb 2007, Nicolas Pitre wrote:
>
>> On Thu, 22 Feb 2007, Johannes Schindelin wrote:
>>
>>> diff --git a/index-pack.c b/index-pack.c
>>> index fa9a0e7..5ccf4c4 100644
>>> --- a/index-pack.c
>>> +++ b/index-pack.c
>>> @@ -457,8 +457,8 @@ static void parse_pack_objects(unsigned char *sha1)
>>> /* If input_fd is a file, we should have reached its end now. */
>>> if (fstat(input_fd, &st))
>>> die("cannot fstat packfile: %s", strerror(errno));
>>> - if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
>>> - die("pack has junk at the end");
>>> + if (input_fd && S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
>>> + die("pack has junk at the end: 0%o, %d, %d %d", st.st_mode, (int)st.st_size, (int)consumed_bytes, input_fd);
>>>
>>> if (!nr_deltas)
>>> return;
>> What is this supposed to mean?
>
> The funny thing is, if you stream part of the bundle file to index-pack,
> S_ISREG(st.st_mode) is true, even if input_fd == 0.
Well, of course: you opened a regular file and pass this as stdin to index-pack.
Maybe something like this would be cleaner:
if (IS_REF(st.st_mode) && lseek(input_fd, 0, SEEK_CUR) != st.st_size)
die("...");
Because the point isn't actually that we consumed less bytes than the file's size, but that there is still data available after we are done with handling the pack.
Anyways, is this a reason to die(), or rather just a remark?
cheers
simon
--
Serve - BSD +++ RENT this banner advert +++ ASCII Ribbon /"\
Work - Mac +++ space for low €€€ NOW!1 +++ Campaign \ /
Party Enjoy Relax | http://dragonflybsd.org Against HTML \
Dude 2c 2 the max ! http://golden-apple.biz Mail + News / \
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 252 bytes --]
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 6:56 ` Junio C Hamano
2007-02-22 7:08 ` Junio C Hamano
@ 2007-02-22 16:17 ` Johannes Schindelin
2007-02-23 2:32 ` Mark Levedahl
2 siblings, 0 replies; 28+ messages in thread
From: Johannes Schindelin @ 2007-02-22 16:17 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Mark Levedahl
Hi,
On Wed, 21 Feb 2007, Junio C Hamano wrote:
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>
> > It was updated to make git-bundle a builtin, and get rid of the tar
> > format: now, the first line is supposed to say "# v2 git bundle", the next
> > lines either contain a prerequisite ("-" followed by the hash of the
> > needed commit), or a ref (the hash of a commit, followed by the name of
> > the ref), and finally the pack. As a result, the bundle argument can be
> > "-" now.
>
> That in BNF would be?
>
> bundle = signature prereq* ref* pack
more like
bundle = header pack
header = signature prereq* ref* nl
> I suspect that we might want to possibly:
>
> - have checksum protection over the part before pack data?
Possibly. But would the sha1's not be sufficient? I.e. if the header is
corrupted, chances are that the commits are no longer verified correctly.
> - give descriptive name to prereq, so that an error message can
> say "you need v1.4.4" instead of "you need e267c2f6"?
My idea was to allow --since=<date>. You don't have a name there.
> - make the fields extensible without updating "v2"?
You mean, like, warn about unknown header lines? Yes, that's really easy.
> > diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
> > index a2d6268..f61c77a 100755
> > --- a/Documentation/cmd-list.perl
> > +++ b/Documentation/cmd-list.perl
> > @@ -70,6 +70,7 @@ git-archive mainporcelain
> > git-bisect mainporcelain
> > git-blame ancillaryinterrogators
> > git-branch mainporcelain
> > +git-bundle mainporcelain
> > git-cat-file plumbinginterrogators
> > git-checkout-index plumbingmanipulators
> > git-checkout mainporcelain
>
> Is this really a mainporcelain?
git bundle create <file>?
> > diff --git a/Documentation/git-bundle.txt b/Documentation/git-bundle.txt
> > new file mode 100644
> > index 0000000..4ea9e85
> > ...
> > +Author
> > +------
> > +Written by Mark Levedahl <mdl123@verizon.net>
>
> ... and Dscho.
... not the Documentation (only patched a little to reflect the "--"
change).
> > diff --git a/builtin-bundle.c b/builtin-bundle.c
> > new file mode 100644
> > index 0000000..4bd596a
> > --- /dev/null
> > +++ b/builtin-bundle.c
> > ...
> > +static const char *bundle_usage="git-bundle (--create <bundle> <git-rev-list args> | --verify <bundle> | --list-heads <bundle> [refname]... | --unbundle <bundle> [refname]... )";
>
> I thought you removed "--" prefixes from subcommands.
Yes, and I forgot it there. Will fix.
> > +struct bundle_header {
> > + struct ref_list prerequisites, references;
> > +};
>
> (minor style) I find these two members each on its own line
> easier to read, as in:
>
> struct bundle_header {
> struct ref_list prerequisites;
> struct ref_list references;
> };
Will fix.
> > +/* 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++) {
> > + int count = read(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;
> > +}
>
> At least xread() please. I know the reason you read one byte at
> a time is because you cannot over-read into pack area, but
> somehow this makes me go "hmmmmmm". It's not performance
> critical so let's leave it that way. Is bundle supposed to be
> streamable? Otherwise we could probably seek back.
I wanted unbundle to be able to read from stdin (I did not yet allow "git
fetch - master:master"). But I somehow forgot about xread(). D'oh. Will
fix.
> > +/* returns an fd */
> > +static int read_header(const char *path, struct bundle_header *header) {
> > + char buffer[1024];
> > + int fd = open(path, O_RDONLY);
> > +
> > + if (fd < 0)
> > + return error("could not open '%s'", path);
> > + if (read_string(fd, buffer, sizeof(buffer)) < 0 ||
> > + strcmp(buffer, bundle_signature)) {
> > + close(fd);
> > + return error("'%s' does not look like a v2 bundle file", path);
> > + }
> > + while (read_string(fd, buffer, sizeof(buffer)) > 0
> > + && buffer[0] != '\n') {
> > + int offset = buffer[0] == '-' ? 1 : 0;
> > + int len = strlen(buffer);
> > + unsigned char sha1[20];
> > + struct ref_list *list = offset ? &header->prerequisites
> > + : &header->references;
> > + if (get_sha1_hex(buffer + offset, sha1)) {
> > + close(fd);
> > + return error("invalid SHA1: %s", buffer);
> > + }
> > + if (buffer[len - 1] == '\n')
> > + buffer[len - 1] = '\0';
> > + add_to_ref_list(sha1, buffer + 41 + offset, list);
> > + }
> > + return fd;
> > +}
>
> Don't you want to look at and validate buffer[40+offset] in any
> way, other than what get_sha1_hex() does (which is issspace())?
Right. Will fix. (Although I will only write two "s" instead of three...)
> > +/* if in && *in >= 0, take that as input file descriptor instead */
> > +static int fork_with_pipe(const char **argv, int *in, int *out)
> > +{
> > + int needs_in, needs_out;
> > + int fdin[2], fdout[2], pid;
> > +
> > + needs_in = in && *in < 0;
> > + if (needs_in) {
> > + if (pipe(fdin) < 0)
> > + return error("could not setup pipe");
> > + *in = fdin[1];
> > + }
> > +
> > + needs_out = out && *out < 0;
> > + if (needs_out) {
> > + if (pipe(fdout) < 0)
> > + return error("could not setup pipe");
> > + *out = fdout[0];
> > + }
> > +
> > + if ((pid = fork()) < 0) {
> > + if (needs_in) {
> > + close(fdin[0]);
> > + close(fdin[1]);
> > + }
> > + if (needs_out) {
> > + close(fdout[0]);
> > + close(fdout[1]);
> > + }
> > + return error("could not fork");
> > + }
> > + if (!pid) {
> > + if (needs_in) {
> > + dup2(fdin[0], 0);
> > + close(fdin[0]);
> > + close(fdin[1]);
> > + } else if (in)
> > + dup2(*in, 0);
> > + if (needs_out) {
> > + dup2(fdout[1], 1);
> > + close(fdout[0]);
> > + close(fdout[1]);
> > + } else if (out)
> > + dup2(*out, 1);
> > + exit(execv_git_cmd(argv));
> > + }
>
> Do you need to close *in and *out that are given by the caller
> after dup2() in the child?
I probably should. Will fix.
> > +static int verify_bundle(struct bundle_header *header)
> > +{
> > + /*
> > + * Do fast check, then if any prereqs are missing then go line by line
> > + * to be verbose about the errors
> > + */
> > + struct ref_list *p = &header->prerequisites;
> > + const char *argv[5] = {"rev-list", "--stdin", "--not", "--all", NULL};
> > + int pid, in, out, i, ret = 0;
> > + char buffer[1024];
> > +
> > + in = out = -1;
> > + pid = fork_with_pipe(argv, &in, &out);
> > + if (pid < 0)
> > + return error("Could not fork rev-list");
> > + if (!fork()) {
> > + for (i = 0; i < p->nr; i++) {
> > + write(in, sha1_to_hex(p->list[i].sha1), 40);
> > + write(in, "\n", 1);
> > + }
> > + close(in);
> > + exit(0);
> > + }
> > + close(in);
>
> What if write() fails? That can happen when one of the objects
> you feed here, or its parent objects, is missing from your
> repository -- receiving rev-list would die() and the writing
> child would sigpipe.
The error is caught afterwards, with WEXITSTATUS(), no? It might be
cleaner to check, but is it really necessary?
> I also wonder who waits for this child...
rev-list. It only exits when that child closes the input.
> > + while (read_string(out, buffer, sizeof(buffer)) > 0) {
> > + if (ret++ == 0)
> > + error ("The bundle requires the following commits you lack:");
> > + fprintf(stderr, "%s", buffer);
> > + }
> > + close(out);
>
> I do not think this error message would buy you anything. If
> you say:
>
> echo commit | rev-list --stdin --not --all
>
> and you got commit back, that means you _do_ have that commit,
> and that commit reaches some ref you have (objects associated to
> that commit may be still missing). If commit is truly missing,
> then you do not get commit output from rev-list as it cannot
> even determine if it is "--not --all" or not -- it would error
> out, which is the more important check to see if prereqs are
> lacking.
Yes, I realized that rev-list die()s, and does not output the missing
revs -- as git-bundle.sh expected it to. When I found out about that, I
failed to remove that bogus error. Will fix.
> > + while (waitpid(pid, &i, 0) < 0)
> > + if (errno != EINTR)
> > + return -1;
> > + if (!ret && (!WIFEXITED(i) || WEXITSTATUS(i)))
> > + return error("At least one prerequisite is lacking.");
>
> So you would want to keep this error, perhaps even make it more
> helpful by suggesting which ones are missing.
rev-list die()s with the first missing. It does not continue, printing
other missing revs.
> > +static int list_heads(struct bundle_header *header, int argc, const char **argv)
> > +{
> > + int i;
> > + 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;
> > +}
>
> I wonder if we want to have a way to list prereqs in similar way.
That is what verify should be for. Maybe add "-v" to the "verify"
subcommand, make it properly builtin, and output non-missing prerequisites
only with "-v"?
> > +static void show_commit(struct commit *commit)
> > +{
> > + write(1, sha1_to_hex(commit->object.sha1), 40);
> > + write(1, "\n", 1);
> > + if (commit->parents) {
> > + free_commit_list(commit->parents);
> > + commit->parents = NULL;
> > + }
> > +}
>
> (everywhere) We would want write_in_full() with error handling
> that dies even on EPIPE.
Or write_or_die()? I mean, if write() fails, we cannot sanely continue
anyway, right?
> > +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.
> > + * */
>
> An interesting way to wrap comments.
Bad, bad vi. Will fix.
> > + /* write prerequisites */
> > + memcpy(argv_boundary + 2, argv + 1, argc * sizeof(const char *));
> > + argv_boundary[0] = "rev-list";
> > + argv_boundary[1] = "--boundary";
> > + argv_boundary[argc + 1] = NULL;
> > + out = -1;
> > + pid = fork_with_pipe(argv_boundary, NULL, &out);
> > + if (pid < 0)
> > + return -1;
> > + while ((i = read_string(out, buffer, sizeof(buffer))) > 0)
> > + if (buffer[0] == '-')
> > + write(bundle_fd, buffer, i);
>
> It would be helpful for the recipient if you can append output
> >from git-describe (or name-rev) when the buffer lacks "name".
Hmm. This could take a long time, as -describe is not really fast. ATM
name-rev is not fast either, but I have plans to make it so. So obiously,
I would prefer name-rev output. Comments?
> > +static int unbundle(struct bundle_header *header, int bundle_fd,
> > + int argc, const char **argv)
> > +{
> > + const char *argv_index_pack[] = {"index-pack", "--stdin", NULL};
> > + int pid, status, dev_null;
> > +
> > + if (verify_bundle(header))
> > + return -1;
> > + dev_null = open("/dev/null", O_WRONLY);
> > + pid = fork_with_pipe(argv_index_pack, &bundle_fd, &dev_null);
> > + if (pid < 0)
> > + return error("Could not spawn index-pack");
> > + close(bundle_fd);
> > + while (waitpid(pid, &status, 0) < 0)
> > + if (errno != EINTR)
> > + return error("index-pack died");
> > + if (!WIFEXITED(status) || WEXITSTATUS(status))
> > + return error("index-pack exited with status %d",
> > + WEXITSTATUS(status));
> > + return list_heads(header, argc, argv);
> > +}
>
> We might want to later use --keep for this as well...
Yes, later. It is such a low-hanging fruit that I'd prefer somebody else
to get involved with the code.
> > diff --git a/index-pack.c b/index-pack.c
> > index fa9a0e7..5ccf4c4 100644
> > --- a/index-pack.c
> > +++ b/index-pack.c
> > @@ -457,8 +457,8 @@ static void parse_pack_objects(unsigned char *sha1)
> > /* If input_fd is a file, we should have reached its end now. */
> > if (fstat(input_fd, &st))
> > die("cannot fstat packfile: %s", strerror(errno));
> > - if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> > - die("pack has junk at the end");
> > + if (input_fd && S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> > + die("pack has junk at the end: 0%o, %d, %d %d", st.st_mode, (int)st.st_size, (int)consumed_bytes, input_fd);
> >
> > if (!nr_deltas)
> > return;
>
> ??
As I said in my reply to Nico, if input_fd is 0, but really comes from a
file, this check will fail (the bundle is _strictly_ larger than the
pack).
Would you like the fixes on top of the big commit, or a replacement patch?
Ciao,
Dscho
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 7:08 ` Junio C Hamano
@ 2007-02-22 16:20 ` Johannes Schindelin
2007-02-22 19:10 ` Junio C Hamano
0 siblings, 1 reply; 28+ messages in thread
From: Johannes Schindelin @ 2007-02-22 16:20 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Mark Levedahl
Hi,
On Wed, 21 Feb 2007, Junio C Hamano wrote:
> Junio C Hamano <junkio@cox.net> writes:
>
> >> +static int verify_bundle(struct bundle_header *header)
> >> +{
> >> + /*
> >> + * Do fast check, then if any prereqs are missing then go line by line
> >> + * to be verbose about the errors
> >> + */
> >> + struct ref_list *p = &header->prerequisites;
> >> + const char *argv[5] = {"rev-list", "--stdin", "--not", "--all", NULL};
> >> + int pid, in, out, i, ret = 0;
> >> + char buffer[1024];
> >> +
> >> + in = out = -1;
> >> + pid = fork_with_pipe(argv, &in, &out);
> >> + if (pid < 0)
> >> + return error("Could not fork rev-list");
> >> + if (!fork()) {
> >> + for (i = 0; i < p->nr; i++) {
> >> + write(in, sha1_to_hex(p->list[i].sha1), 40);
> >> + write(in, "\n", 1);
> >> + }
> >> + close(in);
> >> + exit(0);
> >> + }
> >> + close(in);
> >
> > What if write() fails? That can happen when one of the objects
> > you feed here, or its parent objects, is missing from your
> > repository -- receiving rev-list would die() and the writing
> > child would sigpipe.
> >
> > I also wonder who waits for this child...
>
> In general, fork() to avoid bidirectional pipe deadlock is a
> good discipline, but in this particular case I think it would be
> easier to handle errors if you don't (and it would save another
> process). The other side "rev-list --stdin --not --all" is
> running a limited traversal, and would not emit anything until
> you stop feeding it from --stdin, or until it dies because you
> fed it a commit that does not exist. So as long as you check
> the error condition from write() for EPIPE to notice the other
> end died, I think you are Ok.
Thinking about this deeper, I have to say I find my decision to use
"--stdin" rather silly, given that I know the exact number of revisions,
and their SHA1s.
But it might make more sense to rewrite the checking part, instead of
fork()ing it.
Ciao,
Dscho
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 15:55 ` Johannes Schindelin
2007-02-22 16:14 ` Simon 'corecode' Schubert
@ 2007-02-22 16:24 ` Nicolas Pitre
2007-02-22 17:12 ` Johannes Schindelin
1 sibling, 1 reply; 28+ messages in thread
From: Nicolas Pitre @ 2007-02-22 16:24 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git, Mark Levedahl, Junio C Hamano
On Thu, 22 Feb 2007, Johannes Schindelin wrote:
> Hi,
>
> On Wed, 21 Feb 2007, Nicolas Pitre wrote:
>
> > On Thu, 22 Feb 2007, Johannes Schindelin wrote:
> >
> > > diff --git a/index-pack.c b/index-pack.c
> > > index fa9a0e7..5ccf4c4 100644
> > > --- a/index-pack.c
> > > +++ b/index-pack.c
> > > @@ -457,8 +457,8 @@ static void parse_pack_objects(unsigned char *sha1)
> > > /* If input_fd is a file, we should have reached its end now. */
> > > if (fstat(input_fd, &st))
> > > die("cannot fstat packfile: %s", strerror(errno));
> > > - if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> > > - die("pack has junk at the end");
> > > + if (input_fd && S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> > > + die("pack has junk at the end: 0%o, %d, %d %d", st.st_mode, (int)st.st_size, (int)consumed_bytes, input_fd);
> > >
> > > if (!nr_deltas)
> > > return;
> >
> > What is this supposed to mean?
>
> The funny thing is, if you stream part of the bundle file to index-pack,
> S_ISREG(st.st_mode) is true, even if input_fd == 0.
Hmmmm. indeed..
Could you please make the test, including the call to fstat(),
conditional on !from_stdin instead?
Also I don't see the point of displaying the mode since we know that
S_ISREG(st.st_mode) is true, and input_fd is of little interest as well.
Nicolas
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 16:14 ` Simon 'corecode' Schubert
@ 2007-02-22 16:28 ` Nicolas Pitre
2007-02-22 16:37 ` Johannes Schindelin
1 sibling, 0 replies; 28+ messages in thread
From: Nicolas Pitre @ 2007-02-22 16:28 UTC (permalink / raw)
To: Simon 'corecode' Schubert
Cc: Johannes Schindelin, git, Mark Levedahl, Junio C Hamano
On Thu, 22 Feb 2007, Simon 'corecode' Schubert wrote:
> Johannes Schindelin wrote:
> > Hi,
> >
> > On Wed, 21 Feb 2007, Nicolas Pitre wrote:
> >
> > > On Thu, 22 Feb 2007, Johannes Schindelin wrote:
> > >
> > > > diff --git a/index-pack.c b/index-pack.c
> > > > index fa9a0e7..5ccf4c4 100644
> > > > --- a/index-pack.c
> > > > +++ b/index-pack.c
> > > > @@ -457,8 +457,8 @@ static void parse_pack_objects(unsigned char *sha1)
> > > > /* If input_fd is a file, we should have reached its end now. */
> > > > if (fstat(input_fd, &st))
> > > > die("cannot fstat packfile: %s", strerror(errno));
> > > > - if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> > > > - die("pack has junk at the end");
> > > > + if (input_fd && S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> > > > + die("pack has junk at the end: 0%o, %d, %d %d", st.st_mode,
> > > > (int)st.st_size, (int)consumed_bytes, input_fd);
> > > > if (!nr_deltas)
> > > > return;
> > > What is this supposed to mean?
> >
> > The funny thing is, if you stream part of the bundle file to index-pack,
> > S_ISREG(st.st_mode) is true, even if input_fd == 0.
>
> Well, of course: you opened a regular file and pass this as stdin to
> index-pack.
>
> Maybe something like this would be cleaner:
>
> if (IS_REF(st.st_mode) && lseek(input_fd, 0, SEEK_CUR) != st.st_size)
No that won't work because the input is buffered in the fill() function.
Nicolas
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 16:14 ` Simon 'corecode' Schubert
2007-02-22 16:28 ` Nicolas Pitre
@ 2007-02-22 16:37 ` Johannes Schindelin
2007-02-22 16:46 ` Simon 'corecode' Schubert
1 sibling, 1 reply; 28+ messages in thread
From: Johannes Schindelin @ 2007-02-22 16:37 UTC (permalink / raw)
To: Simon 'corecode' Schubert
Cc: Nicolas Pitre, git, Mark Levedahl, Junio C Hamano
Hi,
On Thu, 22 Feb 2007, Simon 'corecode' Schubert wrote:
> Johannes Schindelin wrote:
> > Hi,
> >
> > On Wed, 21 Feb 2007, Nicolas Pitre wrote:
> >
> > > On Thu, 22 Feb 2007, Johannes Schindelin wrote:
> > >
> > > > diff --git a/index-pack.c b/index-pack.c
> > > > index fa9a0e7..5ccf4c4 100644
> > > > --- a/index-pack.c
> > > > +++ b/index-pack.c
> > > > @@ -457,8 +457,8 @@ static void parse_pack_objects(unsigned char *sha1)
> > > > /* If input_fd is a file, we should have reached its end now. */
> > > > if (fstat(input_fd, &st))
> > > > die("cannot fstat packfile: %s", strerror(errno));
> > > > - if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> > > > - die("pack has junk at the end");
> > > > + if (input_fd && S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> > > > + die("pack has junk at the end: 0%o, %d, %d %d", st.st_mode,
> > > > (int)st.st_size, (int)consumed_bytes, input_fd);
> > > > if (!nr_deltas)
> > > > return;
> > > What is this supposed to mean?
> >
> > The funny thing is, if you stream part of the bundle file to index-pack,
> > S_ISREG(st.st_mode) is true, even if input_fd == 0.
>
> Well, of course: you opened a regular file and pass this as stdin to
> index-pack.
>
> Maybe something like this would be cleaner:
>
> if (IS_REF(st.st_mode) && lseek(input_fd, 0, SEEK_CUR) != st.st_size)
> die("...");
The lseek would fail whenever the input is _not_ a file, dying. Since
index-pack is called from fetch-pack, with a socket instead of a file, it
would fail for the most common user.
My patch was only a minimal fixup to allow "--stdin" even starting in the
middle of a file, and it does not break fetching.
Ciao,
Dscho
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 16:37 ` Johannes Schindelin
@ 2007-02-22 16:46 ` Simon 'corecode' Schubert
2007-02-22 17:09 ` Johannes Schindelin
0 siblings, 1 reply; 28+ messages in thread
From: Simon 'corecode' Schubert @ 2007-02-22 16:46 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: Nicolas Pitre, git, Mark Levedahl, Junio C Hamano
[-- Attachment #1: Type: text/plain, Size: 2048 bytes --]
Johannes Schindelin wrote:
> Hi,
>
> On Thu, 22 Feb 2007, Simon 'corecode' Schubert wrote:
>
>> Johannes Schindelin wrote:
>>> Hi,
>>>
>>> On Wed, 21 Feb 2007, Nicolas Pitre wrote:
>>>
>>>> On Thu, 22 Feb 2007, Johannes Schindelin wrote:
>>>>
>>>>> diff --git a/index-pack.c b/index-pack.c
>>>>> index fa9a0e7..5ccf4c4 100644
>>>>> --- a/index-pack.c
>>>>> +++ b/index-pack.c
>>>>> @@ -457,8 +457,8 @@ static void parse_pack_objects(unsigned char *sha1)
>>>>> /* If input_fd is a file, we should have reached its end now. */
>>>>> if (fstat(input_fd, &st))
>>>>> die("cannot fstat packfile: %s", strerror(errno));
>>>>> - if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
>>>>> - die("pack has junk at the end");
>>>>> + if (input_fd && S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
>>>>> + die("pack has junk at the end: 0%o, %d, %d %d", st.st_mode,
>>>>> (int)st.st_size, (int)consumed_bytes, input_fd);
>>>>> if (!nr_deltas)
>>>>> return;
>>>> What is this supposed to mean?
>>> The funny thing is, if you stream part of the bundle file to index-pack,
>>> S_ISREG(st.st_mode) is true, even if input_fd == 0.
>> Well, of course: you opened a regular file and pass this as stdin to
>> index-pack.
>>
>> Maybe something like this would be cleaner:
>>
>> if (IS_REF(st.st_mode) && lseek(input_fd, 0, SEEK_CUR) != st.st_size)
>> die("...");
>
> The lseek would fail whenever the input is _not_ a file, dying. Since
> index-pack is called from fetch-pack, with a socket instead of a file, it
> would fail for the most common user.
that's why i put IS_REF (should read IS_REG) there.
but as nicolas pointed out, this won't work for read-ahead.
cheers
simon
--
Serve - BSD +++ RENT this banner advert +++ ASCII Ribbon /"\
Work - Mac +++ space for low €€€ NOW!1 +++ Campaign \ /
Party Enjoy Relax | http://dragonflybsd.org Against HTML \
Dude 2c 2 the max ! http://golden-apple.biz Mail + News / \
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 252 bytes --]
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 16:46 ` Simon 'corecode' Schubert
@ 2007-02-22 17:09 ` Johannes Schindelin
0 siblings, 0 replies; 28+ messages in thread
From: Johannes Schindelin @ 2007-02-22 17:09 UTC (permalink / raw)
To: Simon 'corecode' Schubert
Cc: Nicolas Pitre, git, Mark Levedahl, Junio C Hamano
Hi,
On Thu, 22 Feb 2007, Simon 'corecode' Schubert wrote:
> Johannes Schindelin wrote:
> >
> > On Thu, 22 Feb 2007, Simon 'corecode' Schubert wrote:
> >
> > > Maybe something like this would be cleaner:
> > >
> > > if (IS_REF(st.st_mode) && lseek(input_fd, 0, SEEK_CUR) != st.st_size)
> > > die("...");
> >
> > The lseek would fail whenever the input is _not_ a file, dying. Since
> > index-pack is called from fetch-pack, with a socket instead of a file, it
> > would fail for the most common user.
>
> that's why i put IS_REF (should read IS_REG) there.
Ah, yes, I understand.
> but as nicolas pointed out, this won't work for read-ahead.
Well, you could do this:
if (S_ISREG(st.st_mode) &&
lseek(input_fd, 0, SEEK_CUR) - input_len != st.st_size)
die("...");
Of course, this suggests that a file containing a pack must not have
_anything_ after the pack. But AFAIR the reading part chokes on "garbage
after pack" anyway.
Ciao,
Dscho
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 16:24 ` Nicolas Pitre
@ 2007-02-22 17:12 ` Johannes Schindelin
2007-02-22 17:21 ` Nicolas Pitre
0 siblings, 1 reply; 28+ messages in thread
From: Johannes Schindelin @ 2007-02-22 17:12 UTC (permalink / raw)
To: Nicolas Pitre; +Cc: git, Mark Levedahl, Junio C Hamano
Hi,
On Thu, 22 Feb 2007, Nicolas Pitre wrote:
> On Thu, 22 Feb 2007, Johannes Schindelin wrote:
>
> > Hi,
> >
> > On Wed, 21 Feb 2007, Nicolas Pitre wrote:
> >
> > > On Thu, 22 Feb 2007, Johannes Schindelin wrote:
> > >
> > > > diff --git a/index-pack.c b/index-pack.c
> > > > index fa9a0e7..5ccf4c4 100644
> > > > --- a/index-pack.c
> > > > +++ b/index-pack.c
> > > > @@ -457,8 +457,8 @@ static void parse_pack_objects(unsigned char *sha1)
> > > > /* If input_fd is a file, we should have reached its end now. */
> > > > if (fstat(input_fd, &st))
> > > > die("cannot fstat packfile: %s", strerror(errno));
> > > > - if (S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> > > > - die("pack has junk at the end");
> > > > + if (input_fd && S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
> > > > + die("pack has junk at the end: 0%o, %d, %d %d", st.st_mode, (int)st.st_size, (int)consumed_bytes, input_fd);
> > > >
> > > > if (!nr_deltas)
> > > > return;
> > >
> > > What is this supposed to mean?
> >
> > The funny thing is, if you stream part of the bundle file to index-pack,
> > S_ISREG(st.st_mode) is true, even if input_fd == 0.
>
> Hmmmm. indeed..
>
> Could you please make the test, including the call to fstat(),
> conditional on !from_stdin instead?
How about using Simon's idea instead (subtracting input_len)?
> Also I don't see the point of displaying the mode since we know that
> S_ISREG(st.st_mode) is true, and input_fd is of little interest as well.
As you probably guessed, these are debug remnants. Will fix.
Ciao,
Dscho
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 17:12 ` Johannes Schindelin
@ 2007-02-22 17:21 ` Nicolas Pitre
0 siblings, 0 replies; 28+ messages in thread
From: Nicolas Pitre @ 2007-02-22 17:21 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git, Mark Levedahl, Junio C Hamano
On Thu, 22 Feb 2007, Johannes Schindelin wrote:
> On Thu, 22 Feb 2007, Nicolas Pitre wrote:
>
> > Could you please make the test, including the call to fstat(),
> > conditional on !from_stdin instead?
>
> How about using Simon's idea instead (subtracting input_len)?
Yes it would work.
Nicolas
^ permalink raw reply [flat|nested] 28+ messages in thread
* [PATCH] git-bundle: assorted fixes
2007-02-22 0:59 ` [PATCH] Add git-bundle: move objects and references by archive Johannes Schindelin
` (2 preceding siblings ...)
2007-02-22 9:31 ` Johannes Sixt
@ 2007-02-22 18:14 ` Johannes Schindelin
2007-02-23 1:36 ` Mark Levedahl
3 siblings, 1 reply; 28+ messages in thread
From: Johannes Schindelin @ 2007-02-22 18:14 UTC (permalink / raw)
To: git; +Cc: Mark Levedahl, junkio, Nicolas Pitre,
Simon 'corecode' Schubert
This patch fixes issues mentioned by Junio, Nico and Simon:
- I forgot to convert the usage string when removing the "--" from
the subcommands,
- a style fix in the bundle_header,
- use xread() instead of read(),
- use write_or_die() instead of write(),
- make the bundle header extensible,
- fail if the whitespace after a sha1 of a reference is missing,
- close() the fds passed to a subprocess,
- in verify_bundle(), do not use "rev-list --stdin", but rather
pass the revs directly (avoiding a fork()),
- fix a corrupted comment in show_object(), and
- fix the size check in index_pack.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
builtin-bundle.c | 107 ++++++++++++++++++++++++++++++------------------------
index-pack.c | 5 ++-
2 files changed, 63 insertions(+), 49 deletions(-)
diff --git a/builtin-bundle.c b/builtin-bundle.c
index 4bd596a..521bbda 100644
--- a/builtin-bundle.c
+++ b/builtin-bundle.c
@@ -13,7 +13,7 @@
* bundle supporting git-fetch, git-pull, and git-ls-remote
*/
-static const char *bundle_usage="git-bundle (--create <bundle> <git-rev-list args> | --verify <bundle> | --list-heads <bundle> [refname]... | --unbundle <bundle> [refname]... )";
+static const char *bundle_usage="git-bundle (create <bundle> <git-rev-list args> | verify <bundle> | list-heads <bundle> [refname]... | unbundle <bundle> [refname]... )";
static const char bundle_signature[] = "# v2 git bundle\n";
@@ -39,7 +39,8 @@ static void add_to_ref_list(const unsigned char *sha1, const char *name,
}
struct bundle_header {
- struct ref_list prerequisites, references;
+ struct ref_list prerequisites;
+ struct ref_list references;
};
/* this function returns the length of the string */
@@ -47,7 +48,7 @@ static int read_string(int fd, char *buffer, int size)
{
int i;
for (i = 0; i < size - 1; i++) {
- int count = read(fd, buffer + i, 1);
+ int count = xread(fd, buffer + i, 1);
if (count < 0)
return error("Read error: %s", strerror(errno));
if (count == 0) {
@@ -75,18 +76,25 @@ static int read_header(const char *path, struct bundle_header *header) {
}
while (read_string(fd, buffer, sizeof(buffer)) > 0
&& buffer[0] != '\n') {
- int offset = buffer[0] == '-' ? 1 : 0;
+ int is_prereq = buffer[0] == '-';
+ int offset = is_prereq ? 1 : 0;
int len = strlen(buffer);
unsigned char sha1[20];
- struct ref_list *list = offset ? &header->prerequisites
+ struct ref_list *list = is_prereq ? &header->prerequisites
: &header->references;
- if (get_sha1_hex(buffer + offset, sha1)) {
- close(fd);
- return error("invalid SHA1: %s", buffer);
- }
+ char delim;
+
if (buffer[len - 1] == '\n')
buffer[len - 1] = '\0';
- add_to_ref_list(sha1, buffer + 41 + offset, list);
+ if (get_sha1_hex(buffer + offset, sha1)) {
+ warn("unrecognized header: %s", buffer);
+ continue;
+ }
+ delim = buffer[40 + offset];
+ if (!isspace(delim) && (delim != '\0' || !is_prereq))
+ die ("invalid header: %s", buffer);
+ add_to_ref_list(sha1, isspace(delim) ?
+ buffer + 41 + offset : "", list);
}
return fd;
}
@@ -127,20 +135,28 @@ static int fork_with_pipe(const char **argv, int *in, int *out)
dup2(fdin[0], 0);
close(fdin[0]);
close(fdin[1]);
- } else if (in)
+ } else if (in) {
dup2(*in, 0);
+ close(*in);
+ }
if (needs_out) {
dup2(fdout[1], 1);
close(fdout[0]);
close(fdout[1]);
- } else if (out)
+ } else if (out) {
dup2(*out, 1);
+ close(*out);
+ }
exit(execv_git_cmd(argv));
}
if (needs_in)
close(fdin[0]);
+ else if (in)
+ close(*in);
if (needs_out)
close(fdout[1]);
+ else if (out)
+ close(*out);
return pid;
}
@@ -151,29 +167,28 @@ static int verify_bundle(struct bundle_header *header)
* to be verbose about the errors
*/
struct ref_list *p = &header->prerequisites;
- const char *argv[5] = {"rev-list", "--stdin", "--not", "--all", NULL};
- int pid, in, out, i, ret = 0;
+ char **argv;
+ int pid, out, i, ret = 0;
char buffer[1024];
- in = out = -1;
- pid = fork_with_pipe(argv, &in, &out);
+ argv = xmalloc((p->nr + 4) * sizeof(const char *));
+ argv[0] = "rev-list";
+ argv[1] = "--not";
+ argv[2] = "--all";
+ for (i = 0; i < p->nr; i++)
+ argv[i + 3] = xstrdup(sha1_to_hex(p->list[i].sha1));
+ argv[p->nr + 3] = NULL;
+ out = -1;
+ pid = fork_with_pipe((const char **)argv, NULL, &out);
if (pid < 0)
return error("Could not fork rev-list");
- if (!fork()) {
- for (i = 0; i < p->nr; i++) {
- write(in, sha1_to_hex(p->list[i].sha1), 40);
- write(in, "\n", 1);
- }
- close(in);
- exit(0);
- }
- close(in);
- while (read_string(out, buffer, sizeof(buffer)) > 0) {
- if (ret++ == 0)
- error ("The bundle requires the following commits you lack:");
- fprintf(stderr, "%s", buffer);
- }
+ while (read_string(out, buffer, sizeof(buffer)) > 0)
+ ; /* do nothing */
close(out);
+ for (i = 0; i < p->nr; i++)
+ free(argv[i + 3]);
+ free(argv);
+
while (waitpid(pid, &i, 0) < 0)
if (errno != EINTR)
return -1;
@@ -205,8 +220,8 @@ static int list_heads(struct bundle_header *header, int argc, const char **argv)
static void show_commit(struct commit *commit)
{
- write(1, sha1_to_hex(commit->object.sha1), 40);
- write(1, "\n", 1);
+ 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;
@@ -216,15 +231,15 @@ static void show_commit(struct commit *commit)
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.
- * */
+ * confuse downstream git-pack-objects very badly.
+ */
const char *ep = strchr(p->name, '\n');
int len = ep ? ep - p->name : strlen(p->name);
- write(1, sha1_to_hex(p->item->sha1), 40);
- write(1, " ", 1);
+ write_or_die(1, sha1_to_hex(p->item->sha1), 40);
+ write_or_die(1, " ", 1);
if (len)
- write(1, p->name, len);
- write(1, "\n", 1);
+ write_or_die(1, p->name, len);
+ write_or_die(1, "\n", 1);
}
static int create_bundle(struct bundle_header *header, const char *path,
@@ -243,7 +258,7 @@ static int create_bundle(struct bundle_header *header, const char *path,
return error("Could not write to '%s'", path);
/* write signature */
- write(bundle_fd, bundle_signature, strlen(bundle_signature));
+ write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature));
/* write prerequisites */
memcpy(argv_boundary + 2, argv + 1, argc * sizeof(const char *));
@@ -256,7 +271,7 @@ static int create_bundle(struct bundle_header *header, const char *path,
return -1;
while ((i = read_string(out, buffer, sizeof(buffer))) > 0)
if (buffer[0] == '-')
- write(bundle_fd, buffer, i);
+ write_or_die(bundle_fd, buffer, i);
while ((i = waitpid(pid, &status, 0)) < 0)
if (errno != EINTR)
return error("rev-list died");
@@ -279,16 +294,16 @@ static int create_bundle(struct bundle_header *header, const char *path,
char *ref;
if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
continue;
- write(bundle_fd, sha1_to_hex(e->item->sha1), 40);
- write(bundle_fd, " ", 1);
- write(bundle_fd, ref, strlen(ref));
- write(bundle_fd, "\n", 1);
+ 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 */
- write(bundle_fd, "\n", 1);
+ write_or_die(bundle_fd, "\n", 1);
/* write pack */
argv_pack[0] = "pack-objects";
@@ -301,7 +316,6 @@ static int create_bundle(struct bundle_header *header, const char *path,
if (pid < 0)
return error("Could not spawn pack-objects");
close(1);
- close(bundle_fd);
dup2(in, 1);
close(in);
prepare_revision_walk(&revs);
@@ -327,7 +341,6 @@ static int unbundle(struct bundle_header *header, int bundle_fd,
pid = fork_with_pipe(argv_index_pack, &bundle_fd, &dev_null);
if (pid < 0)
return error("Could not spawn index-pack");
- close(bundle_fd);
while (waitpid(pid, &status, 0) < 0)
if (errno != EINTR)
return error("index-pack died");
diff --git a/index-pack.c b/index-pack.c
index 5ccf4c4..365f8f3 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -457,8 +457,9 @@ static void parse_pack_objects(unsigned char *sha1)
/* If input_fd is a file, we should have reached its end now. */
if (fstat(input_fd, &st))
die("cannot fstat packfile: %s", strerror(errno));
- if (input_fd && S_ISREG(st.st_mode) && st.st_size != consumed_bytes)
- die("pack has junk at the end: 0%o, %d, %d %d", st.st_mode, (int)st.st_size, (int)consumed_bytes, input_fd);
+ if (S_ISREG(st.st_mode) &&
+ lseek(input_fd, 0, SEEK_CUR) - input_len != st.st_size)
+ die("pack has junk at the end");
if (!nr_deltas)
return;
--
1.5.0.1.2213.g94bc-dirty
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 16:20 ` Johannes Schindelin
@ 2007-02-22 19:10 ` Junio C Hamano
2007-02-22 19:16 ` Johannes Schindelin
0 siblings, 1 reply; 28+ messages in thread
From: Junio C Hamano @ 2007-02-22 19:10 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git, Mark Levedahl
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
> Thinking about this deeper, I have to say I find my decision to use
> "--stdin" rather silly, given that I know the exact number of revisions,
> and their SHA1s.
The only worry I would have is if that exact number is too large
that you cannot pass them sensibly in argv[].
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 19:10 ` Junio C Hamano
@ 2007-02-22 19:16 ` Johannes Schindelin
2007-02-22 20:05 ` Junio C Hamano
0 siblings, 1 reply; 28+ messages in thread
From: Johannes Schindelin @ 2007-02-22 19:16 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Mark Levedahl
Hi,
On Thu, 22 Feb 2007, Junio C Hamano wrote:
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>
> > Thinking about this deeper, I have to say I find my decision to use
> > "--stdin" rather silly, given that I know the exact number of revisions,
> > and their SHA1s.
>
> The only worry I would have is if that exact number is too large
> that you cannot pass them sensibly in argv[].
I thought there is only a limitation in bash?
Well, anyway, I think that it makes sense writing just a little loop to
find if we have the revs or not, to be able to warn about them properly.
I'll do that.
Ciao,
Dscho
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 19:16 ` Johannes Schindelin
@ 2007-02-22 20:05 ` Junio C Hamano
2007-02-22 20:25 ` Johannes Schindelin
0 siblings, 1 reply; 28+ messages in thread
From: Junio C Hamano @ 2007-02-22 20:05 UTC (permalink / raw)
To: Johannes Schindelin; +Cc: git, Mark Levedahl
Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>> The only worry I would have is if that exact number is too large
>> that you cannot pass them sensibly in argv[].
>
> I thought there is only a limitation in bash?
I am sure kernel folks on the list would correct me, but my
understanding is that is execve(2) limitation and you would get
E2BIG.
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 20:05 ` Junio C Hamano
@ 2007-02-22 20:25 ` Johannes Schindelin
0 siblings, 0 replies; 28+ messages in thread
From: Johannes Schindelin @ 2007-02-22 20:25 UTC (permalink / raw)
To: Junio C Hamano; +Cc: git, Mark Levedahl
Hi,
On Thu, 22 Feb 2007, Junio C Hamano wrote:
> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:
>
> >> The only worry I would have is if that exact number is too large
> >> that you cannot pass them sensibly in argv[].
> >
> > I thought there is only a limitation in bash?
>
> I am sure kernel folks on the list would correct me, but my
> understanding is that is execve(2) limitation and you would get
> E2BIG.
Okay. So how about this (on top of my previous fixup):
-- snipsnap --
[PATCH] git-bundle: avoid fork() in verify_bundle()
We can use the revision walker easily for checking if the
prerequisites are met, instead of fork()ing off a rev-list,
which would list only the first unmet prerequisite.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
builtin-bundle.c | 75 ++++++++++++++++++++++++++++++++++-------------------
1 files changed, 48 insertions(+), 27 deletions(-)
diff --git a/builtin-bundle.c b/builtin-bundle.c
index 521bbda..4ac06f9 100644
--- a/builtin-bundle.c
+++ b/builtin-bundle.c
@@ -19,7 +19,7 @@ static const char bundle_signature[] = "# v2 git bundle\n";
struct ref_list {
unsigned int nr, alloc;
- struct {
+ struct ref_list_entry {
unsigned char sha1[20];
char *name;
} *list;
@@ -167,33 +167,54 @@ static int verify_bundle(struct bundle_header *header)
* to be verbose about the errors
*/
struct ref_list *p = &header->prerequisites;
- char **argv;
- int pid, out, i, ret = 0;
- char buffer[1024];
+ struct rev_info revs;
+ const char *argv[] = {NULL, "--all"};
+ struct object_array refs;
+ struct commit *commit;
+ int i, ret = 0, req_nr;
+ const char *message = "The bundle requires these lacking revs:";
- argv = xmalloc((p->nr + 4) * sizeof(const char *));
- argv[0] = "rev-list";
- argv[1] = "--not";
- argv[2] = "--all";
- for (i = 0; i < p->nr; i++)
- argv[i + 3] = xstrdup(sha1_to_hex(p->list[i].sha1));
- argv[p->nr + 3] = NULL;
- out = -1;
- pid = fork_with_pipe((const char **)argv, NULL, &out);
- if (pid < 0)
- return error("Could not fork rev-list");
- while (read_string(out, buffer, sizeof(buffer)) > 0)
- ; /* do nothing */
- close(out);
- for (i = 0; i < p->nr; i++)
- free(argv[i + 3]);
- free(argv);
-
- while (waitpid(pid, &i, 0) < 0)
- if (errno != EINTR)
- return -1;
- if (!ret && (!WIFEXITED(i) || WEXITSTATUS(i)))
- return error("At least one prerequisite is lacking.");
+ init_revisions(&revs, NULL);
+ for (i = 0; i < p->nr; i++) {
+ struct ref_list_entry *e = p->list + i;
+ struct object *o = parse_object(e->sha1);
+ if (o) {
+ o->flags |= BOUNDARY_SHOW;
+ add_pending_object(&revs, o, e->name);
+ continue;
+ }
+ if (++ret == 1)
+ error(message);
+ error("%s %s", sha1_to_hex(e->sha1), e->name);
+ }
+ if (revs.pending.nr == 0)
+ return ret;
+ req_nr = revs.pending.nr;
+ setup_revisions(2, argv, &revs, NULL);
+
+ memset(&refs, 0, sizeof(struct object_array));
+ for (i = 0; i < revs.pending.nr; i++) {
+ struct object_array_entry *e = revs.pending.objects + i;
+ add_object_array(e->item, e->name, &refs);
+ }
+
+ prepare_revision_walk(&revs);
+
+ i = req_nr;
+ while (i && (commit = get_revision(&revs)))
+ if (commit->object.flags & BOUNDARY_SHOW)
+ i--;
+
+ for (i = 0; i < req_nr; i++)
+ if (!(refs.objects[i].item->flags & SHOWN)) {
+ if (++ret == 1)
+ error(message);
+ error("%s %s", sha1_to_hex(refs.objects[i].item->sha1),
+ refs.objects[i].name);
+ }
+
+ for (i = 0; i < refs.nr; i++)
+ clear_commit_marks((struct commit *)refs.objects[i].item, -1);
return ret;
}
--
1.5.0.1.2214.gf22e-dirty
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH] git-bundle: assorted fixes
2007-02-22 18:14 ` [PATCH] git-bundle: assorted fixes Johannes Schindelin
@ 2007-02-23 1:36 ` Mark Levedahl
2007-02-23 1:56 ` Johannes Schindelin
0 siblings, 1 reply; 28+ messages in thread
From: Mark Levedahl @ 2007-02-23 1:36 UTC (permalink / raw)
To: Johannes Schindelin
Cc: git, junkio, Nicolas Pitre, Simon 'corecode' Schubert
Johannes Schindelin wrote:
I built this under cygwin, it is running but I find two regressions
compared to my git-bundle.sh:
1) git bundle create --all <whatever> does not record any references,
while it should record all references (heads and tags). From shell, this
requires use of both git-rev-parse and git-show-ref:
gitrevargs=$(git-rev-parse --symbolic --revs-only $args) || exit 1
# find the refs to carry along and get sha1s for each.
refs=
fullrevargs=
for arg in $gitrevargs ; do
#ignore options and basis refs, get full ref name for things
# we will transport rejecting anything ambiguous (e.g., user
# gives master, have heads/master and remotes/origin/master, we
# keep the former).
case "$arg" in
-* | ^*) fullrevargs="$fullrevargs $arg";;
*) ref=$(git-show-ref "$arg")
test "$(echo $ref | wc -w)" = "2" || die "Ambigous
reference: $arg
$ref"
fullrevargs="$fullrevargs ${ref#* }"
refs="$refs $ref";;
esac
done
2) git bundle verify reports only a single sha1 if prerequisites are
not met. git-bundle.sh would loop through finding each missing one,
annotating with the one line commit message. The prerequisites were
stored using:
(for p in $prereqs ; do
git-rev-list --pretty=one --max-count=1 $p
done) > "$prerequisites"
and then verify does:
test -z "$prereqs" && return 0
bad=$(echo "$prereqs" | cut -b-40 | git-rev-list --stdin --not --all
2>&1)
if test -n "$bad" ; then
test "$1" = "--silent" && return 1
echo "error: $bfile requires the following commits you lack:"
echo "$prereqs" |
while read sha1 comment ; do
missing=$(git-rev-list $sha1 --not --all 2>&1)
test -n "$missing" && echo "$sha1 $comment"
done
exit 1
fi
return 0
The difference is
fatal: bad object 59e4aa84d4b4f7c6393317c68649ef7db3c4440c
error: At least one prerequisite is lacking.
vs.
error: /home/mlevedahl/bundle2 requires the following commits you lack:
59e4aa84d4b4f7c6393317c68649ef7db3c4440c Merge remote branch
'rvaas05/gem.418' into lcb.418
8eb8c8274c28be9435da8534f8bb598503f4f85a Merge remote branch
'rvaas05/gem.418' into lcd.419
This was Junio's suggestion originally, and I think it a very good one.
The latter error message is far more helpful to some poor soul trying to
fix a problem.
Also, I did track down the issue that forced me to use tar (or at least
*some* archiver) in cygwin: it is a known bug without a planned fix that
precludes saving mixed text and binary in one file from bash, and seems
to be tied deeply into bash, fork, and pipes. Basically, doing
echo "some text data" > file
echo "some binary data" >>file
is totally confused as the underlying pipe / fork mechanism thinks file
is text mode on the second operation. This happens regardless of mount
mode or CYGWIN=binmode. The end result is under bash the binary data
unconditionally suffers crlf->lf translation. You can look at at
http://www.mail-archive.com/cygwin@cygwin.com/msg76319.html for more
information.
Mark
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] git-bundle: assorted fixes
2007-02-23 1:36 ` Mark Levedahl
@ 2007-02-23 1:56 ` Johannes Schindelin
2007-02-23 2:12 ` Mark Levedahl
0 siblings, 1 reply; 28+ messages in thread
From: Johannes Schindelin @ 2007-02-23 1:56 UTC (permalink / raw)
To: Mark Levedahl
Cc: git, junkio, Nicolas Pitre, Simon 'corecode' Schubert
Hi,
On Thu, 22 Feb 2007, Mark Levedahl wrote:
> Johannes Schindelin wrote:
>
> I built this under cygwin, it is running but I find two regressions compared
> to my git-bundle.sh:
>
> 1) git bundle create --all <whatever>
Are you sure you did not provide a bundle filename?
But indeed, my test shows that "--all" does not leave any refs. Bad.
This fixes it:
-- snip --
revision.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/revision.c b/revision.c
index 4cf697e..823bbd1 100644
--- a/revision.c
+++ b/revision.c
@@ -480,7 +480,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, int flag,
struct all_refs_cb *cb = cb_data;
struct object *object = get_reference(cb->all_revs, path, sha1,
cb->all_flags);
- add_pending_object(cb->all_revs, object, "");
+ add_pending_object(cb->all_revs, object, path);
return 0;
}
-- snap --
but I think this adds a memory leak, and I don't know what else is
affected by it.
> 2) git bundle verify reports only a single sha1 if prerequisites are not
> met.
With the two follow-up patches I sent, this issue should be resolved, no?
Ciao,
Dscho
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH] git-bundle: assorted fixes
2007-02-23 1:56 ` Johannes Schindelin
@ 2007-02-23 2:12 ` Mark Levedahl
2007-02-23 2:17 ` Johannes Schindelin
0 siblings, 1 reply; 28+ messages in thread
From: Mark Levedahl @ 2007-02-23 2:12 UTC (permalink / raw)
To: Johannes Schindelin
Cc: git, junkio, Nicolas Pitre, Simon 'corecode' Schubert
Johannes Schindelin wrote:
> Hi,
>
>> 1) git bundle create --all <whatever>
>>
>
> Are you sure you did not provide a bundle filename?
>
I did, you *have* to give a bundle filename..
> But indeed, my test shows that "--all" does not leave any refs. Bad.
>
> This fixes it:
>
Confirmed - that works.
>
>
>> 2) git bundle verify reports only a single sha1 if prerequisites are not
>> met.
>>
>
> With the two follow-up patches I sent, this issue should be resolved, no?
>
> Ciao,
> Dscho
>
>
I missed one of your patches, adding that, the problem is incompletely
fixed: all missing commits are shown, but only as raw hashes (e.g., the
1-line commit message is not there to describe what is missing).
Mark
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] git-bundle: assorted fixes
2007-02-23 2:12 ` Mark Levedahl
@ 2007-02-23 2:17 ` Johannes Schindelin
2007-02-23 3:37 ` Mark Levedahl
0 siblings, 1 reply; 28+ messages in thread
From: Johannes Schindelin @ 2007-02-23 2:17 UTC (permalink / raw)
To: Mark Levedahl
Cc: git, junkio, Nicolas Pitre, Simon 'corecode' Schubert
On Thu, 22 Feb 2007, Mark Levedahl wrote:
> Johannes Schindelin wrote:
>
> > Mark wrote:
> >
> > > 2) git bundle verify reports only a single sha1 if prerequisites are
> > > not met.
> >
> > With the two follow-up patches I sent, this issue should be resolved,
> > no?
> >
> I missed one of your patches, adding that, the problem is incompletely
> fixed: all missing commits are shown, but only as raw hashes (e.g., the
> 1-line commit message is not there to describe what is missing).
Okay, how about this on top:
-- snipsnap --
builtin-bundle.c | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/builtin-bundle.c b/builtin-bundle.c
index 4ac06f9..d11435b 100644
--- a/builtin-bundle.c
+++ b/builtin-bundle.c
@@ -267,7 +267,7 @@ static int create_bundle(struct bundle_header *header, const char *path,
int argc, const char **argv)
{
int bundle_fd = -1;
- const char **argv_boundary = xmalloc((argc + 3) * sizeof(const char *));
+ const char **argv_boundary = xmalloc((argc + 4) * sizeof(const char *));
const char **argv_pack = xmalloc(4 * sizeof(const char *));
int pid, in, out, i, status;
char buffer[1024];
@@ -282,10 +282,11 @@ static int create_bundle(struct bundle_header *header, const char *path,
write_or_die(bundle_fd, bundle_signature, strlen(bundle_signature));
/* write prerequisites */
- memcpy(argv_boundary + 2, argv + 1, argc * sizeof(const char *));
+ memcpy(argv_boundary + 3, argv + 1, argc * sizeof(const char *));
argv_boundary[0] = "rev-list";
argv_boundary[1] = "--boundary";
- argv_boundary[argc + 1] = NULL;
+ argv_boundary[2] = "--pretty=oneline";
+ argv_boundary[argc + 2] = NULL;
out = -1;
pid = fork_with_pipe(argv_boundary, NULL, &out);
if (pid < 0)
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-22 6:56 ` Junio C Hamano
2007-02-22 7:08 ` Junio C Hamano
2007-02-22 16:17 ` Johannes Schindelin
@ 2007-02-23 2:32 ` Mark Levedahl
2007-02-23 4:39 ` Junio C Hamano
2 siblings, 1 reply; 28+ messages in thread
From: Mark Levedahl @ 2007-02-23 2:32 UTC (permalink / raw)
To: Junio C Hamano; +Cc: Johannes Schindelin, git
Junio C Hamano wrote:
>> diff --git a/Documentation/cmd-list.perl b/Documentation/cmd-list.perl
>> index a2d6268..f61c77a 100755
>> --- a/Documentation/cmd-list.perl
>> +++ b/Documentation/cmd-list.perl
>> @@ -70,6 +70,7 @@ git-archive mainporcelain
>> git-bisect mainporcelain
>> git-blame ancillaryinterrogators
>> git-branch mainporcelain
>> +git-bundle mainporcelain
>> git-cat-file plumbinginterrogators
>> git-checkout-index plumbingmanipulators
>> git-checkout mainporcelain
>>
>
> Is this really a mainporcelain?
> I would say ancillarymanipulators (or perhaps synchelpers).
>
>
git bundle has four commands: create, verify, list-heads, and unbundle.
The last two are pure helper functions, basically plumbing. Verify is
questionable as to where it lies. But, create is the only way to create
a bundle, is logically equivalent to git push as a user command to move
data, so I called it mainporcelain because that is how git push is
classified.
>> + /* write prerequisites */
>> + memcpy(argv_boundary + 2, argv + 1, argc * sizeof(const char *));
>> + argv_boundary[0] = "rev-list";
>> + argv_boundary[1] = "--boundary";
>> + argv_boundary[argc + 1] = NULL;
>> + out = -1;
>> + pid = fork_with_pipe(argv_boundary, NULL, &out);
>> + if (pid < 0)
>> + return -1;
>> + while ((i = read_string(out, buffer, sizeof(buffer))) > 0)
>> + if (buffer[0] == '-')
>> + write(bundle_fd, buffer, i);
>>
>
> It would be helpful for the recipient if you can append output
> from git-describe (or name-rev) when the buffer lacks "name".
>
I found the actual commit summary message (i.e., git-rev-list
--pretty=one --max-count=1 sha1) the most useful of the various
summaries available.
Mark
^ permalink raw reply [flat|nested] 28+ messages in thread
* Re: [PATCH] git-bundle: assorted fixes
2007-02-23 2:17 ` Johannes Schindelin
@ 2007-02-23 3:37 ` Mark Levedahl
0 siblings, 0 replies; 28+ messages in thread
From: Mark Levedahl @ 2007-02-23 3:37 UTC (permalink / raw)
To: Johannes Schindelin
Cc: git, junkio, Nicolas Pitre, Simon 'corecode' Schubert
Johannes Schindelin wrote:
>
> On Thu, 22 Feb 2007, Mark Levedahl wrote:
>
>> Johannes Schindelin wrote:
>>
>>> Mark wrote:
>>>
>>>> 2) git bundle verify reports only a single sha1 if prerequisites are
>>>> not met.
>>> With the two follow-up patches I sent, this issue should be resolved,
>>> no?
>>>
>> I missed one of your patches, adding that, the problem is incompletely
>> fixed: all missing commits are shown, but only as raw hashes (e.g., the
>> 1-line commit message is not there to describe what is missing).
>
> Okay, how about this on top:
>
>
That works. I would change your error message slightly, perhaps...
diff --git a/builtin-bundle.c b/builtin-bundle.c
index d74afaa..e115feb 100644
--- a/builtin-bundle.c
+++ b/builtin-bundle.c
@@ -172,7 +172,7 @@ static int verify_bundle(struct bundle_header *header)
struct object_array refs;
struct commit *commit;
int i, ret = 0, req_nr;
- const char *message = "The bundle requires these lacking revs:";
+ const char *message = "Your repository lacks these prerequisite
commits:";
init_revisions(&revs, NULL);
for (i = 0; i < p->nr; i++) {
Mark
^ permalink raw reply related [flat|nested] 28+ messages in thread
* Re: [PATCH] Add git-bundle: move objects and references by archive
2007-02-23 2:32 ` Mark Levedahl
@ 2007-02-23 4:39 ` Junio C Hamano
0 siblings, 0 replies; 28+ messages in thread
From: Junio C Hamano @ 2007-02-23 4:39 UTC (permalink / raw)
To: Mark Levedahl; +Cc: Johannes Schindelin, git
Mark Levedahl <mdl123@verizon.net> writes:
> I found the actual commit summary message (i.e., git-rev-list
> --pretty=one --max-count=1 sha1) the most useful of the various
> summaries available.
I would agree that would be the case, and Johannes's follow-up
seems to do that.
^ permalink raw reply [flat|nested] 28+ messages in thread
end of thread, other threads:[~2007-02-23 4:39 UTC | newest]
Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <Pine.LNX.4.63.0702220157130.22628@wbgn013.biozentrum.uni-wuerz burg.de>
2007-02-22 0:59 ` [PATCH] Add git-bundle: move objects and references by archive Johannes Schindelin
2007-02-22 3:28 ` Nicolas Pitre
2007-02-22 15:55 ` Johannes Schindelin
2007-02-22 16:14 ` Simon 'corecode' Schubert
2007-02-22 16:28 ` Nicolas Pitre
2007-02-22 16:37 ` Johannes Schindelin
2007-02-22 16:46 ` Simon 'corecode' Schubert
2007-02-22 17:09 ` Johannes Schindelin
2007-02-22 16:24 ` Nicolas Pitre
2007-02-22 17:12 ` Johannes Schindelin
2007-02-22 17:21 ` Nicolas Pitre
2007-02-22 6:56 ` Junio C Hamano
2007-02-22 7:08 ` Junio C Hamano
2007-02-22 16:20 ` Johannes Schindelin
2007-02-22 19:10 ` Junio C Hamano
2007-02-22 19:16 ` Johannes Schindelin
2007-02-22 20:05 ` Junio C Hamano
2007-02-22 20:25 ` Johannes Schindelin
2007-02-22 16:17 ` Johannes Schindelin
2007-02-23 2:32 ` Mark Levedahl
2007-02-23 4:39 ` Junio C Hamano
2007-02-22 9:31 ` Johannes Sixt
2007-02-22 18:14 ` [PATCH] git-bundle: assorted fixes Johannes Schindelin
2007-02-23 1:36 ` Mark Levedahl
2007-02-23 1:56 ` Johannes Schindelin
2007-02-23 2:12 ` Mark Levedahl
2007-02-23 2:17 ` Johannes Schindelin
2007-02-23 3:37 ` Mark Levedahl
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).