Merge branch 'jk/cleanup-failed-clone' into maint
authorJunio C Hamano <gitster@pobox.com>
Sat, 28 Mar 2015 16:33:09 +0000 (09:33 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sat, 28 Mar 2015 16:33:09 +0000 (09:33 -0700)
An failure early in the "git clone" that started creating the
working tree and repository could have resulted in some directories
and files left without getting cleaned up.

* jk/cleanup-failed-clone:
clone: drop period from end of die_errno message
clone: initialize atexit cleanup handler earlier

1  2 
builtin/clone.c
diff --combined builtin/clone.c
index 316c75d0b342d039696608c18adfabaa3366b0e7,3c5ba0ab25b661f4069d3ac9a6b7c2549eac01ec..2d77a2de4f3d3c73019b0b9873048f2d5747a378
@@@ -9,7 -9,6 +9,7 @@@
   */
  
  #include "builtin.h"
 +#include "lockfile.h"
  #include "parse-options.h"
  #include "fetch-pack.h"
  #include "refs.h"
@@@ -49,7 -48,6 +49,7 @@@ static int option_verbosity
  static int option_progress = -1;
  static struct string_list option_config;
  static struct string_list option_reference;
 +static int option_dissociate;
  
  static int opt_parse_reference(const struct option *opt, const char *arg, int unset)
  {
@@@ -95,8 -93,6 +95,8 @@@ static struct option builtin_clone_opti
                    N_("create a shallow clone of that depth")),
        OPT_BOOL(0, "single-branch", &option_single_branch,
                    N_("clone only one branch, HEAD or --branch")),
 +      OPT_BOOL(0, "dissociate", &option_dissociate,
 +               N_("use --reference only while cloning")),
        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"),
@@@ -394,6 -390,7 +394,6 @@@ static void clone_local(const char *src
  
  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,
@@@ -420,6 -417,8 +420,6 @@@ static void remove_junk(void
                break;
        }
  
 -      if (getpid() != junk_pid)
 -              return;
        if (junk_git_dir) {
                strbuf_addstr(&sb, junk_git_dir);
                remove_dir_recursively(&sb, 0);
@@@ -522,7 -521,7 +522,7 @@@ static void write_followtags(const stru
                if (!has_sha1_file(ref->old_sha1))
                        continue;
                update_ref(msg, ref->name, ref->old_sha1,
 -                         NULL, 0, DIE_ON_ERR);
 +                         NULL, 0, UPDATE_REFS_DIE_ON_ERR);
        }
  }
  
@@@ -585,20 -584,19 +585,20 @@@ static void update_remote_refs(const st
  static void update_head(const struct ref *our, const struct ref *remote,
                        const char *msg)
  {
 -      if (our && starts_with(our->name, "refs/heads/")) {
 +      const char *head;
 +      if (our && skip_prefix(our->name, "refs/heads/", &head)) {
                /* Local default branch link */
                create_symref("HEAD", our->name, NULL);
                if (!option_bare) {
 -                      const char *head = skip_prefix(our->name, "refs/heads/");
 -                      update_ref(msg, "HEAD", our->old_sha1, NULL, 0, DIE_ON_ERR);
 +                      update_ref(msg, "HEAD", our->old_sha1, NULL, 0,
 +                                 UPDATE_REFS_DIE_ON_ERR);
                        install_branch_config(0, head, option_origin, our->name);
                }
        } else if (our) {
                struct commit *c = lookup_commit_reference(our->old_sha1);
                /* --branch specifies a non-branch (i.e. tags), detach HEAD */
                update_ref(msg, "HEAD", c->object.sha1,
 -                         NULL, REF_NODEREF, DIE_ON_ERR);
 +                         NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
        } else if (remote) {
                /*
                 * We know remote HEAD points to a non-branch, or
                 * Detach HEAD in all these cases.
                 */
                update_ref(msg, "HEAD", remote->old_sha1,
 -                         NULL, REF_NODEREF, DIE_ON_ERR);
 +                         NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
        }
  }
  
@@@ -618,12 -616,12 +618,12 @@@ static int checkout(void
        struct unpack_trees_options opts;
        struct tree *tree;
        struct tree_desc t;
 -      int err = 0, fd;
 +      int err = 0;
  
        if (option_no_checkout)
                return 0;
  
 -      head = resolve_refdup("HEAD", sha1, 1, NULL);
 +      head = resolve_refdup("HEAD", RESOLVE_REF_READING, sha1, NULL);
        if (!head) {
                warning(_("remote HEAD refers to nonexistent ref, "
                          "unable to checkout.\n"));
        setup_work_tree();
  
        lock_file = xcalloc(1, sizeof(struct lock_file));
 -      fd = hold_locked_index(lock_file, 1);
 +      hold_locked_index(lock_file, 1);
  
        memset(&opts, 0, sizeof opts);
        opts.update = 1;
        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))
 +      if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                die(_("unable to write new index file"));
  
        err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
@@@ -686,10 -685,9 +686,10 @@@ static void write_config(struct string_
        }
  }
  
 -static void write_refspec_config(const char* src_ref_prefix,
 -              const struct ref* our_head_points_at,
 -              const struct ref* remote_head_points_at, struct strbuf* branch_top)
 +static void write_refspec_config(const char *src_ref_prefix,
 +              const struct ref *our_head_points_at,
 +              const struct ref *remote_head_points_at,
 +              struct strbuf *branch_top)
  {
        struct strbuf key = STRBUF_INIT;
        struct strbuf value = STRBUF_INIT;
                                        strbuf_addf(&value, "+%s:%s%s", our_head_points_at->name,
                                                branch_top->buf, option_branch);
                        } else if (remote_head_points_at) {
 +                              const char *head = remote_head_points_at->name;
 +                              if (!skip_prefix(head, "refs/heads/", &head))
 +                                      die("BUG: remote HEAD points at non-head?");
 +
                                strbuf_addf(&value, "+%s:%s%s", remote_head_points_at->name,
 -                                              branch_top->buf,
 -                                              skip_prefix(remote_head_points_at->name, "refs/heads/"));
 +                                              branch_top->buf, head);
                        }
                        /*
                         * otherwise, the next "git fetch" will
        strbuf_release(&value);
  }
  
 +static void dissociate_from_references(void)
 +{
 +      static const char* argv[] = { "repack", "-a", "-d", NULL };
 +
 +      if (run_command_v_opt(argv, RUN_GIT_CMD|RUN_COMMAND_NO_STDIN))
 +              die(_("cannot repack to clean up"));
 +      if (unlink(git_path("objects/info/alternates")) && errno != ENOENT)
 +              die_errno(_("cannot unlink temporary alternates file"));
 +}
 +
  int cmd_clone(int argc, const char **argv, const char *prefix)
  {
        int is_bundle = 0, is_local;
        struct refspec *refspec;
        const char *fetch_pattern;
  
 -      junk_pid = getpid();
 -
        packet_trace_identity("clone");
        argc = parse_options(argc, argv, prefix, builtin_clone_options,
                             builtin_clone_usage, 0);
                die(_("repository '%s' does not exist"), repo_name);
        else
                repo = repo_name;
 -      is_local = option_local != 0 && path && !is_bundle;
 -      if (is_local) {
 -              if (option_depth)
 -                      warning(_("--depth is ignored in local clones; use file:// instead."));
 -              if (!access(mkpath("%s/shallow", path), F_OK)) {
 -                      if (option_local > 0)
 -                              warning(_("source repository is shallow, ignoring --local"));
 -                      is_local = 0;
 -              }
 -      }
 -      if (option_local > 0 && !is_local)
 -              warning(_("--local is ignored"));
  
        /* no need to be strict, transport_set_option() will validate it again */
        if (option_depth && atoi(option_depth) < 1)
                git_dir = mkpathdup("%s/.git", dir);
        }
  
+       atexit(remove_junk);
+       sigchain_push_common(remove_junk_on_signal);
        if (!option_bare) {
-               junk_work_tree = work_tree;
                if (safe_create_leading_directories_const(work_tree) < 0)
                        die_errno(_("could not create leading directories of '%s'"),
                                  work_tree);
                if (!dest_exists && mkdir(work_tree, 0777))
-                       die_errno(_("could not create work tree dir '%s'."),
+                       die_errno(_("could not create work tree dir '%s'"),
                                  work_tree);
+               junk_work_tree = work_tree;
                set_git_work_tree(work_tree);
        }
-       junk_git_dir = git_dir;
-       atexit(remove_junk);
-       sigchain_push_common(remove_junk_on_signal);
  
+       junk_git_dir = git_dir;
        if (safe_create_leading_directories_const(git_dir) < 0)
                die(_("could not create leading directories of '%s'"), git_dir);
  
  
        if (option_reference.nr)
                setup_reference();
 +      else if (option_dissociate) {
 +              warning(_("--dissociate given, but there is no --reference"));
 +              option_dissociate = 0;
 +      }
  
        fetch_pattern = value.buf;
        refspec = parse_fetch_refspec(1, &fetch_pattern);
  
        remote = remote_get(option_origin);
        transport = transport_get(remote, remote->url[0]);
 +      path = get_repo_path(remote->url[0], &is_bundle);
 +      is_local = option_local != 0 && path && !is_bundle;
 +      if (is_local) {
 +              if (option_depth)
 +                      warning(_("--depth is ignored in local clones; use file:// instead."));
 +              if (!access(mkpath("%s/shallow", path), F_OK)) {
 +                      if (option_local > 0)
 +                              warning(_("source repository is shallow, ignoring --local"));
 +                      is_local = 0;
 +              }
 +      }
 +      if (option_local > 0 && !is_local)
 +              warning(_("--local is ignored"));
        transport->cloning = 1;
  
        if (!transport->get_refs_list || (!is_local && !transport->fetch))
        transport_unlock_pack(transport);
        transport_disconnect(transport);
  
 +      if (option_dissociate)
 +              dissociate_from_references();
 +
        junk_mode = JUNK_LEAVE_REPO;
        err = checkout();
  
        strbuf_release(&key);
        strbuf_release(&value);
        junk_mode = JUNK_LEAVE_ALL;
 +
 +      free(refspec);
        return err;
  }