cat-file: split --batch input lines on whitespace
[gitweb.git] / builtin / clone.c
index 0d663e34f78431280eb7f79d581262a7c28820d3..035ab649504d69a83f87b854582c8003a2dcb905 100644 (file)
@@ -23,6 +23,7 @@
 #include "branch.h"
 #include "remote.h"
 #include "run-command.h"
+#include "connected.h"
 
 /*
  * Overall FIXMEs:
@@ -33,7 +34,7 @@
  *
  */
 static const char * const builtin_clone_usage[] = {
-       "git clone [options] [--] <repo> [<dir>]",
+       N_("git clone [options] [--] <repo> [<dir>]"),
        NULL
 };
 
@@ -61,43 +62,43 @@ static int opt_parse_reference(const struct option *opt, const char *arg, int un
 static struct option builtin_clone_options[] = {
        OPT__VERBOSITY(&option_verbosity),
        OPT_BOOL(0, "progress", &option_progress,
-                "force progress reporting"),
+                N_("force progress reporting")),
        OPT_BOOLEAN('n', "no-checkout", &option_no_checkout,
-                   "don't create a checkout"),
-       OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"),
+                   N_("don't create a checkout")),
+       OPT_BOOLEAN(0, "bare", &option_bare, N_("create a bare repository")),
        { OPTION_BOOLEAN, 0, "naked", &option_bare, NULL,
-               "create a bare repository",
+               N_("create a bare repository"),
                PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
        OPT_BOOLEAN(0, "mirror", &option_mirror,
-                   "create a mirror repository (implies bare)"),
+                   N_("create a mirror repository (implies bare)")),
        OPT_BOOL('l', "local", &option_local,
-               "to clone from a local repository"),
+               N_("to clone from a local repository")),
        OPT_BOOLEAN(0, "no-hardlinks", &option_no_hardlinks,
-                   "don't use local hardlinks, always copy"),
+                   N_("don't use local hardlinks, always copy")),
        OPT_BOOLEAN('s', "shared", &option_shared,
-                   "setup as shared repository"),
+                   N_("setup as shared repository")),
        OPT_BOOLEAN(0, "recursive", &option_recursive,
-                   "initialize submodules in the clone"),
+                   N_("initialize submodules in the clone")),
        OPT_BOOLEAN(0, "recurse-submodules", &option_recursive,
-                   "initialize submodules in the clone"),
-       OPT_STRING(0, "template", &option_template, "template-directory",
-                  "directory from which templates will be used"),
-       OPT_CALLBACK(0 , "reference", &option_reference, "repo",
-                    "reference repository", &opt_parse_reference),
-       OPT_STRING('o', "origin", &option_origin, "name",
-                  "use <name> instead of 'origin' to track upstream"),
-       OPT_STRING('b', "branch", &option_branch, "branch",
-                  "checkout <branch> instead of the remote's HEAD"),
-       OPT_STRING('u', "upload-pack", &option_upload_pack, "path",
-                  "path to git-upload-pack on the remote"),
-       OPT_STRING(0, "depth", &option_depth, "depth",
-                   "create a shallow clone of that depth"),
+                   N_("initialize submodules in the clone")),
+       OPT_STRING(0, "template", &option_template, N_("template-directory"),
+                  N_("directory from which templates will be used")),
+       OPT_CALLBACK(0 , "reference", &option_reference, N_("repo"),
+                    N_("reference repository"), &opt_parse_reference),
+       OPT_STRING('o', "origin", &option_origin, N_("name"),
+                  N_("use <name> instead of 'origin' to track upstream")),
+       OPT_STRING('b', "branch", &option_branch, N_("branch"),
+                  N_("checkout <branch> instead of the remote's HEAD")),
+       OPT_STRING('u', "upload-pack", &option_upload_pack, N_("path"),
+                  N_("path to git-upload-pack on the remote")),
+       OPT_STRING(0, "depth", &option_depth, N_("depth"),
+                   N_("create a shallow clone of that depth")),
        OPT_BOOL(0, "single-branch", &option_single_branch,
-                   "clone only one branch, HEAD or --branch"),
-       OPT_STRING(0, "separate-git-dir", &real_git_dir, "gitdir",
-                  "separate git dir from working tree"),
-       OPT_STRING_LIST('c', "config", &option_config, "key=value",
-                       "set config inside the new repository"),
+                   N_("clone only one branch, HEAD or --branch")),
+       OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
+                  N_("separate git dir from working tree")),
+       OPT_STRING_LIST('c', "config", &option_config, N_("key=value"),
+                       N_("set config inside the new repository")),
        OPT_END()
 };
 
@@ -231,16 +232,26 @@ static void strip_trailing_slashes(char *dir)
 static int add_one_reference(struct string_list_item *item, void *cb_data)
 {
        char *ref_git;
+       const char *repo;
        struct strbuf alternate = STRBUF_INIT;
 
-       /* Beware: real_path() and mkpath() return static buffer */
+       /* Beware: read_gitfile(), real_path() and mkpath() return static buffer */
        ref_git = xstrdup(real_path(item->string));
-       if (is_directory(mkpath("%s/.git/objects", ref_git))) {
-               char *ref_git_git = xstrdup(mkpath("%s/.git", ref_git));
+
+       repo = read_gitfile(ref_git);
+       if (!repo)
+               repo = read_gitfile(mkpath("%s/.git", ref_git));
+       if (repo) {
+               free(ref_git);
+               ref_git = xstrdup(repo);
+       }
+
+       if (!repo && is_directory(mkpath("%s/.git/objects", ref_git))) {
+               char *ref_git_git = mkpathdup("%s/.git", ref_git);
                free(ref_git);
                ref_git = ref_git_git;
        } else if (!is_directory(mkpath("%s/objects", ref_git)))
-               die(_("reference repository '%s' is not a local directory."),
+               die(_("reference repository '%s' is not a local repository."),
                    item->string);
 
        strbuf_addf(&alternate, "%s/objects", ref_git);
@@ -376,10 +387,32 @@ static void clone_local(const char *src_repo, const char *dest_repo)
 static const char *junk_work_tree;
 static const char *junk_git_dir;
 static pid_t junk_pid;
+static enum {
+       JUNK_LEAVE_NONE,
+       JUNK_LEAVE_REPO,
+       JUNK_LEAVE_ALL
+} junk_mode = JUNK_LEAVE_NONE;
+
+static const char junk_leave_repo_msg[] =
+N_("Clone succeeded, but checkout failed.\n"
+   "You can inspect what was checked out with 'git status'\n"
+   "and retry the checkout with 'git checkout -f HEAD'\n");
 
 static void remove_junk(void)
 {
        struct strbuf sb = STRBUF_INIT;
+
+       switch (junk_mode) {
+       case JUNK_LEAVE_REPO:
+               warning("%s", _(junk_leave_repo_msg));
+               /* fall-through */
+       case JUNK_LEAVE_ALL:
+               return;
+       default:
+               /* proceed to removal */
+               break;
+       }
+
        if (getpid() != junk_pid)
                return;
        if (junk_git_dir) {
@@ -485,12 +518,37 @@ static void write_followtags(const struct ref *refs, const char *msg)
        }
 }
 
+static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
+{
+       struct ref **rm = cb_data;
+       struct ref *ref = *rm;
+
+       /*
+        * Skip anything missing a peer_ref, which we are not
+        * actually going to write a ref for.
+        */
+       while (ref && !ref->peer_ref)
+               ref = ref->next;
+       /* Returning -1 notes "end of list" to the caller. */
+       if (!ref)
+               return -1;
+
+       hashcpy(sha1, ref->old_sha1);
+       *rm = ref->next;
+       return 0;
+}
+
 static void update_remote_refs(const struct ref *refs,
                               const struct ref *mapped_refs,
                               const struct ref *remote_head_points_at,
                               const char *branch_top,
                               const char *msg)
 {
+       const struct ref *rm = mapped_refs;
+
+       if (check_everything_connected(iterate_ref_map, 0, &rm))
+               die(_("remote did not send all necessary objects"));
+
        if (refs) {
                write_remote_refs(mapped_refs);
                if (option_single_branch)
@@ -579,7 +637,8 @@ static int checkout(void)
        tree = parse_tree_indirect(sha1);
        parse_tree(tree);
        init_tree_desc(&t, tree->buffer, tree->size);
-       unpack_trees(1, &t, &opts);
+       if (unpack_trees(1, &t, &opts) < 0)
+               die(_("unable to checkout working tree"));
 
        if (write_cache(fd, active_cache, active_nr) ||
            commit_locked_index(lock_file))
@@ -704,6 +763,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                if (option_origin)
                        die(_("--bare and --origin %s options are incompatible."),
                            option_origin);
+               if (real_git_dir)
+                       die(_("--bare and --separate-git-dir are incompatible."));
                option_no_checkout = 1;
        }
 
@@ -748,7 +809,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                git_dir = xstrdup(dir);
        else {
                work_tree = dir;
-               git_dir = xstrdup(mkpath("%s/.git", dir));
+               git_dir = mkpathdup("%s/.git", dir);
        }
 
        if (!option_bare) {
@@ -765,14 +826,14 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        atexit(remove_junk);
        sigchain_push_common(remove_junk_on_signal);
 
-       setenv(CONFIG_ENVIRONMENT, mkpath("%s/config", git_dir), 1);
-
        if (safe_create_leading_directories_const(git_dir) < 0)
                die(_("could not create leading directories of '%s'"), git_dir);
 
        set_git_dir_init(git_dir, real_git_dir, 0);
-       if (real_git_dir)
+       if (real_git_dir) {
                git_dir = real_git_dir;
+               junk_git_dir = real_git_dir;
+       }
 
        if (0 <= option_verbosity) {
                if (option_bare)
@@ -783,13 +844,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        init_db(option_template, INIT_DB_QUIET);
        write_config(&option_config);
 
-       /*
-        * At this point, the config exists, so we do not need the
-        * environment variable.  We actually need to unset it, too, to
-        * re-enable parsing of the global configs.
-        */
-       unsetenv(CONFIG_ENVIRONMENT);
-
        git_config(git_default_config, NULL);
 
        if (option_bare) {
@@ -903,12 +957,13 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        transport_unlock_pack(transport);
        transport_disconnect(transport);
 
+       junk_mode = JUNK_LEAVE_REPO;
        err = checkout();
 
        strbuf_release(&reflog_msg);
        strbuf_release(&branch_top);
        strbuf_release(&key);
        strbuf_release(&value);
-       junk_pid = 0;
+       junk_mode = JUNK_LEAVE_ALL;
        return err;
 }