{"thread":{"id":"12592","subject":"[PATCH 10/11] Build in clone","startedAt":"2008-03-08T23:04:23Z","lastAt":"2008-03-10T10:58:50Z","messageCount":8,"participants":["Daniel Barkalow","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":11},"messages":[{"id":"71465","messageId":"alpine.LNX.1.00.0803081803360.19665@iabervon.org","threadId":"12592","inReplyTo":null,"subject":"[PATCH 10/11] Build in clone","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2008-03-08T23:04:23Z","receivedAt":"2008-03-08T23:04:23Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"This version is still a mess, but it passes all of the tests. I'm\nsomewhat unconvinced by the test coverage for clone, however; the\nlast failure I found was actually for which heads get created in a\nbare repository, and it was only failing when there was an extra one\nin a non-bare clone in a test for something entirely different.\n\nThanks to Johannes Schindelin for various comments and improvements.\n---\n Makefile                                      |    2 +-\n builtin-clone.c                               |  542 +++++++++++++++++++++++++\n builtin.h                                     |    1 +\n git-clone.sh => contrib/examples/git-clone.sh |    0 \n git.c                                         |    1 +\n t/t5701-clone-local.sh                        |    6 +-\n 6 files changed, 548 insertions(+), 4 deletions(-)\n create mode 100644 builtin-clone.c\n rename git-clone.sh => contrib/examples/git-clone.sh (100%)\n\ndiff --git a/Makefile b/Makefile\nindex 7917509..aec2ac3 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -231,7 +231,6 @@ BASIC_LDFLAGS =\n \n SCRIPT_SH = \\\n \tgit-bisect.sh \\\n-\tgit-clone.sh \\\n \tgit-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \\\n \tgit-pull.sh git-rebase.sh git-rebase--interactive.sh \\\n \tgit-repack.sh git-request-pull.sh \\\n@@ -348,6 +347,7 @@ BUILTIN_OBJS = \\\n \tbuiltin-checkout-index.o \\\n \tbuiltin-check-ref-format.o \\\n \tbuiltin-clean.o \\\n+\tbuiltin-clone.o \\\n \tbuiltin-commit.o \\\n \tbuiltin-commit-tree.o \\\n \tbuiltin-count-objects.o \\\ndiff --git a/builtin-clone.c b/builtin-clone.c\nnew file mode 100644\nindex 0000000..1b83062\n--- /dev/null\n+++ b/builtin-clone.c\n@@ -0,0 +1,542 @@\n+/*\n+ * Builtin \"git clone\"\n+ *\n+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>,\n+ *\t\t 2008 Daniel Barkalow <barkalow@iabervon.org>\n+ * Based on git-commit.sh by Junio C Hamano and Linus Torvalds\n+ *\n+ * Clone a repository into a different directory that does not yet exist.\n+ */\n+\n+#include \"cache.h\"\n+#include \"parse-options.h\"\n+#include \"fetch-pack.h\"\n+#include \"refs.h\"\n+#include \"tree.h\"\n+#include \"tree-walk.h\"\n+#include \"unpack-trees.h\"\n+#include \"transport.h\"\n+#include \"strbuf.h\"\n+#include \"dir.h\"\n+\n+/*\n+ * Overall FIXMEs:\n+ *  - respect DB_ENVIRONMENT for .git/objects.\n+ *\n+ * Implementation notes:\n+ *  - dropping use-separate-remote and no-separate-remote compatibility\n+ *\n+ */\n+static const char * const builtin_clone_usage[] = {\n+\t\"git-clone [options] [--] <repo> [<dir>]\",\n+\tNULL\n+};\n+\n+static int option_quiet, option_no_checkout, option_bare;\n+static int option_local, option_no_hardlinks, option_shared;\n+static char *option_template, *option_reference, *option_depth;\n+static char *option_origin = NULL;\n+static char *option_upload_pack = \"git-upload-pack\";\n+\n+static struct option builtin_clone_options[] = {\n+\tOPT__QUIET(&option_quiet),\n+\tOPT_BOOLEAN('n', \"no-checkout\", &option_no_checkout,\n+\t\t    \"don't create a checkout\"),\n+\tOPT_BOOLEAN(0, \"bare\", &option_bare, \"create a bare repository\"),\n+\tOPT_BOOLEAN(0, \"naked\", &option_bare, \"create a bare repository\"),\n+\tOPT_BOOLEAN('l', \"local\", &option_local,\n+\t\t    \"to clone from a local repository\"),\n+\tOPT_BOOLEAN(0, \"no-hardlinks\", &option_no_hardlinks,\n+\t\t    \"don't use local hardlinks, always copy\"),\n+\tOPT_BOOLEAN('s', \"shared\", &option_shared,\n+\t\t    \"setup as shared repository\"),\n+\tOPT_STRING(0, \"template\", &option_template, \"path\",\n+\t\t   \"path the template repository\"),\n+\tOPT_STRING(0, \"reference\", &option_reference, \"repo\",\n+\t\t   \"reference repository\"),\n+\tOPT_STRING('o', \"origin\", &option_origin, \"branch\",\n+\t\t   \"use <branch> instead or 'origin' to track upstream\"),\n+\tOPT_STRING('u', \"upload-pack\", &option_upload_pack, \"path\",\n+\t\t   \"path to git-upload-pack on the remote\"),\n+\tOPT_STRING(0, \"depth\", &option_depth, \"depth\",\n+\t\t    \"create a shallow clone of that depth\"),\n+\n+\tOPT_END()\n+};\n+\n+static char *get_repo_path(const char *repo)\n+{\n+\tconst char *path;\n+\tstruct stat buf;\n+\n+\tpath = mkpath(\"%s/.git\", repo);\n+\tif (!stat(path, &buf) && S_ISDIR(buf.st_mode))\n+\t\treturn xstrdup(make_absolute_path(path));\n+\n+\tpath = mkpath(\"%s.git\", repo);\n+\tif (!stat(path, &buf) && S_ISDIR(buf.st_mode))\n+\t\treturn xstrdup(make_absolute_path(path));\n+\n+\tif (!stat(repo, &buf) && S_ISDIR(buf.st_mode))\n+\t\treturn xstrdup(make_absolute_path(repo));\n+\n+\treturn NULL;\n+}\n+\n+static char *guess_dir_name(const char *repo)\n+{\n+\tconst char *p, *start, *end, *limit;\n+\tint after_slash_or_colon;\n+\n+\t/* Guess dir name from repository: strip trailing '/',\n+\t * strip trailing '[:/]*git', strip leading '.*[/:]'. */\n+\n+\tafter_slash_or_colon = 1;\n+\tlimit = repo + strlen(repo);\n+\tstart = repo;\n+\tend = limit;\n+\tfor (p = repo; p < limit; p++) {\n+\t\tif (!prefixcmp(p, \".git\")) {\n+\t\t\tif (!after_slash_or_colon)\n+\t\t\t\tend = p;\n+\t\t\tp += 3;\n+\t\t} else if (!prefixcmp(p, \".bundle\")) {\n+\t\t\tif (!after_slash_or_colon)\n+\t\t\t\tend = p;\n+\t\t\tp += 7;\n+\t\t} else if (*p == '/' || *p == ':') {\n+\t\t\tif (end == limit)\n+\t\t\t\tend = p;\n+\t\t\tafter_slash_or_colon = 1;\n+\t\t} else if (after_slash_or_colon) {\n+\t\t\tstart = p;\n+\t\t\tend = limit;\n+\t\t\tafter_slash_or_colon = 0;\n+\t\t}\n+\t}\n+\n+\treturn xstrndup(start, end - start);\n+}\n+\n+void setup_reference(const char *repo)\n+{\n+\tstruct stat buf;\n+\tconst char *ref_git;\n+\tchar *ref_git_copy;\n+\n+\tstruct remote *remote;\n+\tstruct transport *transport;\n+\tconst struct ref *extra;\n+\n+\tif (!option_reference)\n+\t\treturn;\n+\n+\tref_git = make_absolute_path(option_reference);\n+\n+\tif (!stat(mkpath(\"%s/.git/objects\", ref_git), &buf) &&\n+\t    S_ISDIR(buf.st_mode))\n+\t\tref_git = mkpath(\"%s/.git\", ref_git);\n+\telse if (stat(mkpath(\"%s/objects\", ref_git), &buf) ||\n+\t\t !S_ISDIR(buf.st_mode))\n+\t\tdie(\"reference repository '%s' is not a local directory.\",\n+\t\t    option_reference);\n+\n+\tref_git_copy = xstrdup(ref_git);\n+\n+\tadd_to_alternates_file(ref_git_copy);\n+\n+\tremote = remote_get(ref_git_copy);\n+\ttransport = transport_get(remote, ref_git_copy);\n+\tfor (extra = transport_get_remote_refs(transport); extra;\n+\t     extra = extra->next)\n+\t\tadd_extra_ref(extra->name, extra->old_sha1, 0);\n+\n+\ttransport_disconnect(transport);\n+\n+\tfree(ref_git_copy);\n+}\n+\n+static void\n+walk_objects(char *src, char *dest)\n+{\n+\tstruct dirent *de;\n+\tstruct stat buf;\n+\tint src_len, dest_len;\n+\tDIR *dir;\n+\n+\tdir = opendir(src);\n+\tif (!dir)\n+\t\tdie(\"failed to open %s\\n\", src);\n+\n+\tif (mkdir(dest, 0777)) {\n+\t\tif (errno != EEXIST)\n+\t\t\tdie(\"failed to create directory %s\\n\", dest);\n+\t\telse if (stat(dest, &buf))\n+\t\t\tdie(\"failed to stat %s\\n\", dest);\n+\t\telse if (!S_ISDIR(buf.st_mode))\n+\t\t\tdie(\"%s exists and is not a directory\\n\", dest);\n+\t}\n+\n+\tsrc_len = strlen(src);\n+\tsrc[src_len] = '/';\n+\tdest_len = strlen(dest);\n+\tdest[dest_len] = '/';\n+\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tstrcpy(src + src_len + 1, de->d_name);\n+\t\tstrcpy(dest + dest_len + 1, de->d_name);\n+\t\tif (stat(src, &buf)) {\n+\t\t\tfprintf(stderr, \"failed to stat %s, ignoring\\n\", src);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (S_ISDIR(buf.st_mode)) {\n+\t\t\tif (de->d_name[0] != '.')\n+\t\t\t\twalk_objects(src, dest);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (unlink(dest) && errno != ENOENT)\n+\t\t\tdie(\"failed to unlink %s\\n\", dest);\n+\t\tif (option_no_hardlinks) {\n+\t\t\tif (copy_file(dest, src, 0666))\n+\t\t\t\tdie(\"failed to copy file to %s\\n\", dest);\n+\t\t} else {\n+\t\t\tif (link(src, dest))\n+\t\t\t\tdie(\"failed to create link %s\\n\", dest);\n+\t\t}\n+\t}\n+}\n+\n+static const struct ref *\n+clone_local(const char *src_repo, const char *dest_repo)\n+{\n+\tconst struct ref *ret;\n+\tchar src[PATH_MAX];\n+\tchar dest[PATH_MAX];\n+\tstruct remote *remote;\n+\tstruct transport *transport;\n+\n+\tif (option_shared) {\n+\t\tadd_to_alternates_file(src_repo);\n+\t} else {\n+\t\tsnprintf(src, PATH_MAX, \"%s/objects\", src_repo);\n+\t\tsnprintf(dest, PATH_MAX, \"%s/objects\", dest_repo);\n+\t\twalk_objects(src, dest);\n+\t}\n+\n+\tfprintf(stderr, \"Get for %s\\n\", src_repo);\n+\tremote = remote_get(src_repo);\n+\ttransport = transport_get(remote, src_repo);\n+\tret = transport_get_remote_refs(transport);\n+\ttransport_disconnect(transport);\n+\treturn ret;\n+}\n+\n+static const char *junk_work_tree;\n+static const char *junk_git_dir;\n+pid_t clone_pid;\n+\n+static void remove_junk(void)\n+{\n+\tstruct strbuf sb;\n+\tif (getpid() != clone_pid)\n+\t\treturn;\n+\tstrbuf_init(&sb, 0);\n+\tif (junk_git_dir) {\n+\t\tfprintf(stderr, \"Remove junk %s\\n\", junk_git_dir);\n+\t\tstrbuf_addstr(&sb, junk_git_dir);\n+\t\tremove_dir_recursively(&sb, 0);\n+\t\tstrbuf_reset(&sb);\n+\t}\n+\tif (junk_work_tree) {\n+\t\tfprintf(stderr, \"Remove junk %s\\n\", junk_work_tree);\n+\t\tstrbuf_addstr(&sb, junk_work_tree);\n+\t\tremove_dir_recursively(&sb, 0);\n+\t\tstrbuf_reset(&sb);\n+\t}\n+}\n+\n+static const struct ref *locate_head(const struct ref *refs,\n+\t\t\t\t     const struct ref *mapped_refs,\n+\t\t\t\t     const struct ref **remote_head_p)\n+{\n+\tconst struct ref *remote_head = NULL;\n+\tconst struct ref *remote_master = NULL;\n+\tconst struct ref *r;\n+\tfor (r = refs; r; r = r->next)\n+\t\tif (!strcmp(r->name, \"HEAD\"))\n+\t\t\tremote_head = r;\n+\n+\tfor (r = mapped_refs; r; r = r->next)\n+\t\tif (!strcmp(r->name, \"refs/heads/master\"))\n+\t\t\tremote_master = r;\n+\n+\tif (remote_head_p)\n+\t\t*remote_head_p = remote_head;\n+\n+\t/* If there's no HEAD value at all, never mind. */\n+\tif (!remote_head)\n+\t\treturn NULL;\n+\n+\t/* If refs/heads/master could be right, it is. */\n+\tif (remote_master && !hashcmp(remote_master->old_sha1,\n+\t\t\t\t      remote_head->old_sha1))\n+\t\treturn remote_master;\n+\n+\t/* Look for another ref that points there */\n+\tfor (r = mapped_refs; r; r = r->next)\n+\t\tif (r != remote_head &&\n+\t\t    !hashcmp(r->old_sha1, remote_head->old_sha1))\n+\t\t\treturn r;\n+\n+\t/* Nothing is the same */\n+\treturn NULL;\n+}\n+\n+static struct ref *write_remote_refs(const struct ref *refs, struct refspec *refspec, const char *reflog)\n+{\n+\tstruct ref *local_refs = NULL;\n+\tstruct ref **tail = &local_refs;\n+\tstruct ref *r;\n+\n+\tget_fetch_map(refs, refspec, &tail, 0);\n+\tget_fetch_map(refs, tag_refspec, &tail, 0);\n+\n+\tfor (r = local_refs; r; r = r->next)\n+\t\tupdate_ref(reflog,\n+\t\t\t   r->peer_ref->name, r->old_sha1, NULL, 0, DIE_ON_ERR);\n+\treturn local_refs;\n+}\n+\n+int cmd_clone(int argc, const char **argv, const char *prefix)\n+{\n+\tint use_local_hardlinks = 1;\n+\tint use_separate_remote = 1;\n+\tstruct stat buf;\n+\tconst char *repo_name, *repo, *work_tree, *git_dir;\n+\tchar *path, *dir;\n+\tconst struct ref *refs, *head_points_at, *remote_head, *mapped_refs;\n+\tchar branch_top[256], key[256], value[256];\n+\tstruct strbuf reflog_msg;\n+\n+\tstruct refspec refspec;\n+\n+\tclone_pid = getpid();\n+\n+\targc = parse_options(argc, argv, builtin_clone_options,\n+\t\t\t     builtin_clone_usage, 0);\n+\n+\tif (argc == 0)\n+\t\tdie(\"You must specify a repository to clone.\");\n+\n+\tif (option_no_hardlinks)\n+\t\tuse_local_hardlinks = 0;\n+\n+\tif (option_bare) {\n+\t\tif (option_origin)\n+\t\t\tdie(\"--bare and --origin %s options are incompatible.\",\n+\t\t\t    option_origin);\n+\t\toption_no_checkout = 1;\n+\t\tuse_separate_remote = 0;\n+\t}\n+\n+\tif (!option_origin)\n+\t\toption_origin = \"origin\";\n+\n+\trepo_name = argv[0];\n+\n+\tif (argc == 2) {\n+\t\tdir = xstrdup(argv[1]);\n+\t} else {\n+\t\tdir = guess_dir_name(repo_name);\n+\t}\n+\tfprintf(stderr, \"dir is %s\\n\", dir);\n+\n+\tpath = get_repo_path(repo_name);\n+\tif (path != NULL)\n+\t\trepo = xstrdup(make_absolute_path(path));\n+\telse if (!strchr(repo_name, ':'))\n+\t\trepo = xstrdup(make_absolute_path(repo_name));\n+\telse\n+\t\trepo = repo_name;\n+\tfprintf(stderr, \"repo is %s\\n\", repo);\n+\n+\tif (!stat(dir, &buf))\n+\t\tdie(\"destination directory '%s' already exists.\", dir);\n+\n+\tstrbuf_init(&reflog_msg, 0);\n+\tstrbuf_addf(&reflog_msg, \"clone: from %s\", repo);\n+\n+\tif (option_bare)\n+\t\twork_tree = NULL;\n+\telse {\n+\t\twork_tree = getenv(\"GIT_WORK_TREE\");\n+\t\tif (work_tree && !stat(work_tree, &buf))\n+\t\t\tdie(\"working tree '%s' already exists.\", work_tree);\n+\t}\n+\n+\tatexit(remove_junk);\n+\n+\tif (option_bare || work_tree)\n+\t\tgit_dir = xstrdup(dir);\n+\telse {\n+\t\twork_tree = dir;\n+\t\tgit_dir = xstrdup(mkpath(\"%s/.git\", dir));\n+\t}\n+\n+\tif (!option_bare) {\n+\t\tjunk_work_tree = work_tree;\n+\t\tif (mkdir(work_tree, 0755))\n+\t\t\tdie(\"could not create work tree dir '%s'.\", work_tree);\n+\t\tset_git_work_tree(work_tree);\n+\t}\n+\n+\tsetenv(CONFIG_ENVIRONMENT, xstrdup(mkpath(\"%s/config\", git_dir)), 1);\n+\n+\tset_git_dir(make_absolute_path(git_dir));\n+\n+\tfprintf(stderr, \"Initialize %s\\n\", git_dir);\n+\tjunk_git_dir = git_dir;\n+\tinit_db(option_template, option_quiet ? INIT_DB_QUIET : 0);\n+\tfprintf(stderr, \"Okay\\n\");\n+\n+\tsetup_reference(git_dir);\n+\n+\tgit_config(git_default_config);\n+\n+\tif (option_bare) {\n+\t\tstrcpy(branch_top, \"refs/heads/\");\n+\n+\t\tgit_config_set(\"core.bare\", \"true\");\n+\t} else {\n+\t\tsnprintf(branch_top, sizeof branch_top,\n+\t\t\t \"refs/remotes/%s/\", option_origin);\n+\n+\t\t/* Configure the remote */\n+\t\tsnprintf(key, sizeof key, \"remote.%s.url\", option_origin);\n+\t\tgit_config_set(key, repo);\n+\n+\t\tsnprintf(key, sizeof key, \"remote.%s.fetch\", option_origin);\n+\t\tsnprintf(value, sizeof value, \"+refs/heads/*:%s*\", branch_top);\n+\t\tgit_config_set_multivar(key, value, \"^$\", 0);\n+\t}\n+\n+\trefspec.force = 0;\n+\trefspec.pattern = 1;\n+\trefspec.src = \"refs/heads/\";\n+\trefspec.dst = branch_top;\n+\n+\tif (path != NULL)\n+\t\trefs = clone_local(path, git_dir);\n+\telse {\n+\t\tstruct remote *remote = remote_get(argv[0]);\n+\t\tstruct transport *transport = transport_get(remote, argv[0]);\n+\n+\t\ttransport_set_option(transport, TRANS_OPT_KEEP, \"yes\");\n+\n+\t\tif (option_depth)\n+\t\t\ttransport_set_option(transport, TRANS_OPT_DEPTH,\n+\t\t\t\t\t     option_depth);\n+\n+\t\tif (option_quiet)\n+\t\t\ttransport->verbose = -1;\n+\n+\t\t//args.no_progress = 1;\n+\n+\t\tfprintf(stderr, \"Get refs for %s\\n\", argv[0]);\n+\t\trefs = transport_get_remote_refs(transport);\n+\n+\t\ttransport_fetch_refs(transport, refs);\n+\t}\n+\n+\t// FFFF transport_get_remote_refs() ought to be able to tell\n+\t// us about HEAD being a symref, and what it's to, if it\n+\t// actually knows.\n+\n+\tclear_extra_refs();\n+\n+\tmapped_refs = write_remote_refs(refs, &refspec, reflog_msg.buf);\n+\n+\thead_points_at = locate_head(refs, mapped_refs, &remote_head);\n+\n+\tif (head_points_at) {\n+\t\t/* Local default branch link */\n+\t\tcreate_symref(\"HEAD\", head_points_at->name, NULL);\n+\n+\t\tif (!option_bare) {\n+\t\t\tstruct strbuf head_ref;\n+\t\t\tconst char *head = head_points_at->name;\n+\n+\t\t\tif (!prefixcmp(head, \"refs/heads/\"))\n+\t\t\t\thead += 11;\n+\n+\t\t\t/* Set up the initial local branch */\n+\n+\t\t\t/* Local branch initial value */\n+\t\t\tupdate_ref(reflog_msg.buf, \"HEAD\",\n+\t\t\t\t   head_points_at->old_sha1,\n+\t\t\t\t   NULL, 0, DIE_ON_ERR);\n+\n+\t\t\tstrbuf_init(&head_ref, 0);\n+\t\t\tstrbuf_addstr(&head_ref, branch_top);\n+\t\t\tstrbuf_addstr(&head_ref, \"HEAD\");\n+\n+\t\t\t/* Remote branch link */\n+\t\t\tcreate_symref(head_ref.buf,\n+\t\t\t\t      head_points_at->peer_ref->name,\n+\t\t\t\t      reflog_msg.buf);\n+\n+\t\t\tsnprintf(key, sizeof key, \"branch.%s.remote\", head);\n+\t\t\tgit_config_set(key, option_origin);\n+\t\t\tsnprintf(key, sizeof key, \"branch.%s.merge\", head);\n+\t\t\tgit_config_set(key, head_points_at->name);\n+\t\t}\n+\t} else if (remote_head) {\n+\t\t/* Source had detached HEAD pointing somewhere. */\n+\t\tif (!option_bare)\n+\t\t\tupdate_ref(reflog_msg.buf, \"HEAD\",\n+\t\t\t\t   remote_head->old_sha1,\n+\t\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n+\t} else {\n+\t\t/* Nothing to checkout out */\n+\t\tif (!option_no_checkout)\n+\t\t\tfprintf(stderr, \"Warning: Remote HEAD refers to nonexistent ref, unable to checkout.\\n\");\n+\t\toption_no_checkout = 1;\n+\t}\n+\n+\tif (!option_no_checkout) {\n+\t\tstruct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));\n+\t\tstruct unpack_trees_options opts;\n+\t\tstruct tree *tree;\n+\t\tstruct tree_desc t[2];\n+\t\tint fd;\n+\n+\t\t/* We need to be in the new work tree for the checkout */\n+\t\tsetup_work_tree();\n+\n+\t\tfprintf(stderr, \"work tree now %s\\n\", get_git_work_tree());\n+\n+\t\tfd = hold_locked_index(lock_file, 1);\n+\n+\t\tmemset(&opts, 0, sizeof opts);\n+\t\topts.update = 1;\n+\t\topts.verbose_update = !option_quiet;\n+\t\topts.merge = 1;\n+\t\topts.fn = twoway_merge;\n+\n+\t\ttree = parse_tree_indirect(remote_head->old_sha1);\n+\t\tparse_tree(tree);\n+\t\tinit_tree_desc(&t[0], tree->buffer, tree->size);\n+\t\tinit_tree_desc(&t[1], tree->buffer, tree->size);\n+\t\tunpack_trees(2, t, &opts);\n+\n+\t\tif (write_cache(fd, active_cache, active_nr) ||\n+\t\t    commit_locked_index(lock_file))\n+\t\t\tdie(\"unable to write new index file\");\n+\t}\n+\n+\tstrbuf_release(&reflog_msg);\n+\tjunk_work_tree = NULL;\n+\tjunk_git_dir = NULL;\n+\treturn 0;\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 95126fd..23a90de 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -24,6 +24,7 @@ extern int cmd_check_attr(int argc, const char **argv, const char *prefix);\n extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);\n extern int cmd_cherry(int argc, const char **argv, const char *prefix);\n extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);\n+extern int cmd_clone(int argc, const char **argv, const char *prefix);\n extern int cmd_clean(int argc, const char **argv, const char *prefix);\n extern int cmd_commit(int argc, const char **argv, const char *prefix);\n extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);\ndiff --git a/git-clone.sh b/contrib/examples/git-clone.sh\nsimilarity index 100%\nrename from git-clone.sh\nrename to contrib/examples/git-clone.sh\ndiff --git a/git.c b/git.c\nindex 1e3eb10..7b474d2 100644\n--- a/git.c\n+++ b/git.c\n@@ -285,6 +285,7 @@ static void handle_internal_command(int argc, const char **argv)\n \t\t{ \"check-attr\", cmd_check_attr, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"cherry\", cmd_cherry, RUN_SETUP },\n \t\t{ \"cherry-pick\", cmd_cherry_pick, RUN_SETUP | NEED_WORK_TREE },\n+\t\t{ \"clone\", cmd_clone },\n \t\t{ \"clean\", cmd_clean, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"commit\", cmd_commit, RUN_SETUP | NEED_WORK_TREE },\n \t\t{ \"commit-tree\", cmd_commit_tree, RUN_SETUP },\ndiff --git a/t/t5701-clone-local.sh b/t/t5701-clone-local.sh\nindex 8dfaaa4..7cfe716 100755\n--- a/t/t5701-clone-local.sh\n+++ b/t/t5701-clone-local.sh\n@@ -83,14 +83,14 @@ test_expect_success 'bundle clone without .bundle suffix' '\n \tgit fetch\n '\n \n-test_expect_success 'bundle clone with .bundle suffix' '\n+test_expect_failure 'bundle clone with .bundle suffix' '\n \tcd \"$D\" &&\n \tgit clone b1.bundle &&\n \tcd b1 &&\n \tgit fetch\n '\n \n-test_expect_success 'bundle clone from b4' '\n+test_expect_failure 'bundle clone from b4' '\n \tcd \"$D\" &&\n \tgit clone b4 bdl &&\n \tcd bdl &&\n@@ -108,7 +108,7 @@ test_expect_success 'bundle clone from b4.bundle that does not exist' '\n \tfi\n '\n \n-test_expect_success 'bundle clone with nonexistent HEAD' '\n+test_expect_failure 'bundle clone with nonexistent HEAD' '\n \tcd \"$D\" &&\n \tgit clone b2.bundle b2 &&\n \tcd b2 &&\n-- \n1.5.4.3.327.g614d7.dirty"},{"id":"71528","messageId":"alpine.LSU.1.00.0803092227230.3975@racer.site","threadId":"12592","inReplyTo":"alpine.LNX.1.00.0803081803360.19665@iabervon.org","subject":"[PATCH] builtin-clone: fix for new unpack_trees() semantics","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-03-09T21:30:35Z","receivedAt":"2008-03-09T21:30:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"In git.git's \"next\" branch, unpack_trees() must specify source and target \nindex.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n\n\tTo be squashed into 10/11\n\n builtin-clone.c |    2 ++\n 1 files changed, 2 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex e4047ed..3890e12 100644\n--- a/builtin-clone.c\n+++ b/builtin-clone.c\n@@ -534,6 +534,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\topts.verbose_update = !option_quiet;\n \t\topts.merge = 1;\n \t\topts.fn = twoway_merge;\n+\t\topts.src_index = &the_index;\n+\t\topts.dst_index = &the_index;\n \n \t\ttree = parse_tree_indirect(remote_head->old_sha1);\n \t\tparse_tree(tree);\n-- \n1.5.4.3.653.gbc310\n"},{"id":"71532","messageId":"alpine.LNX.1.00.0803091829100.19665@iabervon.org","threadId":"12592","inReplyTo":"alpine.LSU.1.00.0803092227230.3975@racer.site","subject":"Re: [PATCH] builtin-clone: fix for new unpack_trees() semantics","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2008-03-09T22:35:02Z","receivedAt":"2008-03-09T22:35:02Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Sun, 9 Mar 2008, Johannes Schindelin wrote:\n\n> In git.git's \"next\" branch, unpack_trees() must specify source and target \n> index.\n> \n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n> \n> \tTo be squashed into 10/11\n> \n>  builtin-clone.c |    2 ++\n>  1 files changed, 2 insertions(+), 0 deletions(-)\n> \n> diff --git a/builtin-clone.c b/builtin-clone.c\n> index e4047ed..3890e12 100644\n> --- a/builtin-clone.c\n> +++ b/builtin-clone.c\n> @@ -534,6 +534,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n>  \t\topts.verbose_update = !option_quiet;\n>  \t\topts.merge = 1;\n>  \t\topts.fn = twoway_merge;\n> +\t\topts.src_index = &the_index;\n> +\t\topts.dst_index = &the_index;\n\nActually, I think the sensible thing is to just not do a merge here, since \nwe know there's no index beforehand and the two trees are the same.\n\nI think the odd twoway merge of two copies of HEAD is just an artifact of \nclone originally just doing \"git checkout HEAD\", and that got translated \nvarious times failing to notice the special cases.\n\n\t-Daniel\n*This .sig left intentionally blank*\n"},{"id":"71535","messageId":"alpine.LSU.1.00.0803092354310.3975@racer.site","threadId":"12592","inReplyTo":"alpine.LNX.1.00.0803091829100.19665@iabervon.org","subject":"Re: [PATCH] builtin-clone: fix for new unpack_trees() semantics","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-03-09T22:55:03Z","receivedAt":"2008-03-09T22:55:03Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 9 Mar 2008, Daniel Barkalow wrote:\n\n> On Sun, 9 Mar 2008, Johannes Schindelin wrote:\n> \n> > In git.git's \"next\" branch, unpack_trees() must specify source and target \n> > index.\n> > \n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> > \n> > \tTo be squashed into 10/11\n> > \n> >  builtin-clone.c |    2 ++\n> >  1 files changed, 2 insertions(+), 0 deletions(-)\n> > \n> > diff --git a/builtin-clone.c b/builtin-clone.c\n> > index e4047ed..3890e12 100644\n> > --- a/builtin-clone.c\n> > +++ b/builtin-clone.c\n> > @@ -534,6 +534,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n> >  \t\topts.verbose_update = !option_quiet;\n> >  \t\topts.merge = 1;\n> >  \t\topts.fn = twoway_merge;\n> > +\t\topts.src_index = &the_index;\n> > +\t\topts.dst_index = &the_index;\n> \n> Actually, I think the sensible thing is to just not do a merge here, since \n> we know there's no index beforehand and the two trees are the same.\n> \n> I think the odd twoway merge of two copies of HEAD is just an artifact of \n> clone originally just doing \"git checkout HEAD\", and that got translated \n> various times failing to notice the special cases.\n\nOkay, but would oneway_merge not want to write the index, too (rightfully \nso)?\n\nCiao,\nDscho\n"},{"id":"71536","messageId":"alpine.LNX.1.00.0803091857540.19665@iabervon.org","threadId":"12592","inReplyTo":"alpine.LSU.1.00.0803092354310.3975@racer.site","subject":"Re: [PATCH] builtin-clone: fix for new unpack_trees() semantics","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2008-03-09T23:02:28Z","receivedAt":"2008-03-09T23:02:28Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Sun, 9 Mar 2008, Johannes Schindelin wrote:\n\n> Hi,\n> \n> On Sun, 9 Mar 2008, Daniel Barkalow wrote:\n> \n> > On Sun, 9 Mar 2008, Johannes Schindelin wrote:\n> > \n> > > In git.git's \"next\" branch, unpack_trees() must specify source and target \n> > > index.\n> > > \n> > > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > > ---\n> > > \n> > > \tTo be squashed into 10/11\n> > > \n> > >  builtin-clone.c |    2 ++\n> > >  1 files changed, 2 insertions(+), 0 deletions(-)\n> > > \n> > > diff --git a/builtin-clone.c b/builtin-clone.c\n> > > index e4047ed..3890e12 100644\n> > > --- a/builtin-clone.c\n> > > +++ b/builtin-clone.c\n> > > @@ -534,6 +534,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n> > >  \t\topts.verbose_update = !option_quiet;\n> > >  \t\topts.merge = 1;\n> > >  \t\topts.fn = twoway_merge;\n> > > +\t\topts.src_index = &the_index;\n> > > +\t\topts.dst_index = &the_index;\n> > \n> > Actually, I think the sensible thing is to just not do a merge here, since \n> > we know there's no index beforehand and the two trees are the same.\n> > \n> > I think the odd twoway merge of two copies of HEAD is just an artifact of \n> > clone originally just doing \"git checkout HEAD\", and that got translated \n> > various times failing to notice the special cases.\n> \n> Okay, but would oneway_merge not want to write the index, too (rightfully \n> so)?\n\nI'm thinking:\n\n  memset(&opts, 0, sizeof opts);\n  opts.update = 1;\n  opts.verbose_update = !option_quiet;\n  opts.dst_index = &the_index;\n\n  init_tree_desc(&t[0], tree->buffer, tree->size);\n  unpack_trees(1, t, &opts);\n\nThat is, write it, but not read it, and only have one tree.\n\n\t-Daniel\n*This .sig left intentionally blank*\n"},{"id":"71538","messageId":"alpine.LSU.1.00.0803100005060.3975@racer.site","threadId":"12592","inReplyTo":"alpine.LNX.1.00.0803091857540.19665@iabervon.org","subject":"Re: [PATCH] builtin-clone: fix for new unpack_trees() semantics","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-03-09T23:05:39Z","receivedAt":"2008-03-09T23:05:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 9 Mar 2008, Daniel Barkalow wrote:\n\n> On Sun, 9 Mar 2008, Johannes Schindelin wrote:\n> \n> > On Sun, 9 Mar 2008, Daniel Barkalow wrote:\n> > \n> > > On Sun, 9 Mar 2008, Johannes Schindelin wrote:\n> > > \n> > > > In git.git's \"next\" branch, unpack_trees() must specify source and target \n> > > > index.\n> > > > \n> > > > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > > > ---\n> > > > \n> > > > \tTo be squashed into 10/11\n> > > > \n> > > >  builtin-clone.c |    2 ++\n> > > >  1 files changed, 2 insertions(+), 0 deletions(-)\n> > > > \n> > > > diff --git a/builtin-clone.c b/builtin-clone.c\n> > > > index e4047ed..3890e12 100644\n> > > > --- a/builtin-clone.c\n> > > > +++ b/builtin-clone.c\n> > > > @@ -534,6 +534,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n> > > >  \t\topts.verbose_update = !option_quiet;\n> > > >  \t\topts.merge = 1;\n> > > >  \t\topts.fn = twoway_merge;\n> > > > +\t\topts.src_index = &the_index;\n> > > > +\t\topts.dst_index = &the_index;\n> > > \n> > > Actually, I think the sensible thing is to just not do a merge here, since \n> > > we know there's no index beforehand and the two trees are the same.\n> > > \n> > > I think the odd twoway merge of two copies of HEAD is just an artifact of \n> > > clone originally just doing \"git checkout HEAD\", and that got translated \n> > > various times failing to notice the special cases.\n> > \n> > Okay, but would oneway_merge not want to write the index, too (rightfully \n> > so)?\n> \n> I'm thinking:\n> \n>   memset(&opts, 0, sizeof opts);\n>   opts.update = 1;\n>   opts.verbose_update = !option_quiet;\n>   opts.dst_index = &the_index;\n> \n>   init_tree_desc(&t[0], tree->buffer, tree->size);\n>   unpack_trees(1, t, &opts);\n> \n> That is, write it, but not read it, and only have one tree.\n\nYes.  And this sets dst_index (what I tried to hint at with my patch).\n\nCiao,\nDscho\n"},{"id":"71539","messageId":"alpine.LNX.1.00.0803091911010.19665@iabervon.org","threadId":"12592","inReplyTo":"alpine.LSU.1.00.0803100005060.3975@racer.site","subject":"Re: [PATCH] builtin-clone: fix for new unpack_trees() semantics","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2008-03-09T23:14:55Z","receivedAt":"2008-03-09T23:14:55Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Mon, 10 Mar 2008, Johannes Schindelin wrote:\n\n> Hi,\n> \n> On Sun, 9 Mar 2008, Daniel Barkalow wrote:\n> \n> > On Sun, 9 Mar 2008, Johannes Schindelin wrote:\n> > \n> > > On Sun, 9 Mar 2008, Daniel Barkalow wrote:\n> > > \n> > > > On Sun, 9 Mar 2008, Johannes Schindelin wrote:\n> > > > \n> > > > > In git.git's \"next\" branch, unpack_trees() must specify source and target \n> > > > > index.\n> > > > > \n> > > > > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > > > > ---\n> > > > > \n> > > > > \tTo be squashed into 10/11\n> > > > > \n> > > > >  builtin-clone.c |    2 ++\n> > > > >  1 files changed, 2 insertions(+), 0 deletions(-)\n> > > > > \n> > > > > diff --git a/builtin-clone.c b/builtin-clone.c\n> > > > > index e4047ed..3890e12 100644\n> > > > > --- a/builtin-clone.c\n> > > > > +++ b/builtin-clone.c\n> > > > > @@ -534,6 +534,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n> > > > >  \t\topts.verbose_update = !option_quiet;\n> > > > >  \t\topts.merge = 1;\n> > > > >  \t\topts.fn = twoway_merge;\n> > > > > +\t\topts.src_index = &the_index;\n> > > > > +\t\topts.dst_index = &the_index;\n> > > > \n> > > > Actually, I think the sensible thing is to just not do a merge here, since \n> > > > we know there's no index beforehand and the two trees are the same.\n> > > > \n> > > > I think the odd twoway merge of two copies of HEAD is just an artifact of \n> > > > clone originally just doing \"git checkout HEAD\", and that got translated \n> > > > various times failing to notice the special cases.\n> > > \n> > > Okay, but would oneway_merge not want to write the index, too (rightfully \n> > > so)?\n> > \n> > I'm thinking:\n> > \n> >   memset(&opts, 0, sizeof opts);\n> >   opts.update = 1;\n> >   opts.verbose_update = !option_quiet;\n> >   opts.dst_index = &the_index;\n> > \n> >   init_tree_desc(&t[0], tree->buffer, tree->size);\n> >   unpack_trees(1, t, &opts);\n> > \n> > That is, write it, but not read it, and only have one tree.\n> \n> Yes.  And this sets dst_index (what I tried to hint at with my patch).\n\nOh, yes. Your patch is what prompted me to fix that part (and to find that \nLinus's unpack_trees was in next now). But seeing src_index in your patch \nmade me wonder what it wanted a source index for anyway, since this is the \nfirst index we've had in this repo.\n\n\t-Daniel\n*This .sig left intentionally blank*\n"},{"id":"71571","messageId":"alpine.LSU.1.00.0803101157300.3975@racer.site","threadId":"12592","inReplyTo":"alpine.LNX.1.00.0803091911010.19665@iabervon.org","subject":"Re: [PATCH] builtin-clone: fix for new unpack_trees() semantics","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-03-10T10:58:50Z","receivedAt":"2008-03-10T10:58:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Sun, 9 Mar 2008, Daniel Barkalow wrote:\n\n> On Mon, 10 Mar 2008, Johannes Schindelin wrote:\n> \n> > Yes.  And this sets dst_index (what I tried to hint at with my patch).\n> \n> Oh, yes. Your patch is what prompted me to fix that part (and to find \n> that Linus's unpack_trees was in next now). But seeing src_index in your \n> patch made me wonder what it wanted a source index for anyway, since \n> this is the first index we've had in this repo.\n\nWell, stupid copy-'n-paste.  Well, not all that stupid: because of the \ntwoway_merge (and because the segmentation fault was in a line accessing \nsrc_index), I assumed that the src_index is needed.\n\nBut your explanation that oneway_merge is all we want makes tons of sense.\n\nCiao,\nDscho\n"}]}