{"thread":{"id":"11242","subject":"[PATCH] builtin-clone: Implement git clone as a builtin command.","startedAt":"2007-12-11T19:57:12Z","lastAt":"2007-12-24T01:52:32Z","messageCount":13,"participants":["Kristian Høgsberg","Daniel Barkalow","Junio C Hamano","Johannes Schindelin","J. Bruce Fields"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"62776","messageId":"20071211195712.GA3865@bitplanet.net","threadId":"11242","inReplyTo":null,"subject":"[PATCH] builtin-clone: Implement git clone as a builtin command.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-12-11T19:57:12Z","receivedAt":"2007-12-11T19:57:12Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"Still work-in-progress, local clones and --reference not fully functional.\n---\n\nOk, don't flame me, I know this isn't appropriate at the moment with\nstabilization for 1.5.4 going on, but I just wanted to post a heads up\non this work to avoid duplicate effort.  It's one big patch at this point\nand I haven't even run the test suite yet, but that will change.\n\ncheers,\nKristian\n\n Makefile                                      |    2 +-\n builtin-clone.c                               |  504 +++++++++++++++++++++++++\n builtin-init-db.c                             |  119 +++----\n builtin-rerere.c                              |   19 +-\n builtin.h                                     |    1 +\n cache.h                                       |    5 +\n git-clone.sh => contrib/examples/git-clone.sh |    0 \n copy.c                                        |   21 +\n diff.c                                        |    8 +-\n git.c                                         |    1 +\n unpack-trees.c                                |    3 +-\n unpack-trees.h                                |    1 +\n 12 files changed, 595 insertions(+), 89 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 cb1cbb1..ca42ed1 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -213,7 +213,6 @@ BASIC_LDFLAGS =\n \n SCRIPT_SH = \\\n \tgit-bisect.sh git-checkout.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@@ -327,6 +326,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..acd7beb\n--- /dev/null\n+++ b/builtin-clone.c\n@@ -0,0 +1,504 @@\n+/*\n+ * Builtin \"git clone\"\n+ *\n+ * Copyright (c) 2007 Kristian Høgsberg <krh@redhat.com>\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+\n+/*\n+ * Overall FIXMEs:\n+ *  - respect DB_ENVIRONMENT for .git/objects.\n+ *  - error path cleanup of dirs+files.\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, option_depth;\n+static char *option_template, *option_reference;\n+static char *option_origin = \"origin\";\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_INTEGER(0, \"depth\", &option_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+\t\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 (*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+static void\n+write_alternates_file(const char *repo, const char *reference)\n+{\n+\tchar *file;\n+\tchar *alternates;\n+\tint fd;\n+\n+\tfile = mkpath(\"%s/objects/info/alternates\", repo);\n+\tfd = open(file, O_CREAT | O_WRONLY, 0666);\n+\tif (fd < 0)\n+\t\tdie(\"failed to create %s\", file);\n+\talternates = mkpath(\"%s/objects\\n\", reference);\n+\twrite_or_die(fd, alternates, strlen(alternates));\n+\tif (close(fd))\n+\t\tdie(\"could not close %s\", file);\n+}\n+\n+static int\n+setup_tmp_ref(const char *refname,\n+\t      const unsigned char *sha1, int flags, void *cb_data)\n+{\n+\tconst char *ref_temp = cb_data;\n+\tchar *path;\n+\tstruct lock_file lk;\n+\tstruct ref_lock *rl;\n+\n+\t/*\n+\n+\techo \"$ref_git/objects\" >\"$GIT_DIR/objects/info/alternates\"\n+\t(\n+\t\tGIT_DIR=\"$ref_git\" git for-each-ref \\\n+\t\t\t--format='%(objectname) %(*objectname)'\n+\t) |\n+\twhile read a b\n+\tdo\n+\t\ttest -z \"$a\" ||\n+\t\tgit update-ref \"refs/reference-tmp/$a\" \"$a\"\n+\t\ttest -z \"$b\" ||\n+\t\tgit update-ref \"refs/reference-tmp/$b\" \"$b\"\n+\tdone\n+\n+\t*/\n+\n+\t/* We go a bit out of way to use write_ref_sha1() here.  We\n+\t * could just write the ref file directly, since neither\n+\t * locking or reflog really matters here.  However, let's use\n+\t * the standard interface for writing refs as much as is\n+\t * possible given that get_git_dir() != the repo we're writing\n+\t * the refs in. */\n+\n+\tprintf(\"%s -> %s/%s\\n\",\n+\t       sha1_to_hex(sha1), ref_temp, sha1_to_hex(sha1));\n+\n+\tpath = mkpath(\"%s/%s\", ref_temp, sha1_to_hex(sha1));\n+\trl = xmalloc(sizeof *rl);\n+\trl->force_write = 1;\n+\trl->lk = &lk;\n+\trl->ref_name = xstrdup(sha1_to_hex(sha1));\n+\trl->orig_ref_name = xstrdup(rl->ref_name);\n+\trl->lock_fd = hold_lock_file_for_update(rl->lk, path, 1);\n+\tif (write_ref_sha1(rl, sha1, NULL) < 0)\n+\t\tdie(\"failed to write temporary ref %s\", lk.filename);\n+\n+\treturn 0;\n+}\n+\n+static char *\n+setup_reference(const char *repo)\n+{\n+\tstruct stat buf;\n+\tconst char *ref_git;\n+\tchar *ref_temp;\n+\n+\tif (!option_reference)\n+\t\treturn NULL;\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+\tset_git_dir(ref_git);\n+\n+\twrite_alternates_file(repo, ref_git);\n+\n+\tref_temp = xstrdup(mkpath(\"%s/refs/reference-tmp\", repo));\n+\tif (mkdir(ref_temp, 0777))\n+\t\tdie(\"could not create directory %s\", ref_temp);\n+\tfor_each_ref(setup_tmp_ref, (void *) ref_temp);\n+\n+\treturn ref_temp;\n+}\n+\n+static void\n+cleanup_reference(char *ref_temp)\n+{\n+\tstruct dirent *de;\n+\tDIR *dir;\n+\n+\tif (!ref_temp)\n+\t\treturn;\n+\tdir = opendir(ref_temp);\n+\tif (!dir) {\n+\t\tif (errno == ENOENT)\n+\t\t\treturn;\n+\t\tdie(\"failed to open directory %s\", ref_temp);\n+\t}\n+\t\t\n+\twhile ((de = readdir(dir)) != NULL) {\n+\t\tif (de->d_name[0] == '.')\n+\t\t\tcontinue;\n+\t\tunlink(mkpath(\"%s/%s\", ref_temp, de->d_name));\n+\t}\n+\n+\tunlink(ref_temp);\n+\tfree(ref_temp);\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+}\t\t\n+\n+static struct ref *\n+clone_local(const char *src_repo, const char *dest_repo)\n+{\n+\tchar src[PATH_MAX];\n+\tchar dest[PATH_MAX];\n+\n+\tif (option_shared) {\n+\t\twrite_alternates_file(dest_repo, 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+\t/* FIXME: Return list of refs for src repo. */\n+\n+\treturn NULL;\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, *work_tree, *git_dir;\n+\tchar *path, *dir, *head, *ref_temp;\n+\tstruct ref *refs, *r, *remote_head, *head_points_at;\n+\tchar branch_top[256], key[256], refname[256], value[256];\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+\trepo = argv[0];\n+\tpath = get_repo_path(repo);\n+\n+\tif (argc == 2) {\n+\t\tdir = xstrdup(argv[1]);\n+\t} else {\n+\t\tdir = guess_dir_name(repo);\n+\t}\n+\n+\tif (!stat(dir, &buf))\n+\t\tdie(\"destination directory '%s' already exists.\", dir);\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+\tif (mkdir(dir, 0755))\n+\t\tdie(\"could not create repository dir '%s'.\", dir);\n+\tif (work_tree && mkdir(work_tree, 0755))\n+\t\tdie(\"could not create work tree dir '%s'.\", work_tree);\n+\n+\tif (option_bare || work_tree)\n+\t\tgit_dir = xstrdup(dir); \n+\telse\n+\t\tgit_dir = xstrdup(mkpath(\"%s/.git\", dir));\n+\n+\tinit_db(git_dir, option_template, option_quiet ? INIT_DB_QUIET : 0);\n+\n+\t/* This calls set_git_dir for the reference repo so we can get\n+\t * the refs there.  Thus, call this before calling\n+\t * set_git_dir() on the repo we're setting up. */\n+\tref_temp = setup_reference(git_dir);\n+\n+\tset_git_dir(make_absolute_path(git_dir));\n+\n+\tif (option_bare)\n+\t\tgit_config_set(\"core.bare\", \"true\");\n+\n+\tif (path != NULL) {\n+\t\trefs = clone_local(path, git_dir);\n+\t} else {\n+\t\tstruct fetch_pack_args args;\n+\n+\t\targs.uploadpack = option_upload_pack;\n+\t\targs.quiet = option_quiet;\n+\t\targs.fetch_all = 1;\n+\t\targs.lock_pack = 0;\n+\t\targs.keep_pack = 1;\n+\t\targs.depth = option_depth;\n+\t\targs.no_progress = 1;\n+\t\t\n+\t\trefs = fetch_pack(&args, argv[0], 0, NULL, NULL);\n+\t}\n+\n+\tcleanup_reference(ref_temp);\n+\n+\tif (option_bare)\n+\t\tstrcpy(branch_top, \"heads\");\n+\telse\n+\t\tsnprintf(branch_top, sizeof branch_top,\n+\t\t\t \"refs/remotes/%s\", option_origin);\n+\n+\tremote_head = NULL;\n+\tfor (r = refs; r; r = r->next) {\n+\t\tif (!strcmp(r->name, \"HEAD\")) {\n+\t\t\tremote_head = r;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (!prefixcmp(r->name, \"refs/heads/\"))\n+\t\t\tsnprintf(refname, sizeof refname,\n+\t\t\t\t \"%s/%s\", branch_top, r->name + 11);\n+\t\telse if (!prefixcmp(r->name, \"refs/tags/\"))\n+\t\t\tsnprintf(refname, sizeof refname,\n+\t\t\t\t \"refs/tags/%s\", r->name + 10);\n+\t\telse\n+\t\t\tcontinue;\n+\n+\t\tupdate_ref(\"clone from $repo\",\n+\t\t\t   refname, r->old_sha1, NULL, 0, DIE_ON_ERR);\n+\t}\n+\n+\tif (option_bare)\n+\t\treturn 0;\n+\n+\t/* Is HEAD always first?  If so, we could do this in the loop above. */\n+\thead_points_at = NULL;\n+\tfor (r = 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\thead_points_at = r;\n+\t\t\tprintf(\"head points at %s\\n\", r->name);\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\n+\tif (strrchr(head_points_at->name, '/'))\n+\t\thead = strrchr(head_points_at->name, '/') + 1;\n+\telse\n+\t\thead = head_points_at->name;\n+\n+\t/* FIXME: What about the \"Uh-oh, the remote told us...\" case? */\n+\n+\tsnprintf(key, sizeof key, \"remote.%s.remote\", option_origin);\n+\tgit_config_set(key, repo);\n+\tsnprintf(key, sizeof key, \"remote.%s.fetch\", option_origin);\n+\tsnprintf(value, sizeof value, \"+refs/heads/*:%s/*\", branch_top);\n+\n+\tgit_config_set_multivar(key, value, \"^$\", 0);\n+\n+\tif (head_points_at) {\n+\t\t/* Local default branch */\n+\t\tcreate_symref(\"HEAD\", head_points_at->name, NULL);\n+\n+\t\t/* Tracking branch for the primary branch at the remote. */\n+\t\tupdate_ref(NULL, \"HEAD\", head_points_at->old_sha1,\n+\t\t\t   NULL, 0, DIE_ON_ERR);\n+\t/*\n+\t\trm -f \"refs/remotes/$origin/HEAD\"\n+\t\tgit symbolic-ref \"refs/remotes/$origin/HEAD\" \\\n+\t\t\t\"refs/remotes/$origin/$head_points_at\" &&\n+\t*/\n+\n+\t\tsnprintf(key, sizeof key, \"branch.%s.remote\", head);\n+\t\tgit_config_set(key, option_origin);\n+\t\tsnprintf(key, sizeof key, \"branch.%s.merge\", head);\n+\t\tgit_config_set(key, head_points_at->name);\n+\t} else {\n+\t\t/* Source had detached HEAD pointing nowhere. */\n+\t\tupdate_ref(\"clone from $repo\", \"HEAD\", remote_head->old_sha1,\n+\t\t\t   NULL, REF_NODEREF, DIE_ON_ERR);\n+\t}\n+\n+\tif (!option_no_checkout) {\n+\t\tchar base_dir[PATH_MAX];\n+\t\tstruct lock_file 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\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\t/* FIXME: Handle basedir ends in '/' */\n+\t\tsnprintf(base_dir, sizeof base_dir, \"%s/\",\n+\t\t\t work_tree ? work_tree : dir);\n+\t\topts.base_dir = base_dir;\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    close(fd) || commit_locked_index(&lock_file))\n+\t\t\tdie(\"unable to write new index file\");\n+\t}\n+\t\n+\treturn 0;\n+}\ndiff --git a/builtin-init-db.c b/builtin-init-db.c\nindex e1393b8..18abc43 100644\n--- a/builtin-init-db.c\n+++ b/builtin-init-db.c\n@@ -29,27 +29,6 @@ static void safe_create_dir(const char *dir, int share)\n \t\tdie(\"Could not make %s writable by group\\n\", dir);\n }\n \n-static int copy_file(const char *dst, const char *src, int mode)\n-{\n-\tint fdi, fdo, status;\n-\n-\tmode = (mode & 0111) ? 0777 : 0666;\n-\tif ((fdi = open(src, O_RDONLY)) < 0)\n-\t\treturn fdi;\n-\tif ((fdo = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) {\n-\t\tclose(fdi);\n-\t\treturn fdo;\n-\t}\n-\tstatus = copy_fd(fdi, fdo);\n-\tif (close(fdo) != 0)\n-\t\treturn error(\"%s: write error: %s\", dst, strerror(errno));\n-\n-\tif (!status && adjust_shared_perm(dst))\n-\t\treturn -1;\n-\n-\treturn status;\n-}\n-\n static void copy_templates_1(char *path, int baselen,\n \t\t\t     char *template, int template_baselen,\n \t\t\t     DIR *dir)\n@@ -330,49 +309,11 @@ static void guess_repository_type(const char *git_dir)\n \treturn;\n }\n \n-static const char init_db_usage[] =\n-\"git-init [-q | --quiet] [--template=<template-directory>] [--shared]\";\n-\n-/*\n- * If you want to, you can share the DB area with any number of branches.\n- * That has advantages: you can save space by sharing all the SHA1 objects.\n- * On the other hand, it might just make lookup slower and messier. You\n- * be the judge.  The default case is to have one DB per managed directory.\n- */\n-int cmd_init_db(int argc, const char **argv, const char *prefix)\n+int init_db(const char *git_dir, const char *template_dir, unsigned int flags)\n {\n-\tconst char *git_dir;\n \tconst char *sha1_dir;\n-\tconst char *template_dir = NULL;\n \tchar *path;\n-\tint len, i, reinit;\n-\tint quiet = 0;\n-\n-\tfor (i = 1; i < argc; i++, argv++) {\n-\t\tconst char *arg = argv[1];\n-\t\tif (!prefixcmp(arg, \"--template=\"))\n-\t\t\ttemplate_dir = arg+11;\n-\t\telse if (!strcmp(arg, \"--shared\"))\n-\t\t\tshared_repository = PERM_GROUP;\n-\t\telse if (!prefixcmp(arg, \"--shared=\"))\n-\t\t\tshared_repository = git_config_perm(\"arg\", arg+9);\n-\t\telse if (!strcmp(arg, \"-q\") || !strcmp(arg, \"--quiet\"))\n-\t\t        quiet = 1;\n-\t\telse\n-\t\t\tusage(init_db_usage);\n-\t}\n-\n-\t/*\n-\t * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR\n-\t * without --bare.  Catch the error early.\n-\t */\n-\tgit_dir = getenv(GIT_DIR_ENVIRONMENT);\n-\tif ((!git_dir || is_bare_repository_cfg == 1)\n-\t    && getenv(GIT_WORK_TREE_ENVIRONMENT))\n-\t\tdie(\"%s (or --work-tree=<directory>) not allowed without \"\n-\t\t    \"specifying %s (or --git-dir=<directory>)\",\n-\t\t    GIT_WORK_TREE_ENVIRONMENT,\n-\t\t    GIT_DIR_ENVIRONMENT);\n+\tint len, reinit;\n \n \tguess_repository_type(git_dir);\n \n@@ -388,7 +329,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t/*\n \t * Set up the default .git directory contents\n \t */\n-\tgit_dir = getenv(GIT_DIR_ENVIRONMENT);\n \tif (!git_dir)\n \t\tgit_dir = DEFAULT_GIT_DIR_ENVIRONMENT;\n \tsafe_create_dir(git_dir, 0);\n@@ -403,9 +343,13 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \treinit = create_default_files(git_dir, template_dir);\n \n \t/*\n-\t * And set up the object store.\n+\t * And set up the object store.  Don't use\n+\t * get_object_directory() here, since we're initializing\n+\t * relative to git_dir, not $GIT_DIR.\n \t */\n-\tsha1_dir = get_object_directory();\n+\tsha1_dir = getenv(DB_ENVIRONMENT);\n+\tif (!sha1_dir)\n+\t\tsha1_dir = mkpath(\"%s/objects\", git_dir);\n \tlen = strlen(sha1_dir);\n \tpath = xmalloc(len + 40);\n \tmemcpy(path, sha1_dir, len);\n@@ -427,7 +371,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t\tgit_config_set(\"receive.denyNonFastforwards\", \"true\");\n \t}\n \n-\tif (!quiet)\n+\tif (!(flags & INIT_DB_QUIET))\n \t\tprintf(\"%s%s Git repository in %s/\\n\",\n \t\t       reinit ? \"Reinitialized existing\" : \"Initialized empty\",\n \t\t       shared_repository ? \" shared\" : \"\",\n@@ -435,3 +379,48 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \n \treturn 0;\n }\n+\n+static const char init_db_usage[] =\n+\"git-init [-q | --quiet] [--template=<template-directory>] [--shared]\";\n+\n+/*\n+ * If you want to, you can share the DB area with any number of branches.\n+ * That has advantages: you can save space by sharing all the SHA1 objects.\n+ * On the other hand, it might just make lookup slower and messier. You\n+ * be the judge.  The default case is to have one DB per managed directory.\n+ */\n+int cmd_init_db(int argc, const char **argv, const char *prefix)\n+{\n+\tconst char *git_dir;\n+\tconst char *template_dir = NULL;\n+\tunsigned int flags = 0;\n+\tint i;\n+\n+\tfor (i = 1; i < argc; i++, argv++) {\n+\t\tconst char *arg = argv[1];\n+\t\tif (!prefixcmp(arg, \"--template=\"))\n+\t\t\ttemplate_dir = arg+11;\n+\t\telse if (!strcmp(arg, \"--shared\"))\n+\t\t\tshared_repository = PERM_GROUP;\n+\t\telse if (!prefixcmp(arg, \"--shared=\"))\n+\t\t\tshared_repository = git_config_perm(\"arg\", arg+9);\n+\t\telse if (!strcmp(arg, \"-q\") || !strcmp(arg, \"--quiet\"))\n+\t\t        flags |= INIT_DB_QUIET;\n+\t\telse\n+\t\t\tusage(init_db_usage);\n+\t}\n+\n+\t/*\n+\t * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR\n+\t * without --bare.  Catch the error early.\n+\t */\n+\tgit_dir = getenv(GIT_DIR_ENVIRONMENT);\n+\tif ((!git_dir || is_bare_repository_cfg == 1)\n+\t    && getenv(GIT_WORK_TREE_ENVIRONMENT))\n+\t\tdie(\"%s (or --work-tree=<directory>) not allowed without \"\n+\t\t    \"specifying %s (or --git-dir=<directory>)\",\n+\t\t    GIT_WORK_TREE_ENVIRONMENT,\n+\t\t    GIT_DIR_ENVIRONMENT);\n+\n+\treturn init_db(git_dir, template_dir, flags);\n+}\ndiff --git a/builtin-rerere.c b/builtin-rerere.c\nindex 7449323..2d83524 100644\n--- a/builtin-rerere.c\n+++ b/builtin-rerere.c\n@@ -267,23 +267,6 @@ static int diff_two(const char *file1, const char *label1,\n \treturn 0;\n }\n \n-static int copy_file(const char *src, const char *dest)\n-{\n-\tFILE *in, *out;\n-\tchar buffer[32768];\n-\tint count;\n-\n-\tif (!(in = fopen(src, \"r\")))\n-\t\treturn error(\"Could not open %s\", src);\n-\tif (!(out = fopen(dest, \"w\")))\n-\t\treturn error(\"Could not open %s\", dest);\n-\twhile ((count = fread(buffer, 1, sizeof(buffer), in)))\n-\t\tfwrite(buffer, 1, count, out);\n-\tfclose(in);\n-\tfclose(out);\n-\treturn 0;\n-}\n-\n static int do_plain_rerere(struct path_list *rr, int fd)\n {\n \tstruct path_list conflict = { NULL, 0, 0, 1 };\n@@ -343,7 +326,7 @@ static int do_plain_rerere(struct path_list *rr, int fd)\n \t\t\tcontinue;\n \n \t\tfprintf(stderr, \"Recorded resolution for '%s'.\\n\", path);\n-\t\tcopy_file(path, rr_path(name, \"postimage\"));\n+\t\tcopy_file(path, rr_path(name, \"postimage\"), 0666);\n tail_optimization:\n \t\tif (i < rr->nr - 1)\n \t\t\tmemmove(rr->items + i,\ndiff --git a/builtin.h b/builtin.h\nindex cb675c4..1b9da64 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/cache.h b/cache.h\nindex 4e59646..1e29e70 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -230,6 +230,10 @@ extern const char *prefix_filename(const char *prefix, int len, const char *path\n extern void verify_filename(const char *prefix, const char *name);\n extern void verify_non_filename(const char *prefix, const char *name);\n \n+#define INIT_DB_QUIET 0x0001\n+\n+extern int init_db(const char *git_dir, const char *template_dir, unsigned int flags);\n+\n #define alloc_nr(x) (((x)+16)*3/2)\n \n /*\n@@ -588,6 +592,7 @@ extern const char *git_log_output_encoding;\n /* IO helper functions */\n extern void maybe_flush_or_die(FILE *, const char *);\n extern int copy_fd(int ifd, int ofd);\n+extern int copy_file(const char *dst, const char *src, int mode);\n extern int read_in_full(int fd, void *buf, size_t count);\n extern int write_in_full(int fd, const void *buf, size_t count);\n extern void write_or_die(int fd, const void *buf, size_t count);\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/copy.c b/copy.c\nindex c225d1b..afc4fbf 100644\n--- a/copy.c\n+++ b/copy.c\n@@ -34,3 +34,24 @@ int copy_fd(int ifd, int ofd)\n \tclose(ifd);\n \treturn 0;\n }\n+\n+int copy_file(const char *dst, const char *src, int mode)\n+{\n+\tint fdi, fdo, status;\n+\n+\tmode = (mode & 0111) ? 0777 : 0666;\n+\tif ((fdi = open(src, O_RDONLY)) < 0)\n+\t\treturn fdi;\n+\tif ((fdo = open(dst, O_WRONLY | O_CREAT | O_EXCL, mode)) < 0) {\n+\t\tclose(fdi);\n+\t\treturn fdo;\n+\t}\n+\tstatus = copy_fd(fdi, fdo);\n+\tif (close(fdo) != 0)\n+\t\treturn error(\"%s: write error: %s\", dst, strerror(errno));\n+\n+\tif (!status && adjust_shared_perm(dst))\n+\t\treturn -1;\n+\n+\treturn status;\n+}\ndiff --git a/diff.c b/diff.c\nindex 5175950..0af5b81 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -258,8 +258,8 @@ static void print_line_count(int count)\n \t}\n }\n \n-static void copy_file(int prefix, const char *data, int size,\n-\t\tconst char *set, const char *reset)\n+static void copy_file_with_prefix(int prefix, const char *data, int size,\n+\t\t\t\t  const char *set, const char *reset)\n {\n \tint ch, nl_just_seen = 1;\n \twhile (0 < size--) {\n@@ -310,9 +310,9 @@ static void emit_rewrite_diff(const char *name_a,\n \tprint_line_count(lc_b);\n \tprintf(\" @@%s\\n\", reset);\n \tif (lc_a)\n-\t\tcopy_file('-', one->data, one->size, old, reset);\n+\t\tcopy_file_with_prefix('-', one->data, one->size, old, reset);\n \tif (lc_b)\n-\t\tcopy_file('+', two->data, two->size, new, reset);\n+\t\tcopy_file_with_prefix('+', two->data, two->size, new, reset);\n }\n \n static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)\ndiff --git a/git.c b/git.c\nindex f406c4b..c8dfb6d 100644\n--- a/git.c\n+++ b/git.c\n@@ -302,6 +302,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/unpack-trees.c b/unpack-trees.c\nindex e9eb795..752278a 100644\n--- a/unpack-trees.c\n+++ b/unpack-trees.c\n@@ -338,7 +338,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options\n \tmemset(&df_conflict_list, 0, sizeof(df_conflict_list));\n \tdf_conflict_list.next = &df_conflict_list;\n \tmemset(&state, 0, sizeof(state));\n-\tstate.base_dir = \"\";\n+\tstate.base_dir = o->base_dir ? o->base_dir : \"\";\n+\tstate.base_dir_len = strlen(state.base_dir);\n \tstate.force = 1;\n \tstate.quiet = 1;\n \tstate.refresh_cache = 1;\ndiff --git a/unpack-trees.h b/unpack-trees.h\nindex 5517faa..15b2ed9 100644\n--- a/unpack-trees.h\n+++ b/unpack-trees.h\n@@ -17,6 +17,7 @@ struct unpack_trees_options {\n \tint verbose_update;\n \tint aggressive;\n \tconst char *prefix;\n+\tconst char *base_dir;\n \tint pos;\n \tstruct dir_struct *dir;\n \tmerge_fn_t fn;\n-- \n1.5.3.4\n"},{"id":"62800","messageId":"Pine.LNX.4.64.0712111549490.5349@iabervon.org","threadId":"11242","inReplyTo":"20071211195712.GA3865@bitplanet.net","subject":"Re: [PATCH] builtin-clone: Implement git clone as a builtin command.","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2007-12-11T20:59:13Z","receivedAt":"2007-12-11T20:59:13Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Tue, 11 Dec 2007, Kristian Høgsberg wrote:\n\n> Ok, don't flame me, I know this isn't appropriate at the moment with\n> stabilization for 1.5.4 going on, but I just wanted to post a heads up\n> on this work to avoid duplicate effort.  It's one big patch at this point\n> and I haven't even run the test suite yet, but that will change.\n\nIs that why you misspelled Junio's email address? :) \n\nAlso as a heads-up, I've got a builtin-checkout that I've got passing all \nthe tests (plus a few to test stuff I originally hadn't implemented). This \nmostly involved correcting the \"interesting\" states that unpack_trees() \ncan leave the index in memory when it returns and figuring out how the \nmerge code works. I can send it off for review and testing to people who \nare interested and don't have other things they should be doing instead.\n\n\t-Daniel\n*This .sig left intentionally blank*"},{"id":"62831","messageId":"1197416286.7552.4.camel@hinata.boston.redhat.com","threadId":"11242","inReplyTo":"Pine.LNX.4.64.0712111549490.5349@iabervon.org","subject":"Re: [PATCH] builtin-clone: Implement git clone as a builtin command.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-12-11T23:38:06Z","receivedAt":"2007-12-11T23:38:06Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"On Tue, 2007-12-11 at 15:59 -0500, Daniel Barkalow wrote:\n> On Tue, 11 Dec 2007, Kristian Høgsberg wrote:\n> \n> > Ok, don't flame me, I know this isn't appropriate at the moment with\n> > stabilization for 1.5.4 going on, but I just wanted to post a heads up\n> > on this work to avoid duplicate effort.  It's one big patch at this point\n> > and I haven't even run the test suite yet, but that will change.\n> \n> Is that why you misspelled Junio's email address? :) \n\nHehe, yeah, do not mess with maintainers in release mode :)\n\n> Also as a heads-up, I've got a builtin-checkout that I've got passing all \n> the tests (plus a few to test stuff I originally hadn't implemented). This \n> mostly involved correcting the \"interesting\" states that unpack_trees() \n> can leave the index in memory when it returns and figuring out how the \n> merge code works. I can send it off for review and testing to people who \n> are interested and don't have other things they should be doing instead.\n\nThanks, that's useful, I was already considering what to do next.  I\nwouldn't mind having a look, but maybe it's better to not discuss new\nfeatures on the list at this point.  I appreciate the heads up though.\n\ncheers,\nKristian\n"},{"id":"62847","messageId":"7vejdsbo7d.fsf@gitster.siamese.dyndns.org","threadId":"11242","inReplyTo":"1197416286.7552.4.camel@hinata.boston.redhat.com","subject":"Re: [PATCH] builtin-clone: Implement git clone as a builtin command.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-12-12T03:12:54Z","receivedAt":"2007-12-12T03:12:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Kristian Høgsberg <krh@redhat.com> writes:\n\n> On Tue, 2007-12-11 at 15:59 -0500, Daniel Barkalow wrote:\n>> On Tue, 11 Dec 2007, Kristian Høgsberg wrote:\n>> \n>> > Ok, don't flame me, I know this isn't appropriate at the moment with\n>> > stabilization for 1.5.4 going on, but I just wanted to post a heads up\n>> > on this work to avoid duplicate effort.  It's one big patch at this point\n>> > and I haven't even run the test suite yet, but that will change.\n>> \n>> Is that why you misspelled Junio's email address? :) \n>\n> Hehe, yeah, do not mess with maintainers in release mode :)\n\nActually this is a bit unfortunate, regardless of everybody being in\nrelease and bugfix only mode.\n\nI was hoping that the evolution path for clone would be to first make it\na very thin wrapper around:\n\n\tgit init\n        git remote add -f\n        git checkout\n\nsequence.  Currently, the \"origin\" repository is not quite equal to\nother remotes added with \"git remote add\", but if we enhance \"git remote\nadd\" a bit, we should be able to make this happen.  This would hopefully\nlose a lot of code from git-clone.  And then after we are done with\nthat, rewrite the remaining thin wrapper in C.\n\nThere are a handful issues in that approach with the current git-remote,\nand that was why I also thought recent \"git remote in C\" by Dscho a bit\nunfortunate, as enhancements and interface fixes (both user and machine)\ntend to be much easier in scripted version.\n\nWhat the current \"git clone\" does that are not naturally expressed by\nthe above sequence are:\n\n * HEAD discovery\n\n   The code can be lifted from the scripted version and transplanted to\n   git-remote.  And to make \"origin\" and other remotes added by \"git\n   remote add\", this logic needs to be moved to \"git remote\".\n\n   However, before rewriting the \"git remote\" to C, it would be really\n   nice if we can update the native protocol so that we can reliably\n   find out which branch HEAD points at.  The current code guesses, only\n   because the native protocol does not carry that information [*1*].\n   Worse yet, even though the current code _knows_ this information when\n   going over dumb protocols, it discards it to use the same guessing\n   logic as used by the native protocol.\n\n * --shared optimization\n\n   This is a very easy addition to \"git remote add\".  You make sure that\n   the added remote repository is on a local machine, and set up\n   alternates to point at its object store.\n\n * --reference optimization\n\n   This is a bit more involved than --shared.  Half the power of this\n   optimization is coming from setting up alternates to point at another\n   local repository, which allows you not to have to _store_ duplicated\n   objects yourself, but the other half is coming from being able to lie\n   to the repository being cloned from that you have branches and tags\n   that reference repository has, even though they are not your branches\n   and tags, which allows you not to have to _download_ the objects to\n   begin with.\n\n   I think this can be added to \"git remote add\" by making --reference\n   also imply -f.  Then while \"git remote add\" sets up the new remote,\n   it can stash the borrowed refs somewhere, just like git-clone does,\n   run the git-fetch, and then remove the borrowed refs once done.\n\n * local optimization (the \"cpio\" thing)\n\n   I think this part needs to stay in git-clone even after we move the\n   above to \"git remote add\".\n\n\n[Footnote]\n\n*1* Here is a demonstration of the necessary protocol extension.\n\n-- >8 --\nImplement show-symref protocol extension.\n\nThis updates the git native \"upload-pack\" protocol to carry extra\ninformation to show which branch HEAD symref points at.  As is the other\nprotocol extension, this is enabled only when both ends of the exchange\nsupports it.\n\nThe receiving end currently does not do anything, and the logic needs to\ngo to peek-remote more than it needs to go to fetch-pack, but one has to\nstart from somewhere.\n\nSigned-off-by: Junio C Hamano <gitster@pobox.com>\n---\n builtin-fetch-pack.c |   86 +++++++++++++++++++++++++++++++++----------------\n upload-pack.c        |   25 +++++++++++++-\n 2 files changed, 81 insertions(+), 30 deletions(-)\n\ndiff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c\nindex 807fa93..e9f86d6 100644\n--- a/builtin-fetch-pack.c\n+++ b/builtin-fetch-pack.c\n@@ -32,7 +32,7 @@ static const char fetch_pack_usage[] =\n #define MAX_IN_VAIN 256\n \n static struct commit_list *rev_list;\n-static int non_common_revs, multi_ack, use_sideband;\n+static int non_common_revs, multi_ack, use_sideband, show_symref;\n \n static void rev_list_push(struct commit *commit, int mark)\n {\n@@ -141,6 +141,51 @@ static const unsigned char* get_rev(void)\n \treturn commit->object.sha1;\n }\n \n+static void handle_shallow(int fd[2])\n+{\n+\tchar line[1024];\n+\tunsigned char sha1[20];\n+\tint len;\n+\n+\twhile ((len = packet_read_line(fd[0], line, sizeof(line)))) {\n+\t\tif (!prefixcmp(line, \"shallow \")) {\n+\t\t\tif (get_sha1_hex(line + 8, sha1))\n+\t\t\t\tdie(\"invalid shallow line: %s\", line);\n+\t\t\tregister_shallow(sha1);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!prefixcmp(line, \"unshallow \")) {\n+\t\t\tif (get_sha1_hex(line + 10, sha1))\n+\t\t\t\tdie(\"invalid unshallow line: %s\", line);\n+\t\t\tif (!lookup_object(sha1))\n+\t\t\t\tdie(\"object not found: %s\", line);\n+\t\t\t/* make sure that it is parsed as shallow */\n+\t\t\tparse_object(sha1);\n+\t\t\tif (unregister_shallow(sha1))\n+\t\t\t\tdie(\"no shallow found: %s\", line);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tdie(\"expected shallow/unshallow, got %s\", line);\n+\t}\n+}\n+\n+static void handle_symref(int fd[2], struct ref *refs)\n+{\n+\tchar line[1024];\n+\tint len;\n+\n+\twhile ((len = packet_read_line(fd[0], line, sizeof(line)))) {\n+\t\tif (!prefixcmp(line, \"symref \")) {\n+\t\t\t/*\n+\t\t\t * Here you would remember what symbolic ref\n+\t\t\t * pointed at what real ref to use that\n+\t\t\t * information later.\n+\t\t\t */\n+\t\t\tfputs(line, stderr);\n+\t\t}\n+\t}\n+}\n+\n static int find_common(int fd[2], unsigned char *result_sha1,\n \t\t       struct ref *refs)\n {\n@@ -173,8 +218,9 @@ static int find_common(int fd[2], unsigned char *result_sha1,\n \t\t}\n \n \t\tif (!fetching)\n-\t\t\tpacket_write(fd[1], \"want %s%s%s%s%s%s%s\\n\",\n+\t\t\tpacket_write(fd[1], \"want %s%s%s%s%s%s%s%s\\n\",\n \t\t\t\t     sha1_to_hex(remote),\n+\t\t\t\t     (show_symref ? \" show-symref\" : \"\"),\n \t\t\t\t     (multi_ack ? \" multi_ack\" : \"\"),\n \t\t\t\t     (use_sideband == 2 ? \" side-band-64k\" : \"\"),\n \t\t\t\t     (use_sideband == 1 ? \" side-band\" : \"\"),\n@@ -193,32 +239,11 @@ static int find_common(int fd[2], unsigned char *result_sha1,\n \tif (!fetching)\n \t\treturn 1;\n \n-\tif (args.depth > 0) {\n-\t\tchar line[1024];\n-\t\tunsigned char sha1[20];\n-\t\tint len;\n-\n-\t\twhile ((len = packet_read_line(fd[0], line, sizeof(line)))) {\n-\t\t\tif (!prefixcmp(line, \"shallow \")) {\n-\t\t\t\tif (get_sha1_hex(line + 8, sha1))\n-\t\t\t\t\tdie(\"invalid shallow line: %s\", line);\n-\t\t\t\tregister_shallow(sha1);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!prefixcmp(line, \"unshallow \")) {\n-\t\t\t\tif (get_sha1_hex(line + 10, sha1))\n-\t\t\t\t\tdie(\"invalid unshallow line: %s\", line);\n-\t\t\t\tif (!lookup_object(sha1))\n-\t\t\t\t\tdie(\"object not found: %s\", line);\n-\t\t\t\t/* make sure that it is parsed as shallow */\n-\t\t\t\tparse_object(sha1);\n-\t\t\t\tif (unregister_shallow(sha1))\n-\t\t\t\t\tdie(\"no shallow found: %s\", line);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tdie(\"expected shallow/unshallow, got %s\", line);\n-\t\t}\n-\t}\n+\tif (args.depth > 0)\n+\t\thandle_shallow(fd);\n+\n+\tif (show_symref)\n+\t\thandle_symref(fd, refs);\n \n \tflushes = 0;\n \tretval = -1;\n@@ -558,6 +583,11 @@ static struct ref *do_fetch_pack(int fd[2],\n \tget_remote_heads(fd[0], &ref, 0, NULL, 0);\n \tif (is_repository_shallow() && !server_supports(\"shallow\"))\n \t\tdie(\"Server does not support shallow clients\");\n+\tif (server_supports(\"show-symref\")) {\n+\t\tif (args.verbose)\n+\t\t\tfprintf(stderr, \"Server supports show-symref\\n\");\n+\t\tshow_symref = 1;\n+\t}\n \tif (server_supports(\"multi_ack\")) {\n \t\tif (args.verbose)\n \t\t\tfprintf(stderr, \"Server supports multi_ack\\n\");\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 7e04311..351d501 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -27,7 +27,7 @@ static const char upload_pack_usage[] = \"git-upload-pack [--strict] [--timeout=n\n static unsigned long oldest_have;\n \n static int multi_ack, nr_our_refs;\n-static int use_thin_pack, use_ofs_delta, no_progress;\n+static int use_thin_pack, use_ofs_delta, no_progress, show_symref;\n static struct object_array have_obj;\n static struct object_array want_obj;\n static unsigned int timeout;\n@@ -477,6 +477,10 @@ static void receive_needs(void)\n \t\t    get_sha1_hex(line+5, sha1_buf))\n \t\t\tdie(\"git-upload-pack: protocol error, \"\n \t\t\t    \"expected to get sha, not '%s'\", line);\n+\n+\t\t/* Protocol extensions */\n+\t\tif (strstr(line+45, \"show-symref\"))\n+\t\t\tshow_symref = 1;\n \t\tif (strstr(line+45, \"multi_ack\"))\n \t\t\tmulti_ack = 1;\n \t\tif (strstr(line+45, \"thin-pack\"))\n@@ -557,7 +561,7 @@ static void receive_needs(void)\n static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstatic const char *capabilities = \"multi_ack thin-pack side-band\"\n-\t\t\" side-band-64k ofs-delta shallow no-progress\";\n+\t\t\" side-band-64k ofs-delta shallow no-progress show-symref\";\n \tstruct object *o = parse_object(sha1);\n \n \tif (!o)\n@@ -580,6 +584,18 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo\n \treturn 0;\n }\n \n+static int send_symref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n+{\n+\tunsigned char object_name[20];\n+\tconst char *symref;\n+\tint what;\n+\n+\tsymref = resolve_ref(refname, object_name, 1, &what);\n+\tif (symref && (what & REF_ISSYMREF))\n+\t\tpacket_write(1, \"symref %s %s\\n\", refname, symref);\n+\treturn 0;\n+}\n+\n static void upload_pack(void)\n {\n \treset_timeout();\n@@ -587,6 +603,11 @@ static void upload_pack(void)\n \tfor_each_ref(send_ref, NULL);\n \tpacket_flush(1);\n \treceive_needs();\n+\tif (show_symref) {\n+\t\tsend_symref(\"HEAD\", NULL, 0, NULL);\n+\t\tfor_each_ref(send_symref, NULL);\n+\t\tpacket_flush(1);\n+\t}\n \tif (want_obj.nr) {\n \t\tget_common_commits();\n \t\tcreate_pack_file();\n-- \n1.5.3.7-1157-gbf82a\n"},{"id":"62848","messageId":"7vabogbnv5.fsf@gitster.siamese.dyndns.org","threadId":"11242","inReplyTo":"7vejdsbo7d.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] builtin-clone: Implement git clone as a builtin command.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2007-12-12T03:20:14Z","receivedAt":"2007-12-12T03:20:14Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> What the current \"git clone\" does that are not naturally expressed by\n> the above sequence are:\n>\n>  * HEAD discovery\n>\n>    The code can be lifted from the scripted version and transplanted to\n>    git-remote.  And to make \"origin\" and other remotes added by \"git\n>    remote add\", this logic needs to be moved to \"git remote\".\n\ns/remote add\", this/remote add\" more equal, this/;\n"},{"id":"62897","messageId":"Pine.LNX.4.64.0712121103510.27959@racer.site","threadId":"11242","inReplyTo":"7vejdsbo7d.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] builtin-clone: Implement git clone as a builtin command.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-12-12T11:12:50Z","receivedAt":"2007-12-12T11:12:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 11 Dec 2007, Junio C Hamano wrote:\n\n> Kristian Høgsberg <krh@redhat.com> writes:\n> \n> > On Tue, 2007-12-11 at 15:59 -0500, Daniel Barkalow wrote:\n> >> On Tue, 11 Dec 2007, Kristian Høgsberg wrote:\n> >> \n> >> > Ok, don't flame me, I know this isn't appropriate at the moment \n> >> > with stabilization for 1.5.4 going on, but I just wanted to post a \n> >> > heads up on this work to avoid duplicate effort.  It's one big \n> >> > patch at this point and I haven't even run the test suite yet, but \n> >> > that will change.\n> >> \n> >> Is that why you misspelled Junio's email address? :)\n> >\n> > Hehe, yeah, do not mess with maintainers in release mode :)\n> \n> Actually this is a bit unfortunate, regardless of everybody being in \n> release and bugfix only mode.\n\nI can understand that feeling, but I have to say that I am actually quite \npleased with the progress in direction of having most of git as builtins.\n\n> I was hoping that the evolution path for clone would be to first make it \n> a very thin wrapper around:\n> \n> \tgit init\n>         git remote add -f\n>         git checkout\n> \n> sequence.\n\nYeah, I thought so too, but I'll also gladly take the builtin first.\n\n> There are a handful issues in that approach with the current git-remote, \n> and that was why I also thought recent \"git remote in C\" by Dscho a bit \n> unfortunate, as enhancements and interface fixes (both user and machine) \n> tend to be much easier in scripted version.\n\nAnd here I have to disagree strongly.  I _wasted_ a _week_ on trying to \nfix that stupid \"add --mirror && prune\" bug in the scripted version.  It \nwas absolutely horrible.  And I felt like a moron after that week.\n\nIn contrast, it was easy as chocolate cake to fix it in the builtin \nremote.\n\nNow, if you not only hinted in some mail that something is wrong with \nbuiltin-remote, but gave me some input, I could fix that in the builtin, \ntoo.\n\n> What the current \"git clone\" does that are not naturally expressed by\n> the above sequence are:\n> \n>  * HEAD discovery\n> \n>    The code can be lifted from the scripted version and transplanted to\n>    git-remote.  And to make \"origin\" and other remotes added by \"git\n>    remote add\", this logic needs to be moved to \"git remote\".\n> \n>    However, before rewriting the \"git remote\" to C, it would be really\n>    nice if we can update the native protocol so that we can reliably\n>    find out which branch HEAD points at.  The current code guesses, only\n>    because the native protocol does not carry that information [*1*].\n>    Worse yet, even though the current code _knows_ this information when\n>    going over dumb protocols, it discards it to use the same guessing\n>    logic as used by the native protocol.\n\nI wonder why this should be easier with git remote in Perl.  IMHO it is \neasier with git remote in C.\n\n>  * --shared optimization\n> \n>    This is a very easy addition to \"git remote add\".  You make sure that\n>    the added remote repository is on a local machine, and set up\n>    alternates to point at its object store.\n\nConcur.\n\nSince I want to lose that dependency on cpio on Windows (which we fake by \nusing tar), I'll implement this in C anyway.\n\nCiao,\nDscho\n"},{"id":"62907","messageId":"1197471866.9269.2.camel@hinata.boston.redhat.com","threadId":"11242","inReplyTo":"Pine.LNX.4.64.0712121103510.27959@racer.site","subject":"Re: [PATCH] builtin-clone: Implement git clone as a builtin command.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-12-12T15:04:26Z","receivedAt":"2007-12-12T15:04:26Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"On Wed, 2007-12-12 at 11:12 +0000, Johannes Schindelin wrote:\n> Hi,\n> \n> On Tue, 11 Dec 2007, Junio C Hamano wrote:\n...\n> >  * --shared optimization\n> > \n> >    This is a very easy addition to \"git remote add\".  You make sure that\n> >    the added remote repository is on a local machine, and set up\n> >    alternates to point at its object store.\n> \n> Concur.\n> \n> Since I want to lose that dependency on cpio on Windows (which we fake by \n> using tar), I'll implement this in C anyway.\n\nIt's not used for --shared (which is just writing an alternates file),\nit's used for -l, hardlinking locally cloned repos.  The code to replace\ncpio is already in the patch I sent, look for clone_local().\n\ncheers,\nKristian\n"},{"id":"62910","messageId":"1197473063.9269.20.camel@hinata.boston.redhat.com","threadId":"11242","inReplyTo":"7vejdsbo7d.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] builtin-clone: Implement git clone as a builtin command.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-12-12T15:24:23Z","receivedAt":"2007-12-12T15:24:23Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"\nOn Tue, 2007-12-11 at 19:12 -0800, Junio C Hamano wrote:\n> Kristian Høgsberg <krh@redhat.com> writes:\n> \n> > On Tue, 2007-12-11 at 15:59 -0500, Daniel Barkalow wrote:\n> >> On Tue, 11 Dec 2007, Kristian Høgsberg wrote:\n> >> \n> >> > Ok, don't flame me, I know this isn't appropriate at the moment with\n> >> > stabilization for 1.5.4 going on, but I just wanted to post a heads up\n> >> > on this work to avoid duplicate effort.  It's one big patch at this point\n> >> > and I haven't even run the test suite yet, but that will change.\n> >> \n> >> Is that why you misspelled Junio's email address? :) \n> >\n> > Hehe, yeah, do not mess with maintainers in release mode :)\n> \n> Actually this is a bit unfortunate, regardless of everybody being in\n> release and bugfix only mode.\n\nWell, let's just pick up the discussion in January, I have a lot of\nother stuff I'm trying to do anyway :)\n\n> I was hoping that the evolution path for clone would be to first make it\n> a very thin wrapper around:\n> \n> \tgit init\n>         git remote add -f\n>         git checkout\n> \n> sequence.\n\nHowever, let me just say that the patch I sent is almost just that.\nPart of the patch refactors init-db to be useful from clone, part of the\ncode is option parsing and figuring out the git dir, work tree.  Also,\nthe part of the patch that does 'git checkout' is approximately 20 lines\nthat end up calling unpack_tre() and then write_cache().  The bulk of\nthe work here is really just builtin boilerplate code, option parsing\nand the builtin-clone tasks you describe below (HEAD discovery, --shared\nand --reference optimizations and the local hardlink optimization - all\nthese are in the 500 line builtin-clone.c I sent).\n\nAnd maybe it makes sense to use builtin-remote for the remote add -f\npart, but the fetch part of the patch is 10 lines to set up for\nfetch_pack().  So while I do agree that it makes sense to keep remotes\nhandling in one place, doing the fetch_pack() in builtin-clone.c doesn't\nseem like a big duplication of code.  And either way, I agree with\nDscho, once we have either builtin-clone or builtin-fetch it's easier to\nshare code and refactor, and there is not a strong reason to do one or\nthe other first.\n\ncheers,\nKristian\n"},{"id":"62937","messageId":"Pine.LNX.4.64.0712121237540.5349@iabervon.org","threadId":"11242","inReplyTo":"1197473063.9269.20.camel@hinata.boston.redhat.com","subject":"Re: [PATCH] builtin-clone: Implement git clone as a builtin command.","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2007-12-12T18:00:08Z","receivedAt":"2007-12-12T18:00:08Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Wed, 12 Dec 2007, Kristian H�gsberg wrote:\n\n> However, let me just say that the patch I sent is almost just that.\n> Part of the patch refactors init-db to be useful from clone, part of the\n> code is option parsing and figuring out the git dir, work tree.  Also,\n> the part of the patch that does 'git checkout' is approximately 20 lines\n> that end up calling unpack_tre() and then write_cache().  The bulk of\n> the work here is really just builtin boilerplate code, option parsing\n> and the builtin-clone tasks you describe below (HEAD discovery, --shared\n> and --reference optimizations and the local hardlink optimization - all\n> these are in the 500 line builtin-clone.c I sent).\n> \n> And maybe it makes sense to use builtin-remote for the remote add -f\n> part, but the fetch part of the patch is 10 lines to set up for\n> fetch_pack().  So while I do agree that it makes sense to keep remotes\n> handling in one place, doing the fetch_pack() in builtin-clone.c doesn't\n> seem like a big duplication of code.  And either way, I agree with\n> Dscho, once we have either builtin-clone or builtin-fetch it's easier to\n> share code and refactor, and there is not a strong reason to do one or\n> the other first.\n\nEr, we have builtin-fetch. We just don't have a way of calling it with all \nof the option parsing done, but that should be easy. I was expecting that \nstep to get done when clone got converted, or maybe remote...\n\nI agree that the checkout special case when the code knows in advance that \nyou don't have anything checked out beforehand is particularly trivial, \nand it's probably just as easy to call unpack_trees() and write_cache() as \nto use an actual checkout implementation.\n\n\t-Daniel\n*This .sig left intentionally blank*"},{"id":"62941","messageId":"Pine.LNX.4.64.0712121823090.27959@racer.site","threadId":"11242","inReplyTo":"1197471866.9269.2.camel@hinata.boston.redhat.com","subject":"Re: [PATCH] builtin-clone: Implement git clone as a builtin command.","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2007-12-12T18:24:18Z","receivedAt":"2007-12-12T18:24:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 12 Dec 2007, Kristian H?gsberg wrote:\n\n> On Wed, 2007-12-12 at 11:12 +0000, Johannes Schindelin wrote:\n> \n> > On Tue, 11 Dec 2007, Junio C Hamano wrote:\n> ...\n> > >  * --shared optimization\n> > > \n> > >    This is a very easy addition to \"git remote add\".  You make sure \n> > >    that the added remote repository is on a local machine, and set \n> > >    up alternates to point at its object store.\n> > \n> > Concur.\n> > \n> > Since I want to lose that dependency on cpio on Windows (which we fake \n> > by using tar), I'll implement this in C anyway.\n> \n> It's not used for --shared (which is just writing an alternates file), \n> it's used for -l, hardlinking locally cloned repos.  The code to replace \n> cpio is already in the patch I sent, look for clone_local().\n\nSorry, that comment should have gone after another part of the original \nmessage.\n\nMy only two excuses are that I am ill, and am overloaded with work.\n\nCiao,\nDscho\n"},{"id":"62943","messageId":"1197483943.10132.4.camel@hinata.boston.redhat.com","threadId":"11242","inReplyTo":"Pine.LNX.4.64.0712121237540.5349@iabervon.org","subject":"Re: [PATCH] builtin-clone: Implement git clone as a builtin command.","fromName":"Kristian Høgsberg","fromEmail":"krh@redhat.com","sentAt":"2007-12-12T18:25:43Z","receivedAt":"2007-12-12T18:25:43Z","isPatch":true,"sender":{"key":"krh@redhat.com","avatar":"https://gravatar.com/avatar/763dee6f9594ac474f725b137a39565792928e583ddf59b32befc2907409027e?d=mp&s=160"},"body":"On Wed, 2007-12-12 at 13:00 -0500, Daniel Barkalow wrote:\n> On Wed, 12 Dec 2007, Kristian Hgsberg wrote:\n> \n> > However, let me just say that the patch I sent is almost just that.\n> > Part of the patch refactors init-db to be useful from clone, part of the\n> > code is option parsing and figuring out the git dir, work tree.  Also,\n> > the part of the patch that does 'git checkout' is approximately 20 lines\n> > that end up calling unpack_tre() and then write_cache().  The bulk of\n> > the work here is really just builtin boilerplate code, option parsing\n> > and the builtin-clone tasks you describe below (HEAD discovery, --shared\n> > and --reference optimizations and the local hardlink optimization - all\n> > these are in the 500 line builtin-clone.c I sent).\n> > \n> > And maybe it makes sense to use builtin-remote for the remote add -f\n> > part, but the fetch part of the patch is 10 lines to set up for\n> > fetch_pack().  So while I do agree that it makes sense to keep remotes\n> > handling in one place, doing the fetch_pack() in builtin-clone.c doesn't\n> > seem like a big duplication of code.  And either way, I agree with\n> > Dscho, once we have either builtin-clone or builtin-fetch it's easier to\n> > share code and refactor, and there is not a strong reason to do one or\n> > the other first.\n> \n> Er, we have builtin-fetch. We just don't have a way of calling it with all \n> of the option parsing done, but that should be easy. I was expecting that \n> step to get done when clone got converted, or maybe remote...\n\nUgh, I meant builtin-remote there, sorry.  I use fetch_pack() like the shell\nscript does, and it seem a lot easier that trying to call fetch:\n\n        struct fetch_pack_args args;\n\n        args.uploadpack = option_upload_pack;\n        args.quiet = option_quiet;\n        args.fetch_all = 1;\n        args.lock_pack = 0;\n        args.keep_pack = 1;\n        args.depth = option_depth;\n        args.no_progress = 1;\n\n        refs = fetch_pack(&args, argv[0], 0, NULL, NULL);\n\nKristian\n"},{"id":"62944","messageId":"Pine.LNX.4.64.0712121330541.5349@iabervon.org","threadId":"11242","inReplyTo":"1197483943.10132.4.camel@hinata.boston.redhat.com","subject":"Re: [PATCH] builtin-clone: Implement git clone as a builtin command.","fromName":"Daniel Barkalow","fromEmail":"barkalow@iabervon.org","sentAt":"2007-12-12T18:40:12Z","receivedAt":"2007-12-12T18:40:12Z","isPatch":true,"sender":{"key":"barkalow@iabervon.org","avatar":"https://avatars.githubusercontent.com/u/55364219?v=4"},"body":"On Wed, 12 Dec 2007, Kristian H�gsberg wrote:\n\n> Ugh, I meant builtin-remote there, sorry.  I use fetch_pack() like the shell\n> script does, and it seem a lot easier that trying to call fetch:\n> \n>         struct fetch_pack_args args;\n> \n>         args.uploadpack = option_upload_pack;\n>         args.quiet = option_quiet;\n>         args.fetch_all = 1;\n>         args.lock_pack = 0;\n>         args.keep_pack = 1;\n>         args.depth = option_depth;\n>         args.no_progress = 1;\n> \n>         refs = fetch_pack(&args, argv[0], 0, NULL, NULL);\n\nAh, but that only works for git native protocol remote repositories. \nCalling fetch instead would mean that other protocols also work without \nany fuss.\n\n\t-Daniel\n*This .sig left intentionally blank*"},{"id":"64060","messageId":"20071224015232.GB12616@fieldses.org","threadId":"11242","inReplyTo":"7vejdsbo7d.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] builtin-clone: Implement git clone as a builtin command.","fromName":"J. Bruce Fields","fromEmail":"bfields@fieldses.org","sentAt":"2007-12-24T01:52:32Z","receivedAt":"2007-12-24T01:52:32Z","isPatch":true,"sender":{"key":"bfields@citi.umich.edu","avatar":null},"body":"On Tue, Dec 11, 2007 at 07:12:54PM -0800, Junio C Hamano wrote:\n>  * HEAD discovery\n> \n>    The code can be lifted from the scripted version and transplanted to\n>    git-remote.  And to make \"origin\" and other remotes added by \"git\n>    remote add\", this logic needs to be moved to \"git remote\".\n\nA rough first attempt appended.\n\nCleaning up in \"remote rm\" is a bit of a pain once a remote can contain\nsymbolic-refs.  Would it make sense to add something like a \"git\nupdate-ref -D <refname>\" that deletes anything at that path with no\nchecking?\n\nThanks for outlining these remote improvements, by the way, I really\nlook forward to them.  I'll do what I can, but will probably be much too\nslow....\n\n--b.\n\ncommit fecdb5c0d118767c216302e2c91950cca04f9a26\nAuthor: J. Bruce Fields <bfields@citi.umich.edu>\nDate:   Fri Dec 21 19:55:09 2007 -0500\n\n    git-remote: make add -f guess HEAD, as clone does\n    \n    Has a few ugly bits.\n    \n    Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>\n\ndiff --git a/git-remote.perl b/git-remote.perl\nindex d13e4c1..3299029 100755\n--- a/git-remote.perl\n+++ b/git-remote.perl\n@@ -270,12 +270,57 @@ sub show_remote {\n \treturn 0;\n }\n \n+sub guess_head {\n+\tmy ($subdir, $head_sha1) = @_;\n+\tmy $found = 0;\n+\n+\tfor ($git->command('for-each-ref', \"$subdir\")) {\n+\t\tchomp;\n+\t\tm|^([0-9a-f]{40})\\s[a-z]+\\s$subdir/(.*)$| || die();\n+\t\tmy ($sha1, $ref) = ($1, $2);\n+\n+\t\tif ($sha1 eq $head_sha1) {\n+\t\t\t$found = $ref;\n+\t\t\t# prefer \"master\" if it matches:\n+\t\t\tif ($ref eq \"master\") {\n+\t\t\t\tlast;\n+\t\t\t}\n+\t\t}\n+\t}\n+\treturn $found\n+}\n+\n+sub fix_head {\n+\tmy ($remote) = @_;\n+\n+\tmy $subdir = \"refs/remotes/$remote\";\n+\tmy $head_sha1 = $git->command(qw(rev-parse --verify), \"$subdir/HEAD\");\n+\tchomp($head_sha1);\n+\n+\tunlink($git->repo_path.\"/$subdir/HEAD\");\n+\tmy $found = guess_head($subdir, $head_sha1);\n+\tif (!$found) {\n+\t\t# Just leave it as a a bare sha1\n+\t\t$git->command(\"update-ref\", \"$subdir/HEAD\", \"$head_sha1\");\n+\t\treturn;\n+\t}\n+\t$git->command(\"symbolic-ref\", \"$subdir/HEAD\", \"$subdir/$found\");\n+\t$git->command(\"config\", \"branch.$found.remote\", \"$remote\");\n+\t$git->command(\"config\", \"branch.$found.merge\", \"refs/heads/$found\");\n+}\n+\n sub add_remote {\n \tmy ($name, $url, $opts) = @_;\n \tif (exists $remote->{$name}) {\n \t\tprint STDERR \"remote $name already exists.\\n\";\n \t\texit(1);\n \t}\n+\n+\t# Tricky!: magic HEAD setup should only be done when\n+\t# \"mirror\", \"master\", and \"track\" options aren't given.\n+\t# And for orthagonality perhaps we should also provide a\n+\t# \"track head\" optio., compatible with \"track\" but not\n+\t# the other two....\n \t$git->command('config', \"remote.$name.url\", $url);\n \tmy $track = $opts->{'track'} || [\"*\"];\n \n@@ -286,7 +331,17 @@ sub add_remote {\n \t\t\t\t\"+refs/heads/$_:refs/remotes/$name/$_\");\n \t}\n \tif ($opts->{'fetch'}) {\n-\t\t$git->command('fetch', $name);\n+\t\tif (!$opts->{'mirror'} && !$opts->{'master'}\n+\t\t\t\t\t&& !$opts->{'track'}) {\n+\t\t\tmy $refspec = \"+refs/heads/*:refs/remotes/$name/*\";\n+\n+\t\t\t# XXX: can there be a remote refs/heads/HEAD??\n+\t\t\t$git->command('fetch', $name, $refspec,\n+\t\t\t\t\t\"HEAD:refs/remotes/$name/HEAD\");\n+\t\t\tfix_head($name);\n+\t\t} else {\n+\t\t\t$git->command('fetch', $name);\n+\t\t}\n \t}\n \tif (exists $opts->{'master'}) {\n \t\t$git->command('symbolic-ref', \"refs/remotes/$name/HEAD\",\n@@ -338,11 +393,11 @@ sub rm_remote {\n \t\t}\n \t};\n \n-\tmy @refs = $git->command('for-each-ref',\n-\t\t'--format=%(refname) %(objectname)', \"refs/remotes/$name\");\n-\tfor (@refs) {\n-\t\t($ref, $object) = split;\n-\t\t$git->command(qw(update-ref -d), $ref, $object);\n+\t# Ugh: update-ref doesn't work on symref (as \"HEAD\" may be),\n+\t# but the following won't work on packed refs, for example.\n+\tfor ($git->command('for-each-ref',\n+\t\t\t'--format=%(refname)', \"refs/remotes/$name\")) {\n+\t\tunlink($git->repo_path.\"/$_\");\n \t}\n \treturn 0;\n }\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex 636aec2..5576f2a 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -69,7 +69,7 @@ test_expect_success 'add another remote' '\n \ttokens_match \"origin second\" \"$(git remote)\" &&\n \tcheck_remote_track origin master side &&\n \tcheck_remote_track second master side another &&\n-\tcheck_tracking_branch second master side another &&\n+\tcheck_tracking_branch second HEAD master side another &&\n \tgit for-each-ref \"--format=%(refname)\" refs/remotes |\n \tsed -e \"/^refs\\/remotes\\/origin\\//d\" \\\n \t    -e \"/^refs\\/remotes\\/second\\//d\" >actual &&\n"}]}