clone: Allow repo using gitfile as a reference
[gitweb.git] / builtin / clone.c
index 36ec99db3f5a7dbd59705aa68bcd592c08c773cd..58fee9874f0fea4ed52acf31f757268be5383267 100644 (file)
@@ -23,6 +23,7 @@
 #include "branch.h"
 #include "remote.h"
 #include "run-command.h"
+#include "connected.h"
 
 /*
  * Overall FIXMEs:
@@ -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))) {
+
+       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;
+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))
@@ -767,8 +826,6 @@ 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);
 
@@ -787,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) {
@@ -907,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;
 }