Merge branch 'ar/clone-honor-umask-at-top'
authorJunio C Hamano <gitster@pobox.com>
Mon, 16 Jul 2012 04:39:03 +0000 (21:39 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 16 Jul 2012 04:39:04 +0000 (21:39 -0700)
A handful of files and directories we create had tighter than
necessary permission bits when the user wanted to have group
writability (e.g. by setting "umask 002").

* ar/clone-honor-umask-at-top:
add: create ADD_EDIT.patch with mode 0666
rerere: make rr-cache fanout directory honor umask
Restore umasks influence on the permissions of work tree created by clone

1  2 
builtin/add.c
builtin/clone.c
rerere.c
diff --combined builtin/add.c
index 87446cf92a686ed69717c85a6b38302f59351460,d0b915143823aa95235a998fda81b121bb6bd48e..89dce56a240d69ccb224590bf134c9656d8a0522
@@@ -281,7 -281,7 +281,7 @@@ static int edit_patch(int argc, const c
        argc = setup_revisions(argc, argv, &rev, NULL);
        rev.diffopt.output_format = DIFF_FORMAT_PATCH;
        DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
-       out = open(file, O_CREAT | O_WRONLY, 0644);
+       out = open(file, O_CREAT | O_WRONLY, 0666);
        if (out < 0)
                die (_("Could not open '%s' for writing."), file);
        rev.diffopt.file = xfdopen(out, "w");
@@@ -443,9 -443,6 +443,9 @@@ int cmd_add(int argc, const char **argv
  
        if (pathspec) {
                int i;
 +              struct path_exclude_check check;
 +
 +              path_exclude_check_init(&check, &dir);
                if (!seen)
                        seen = find_used_pathspec(pathspec);
                for (i = 0; pathspec[i]; i++) {
                            && !file_exists(pathspec[i])) {
                                if (ignore_missing) {
                                        int dtype = DT_UNKNOWN;
 -                                      if (excluded(&dir, pathspec[i], &dtype))
 +                                      if (path_excluded(&check, pathspec[i], -1, &dtype))
                                                dir_add_ignored(&dir, pathspec[i], strlen(pathspec[i]));
                                } else
                                        die(_("pathspec '%s' did not match any files"),
                        }
                }
                free(seen);
 +              path_exclude_check_clear(&check);
        }
  
        plug_bulk_checkin();
diff --combined builtin/clone.c
index d3b7fdccecb7a368b4931d3a8c6ea0483509dccc,d7387e63ae9b84a7ca64235309870c11e10553e9..e314b0b6d2133b09307dc09fa256577e6cdba03a
@@@ -37,8 -37,8 +37,8 @@@ static const char * const builtin_clone
        NULL
  };
  
 -static int option_no_checkout, option_bare, option_mirror;
 -static int option_local, option_no_hardlinks, option_shared, option_recursive;
 +static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
 +static int option_local = -1, option_no_hardlinks, option_shared, option_recursive;
  static char *option_template, *option_depth;
  static char *option_origin = NULL;
  static char *option_branch = NULL;
@@@ -70,8 -70,8 +70,8 @@@ static struct option builtin_clone_opti
                PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
        OPT_BOOLEAN(0, "mirror", &option_mirror,
                    "create a mirror repository (implies bare)"),
 -      OPT_BOOLEAN('l', "local", &option_local,
 -                  "to clone from a local repository"),
 +      OPT_BOOL('l', "local", &option_local,
 +              "to clone from a local repository"),
        OPT_BOOLEAN(0, "no-hardlinks", &option_no_hardlinks,
                    "don't use local hardlinks, always copy"),
        OPT_BOOLEAN('s', "shared", &option_shared,
@@@ -92,8 -92,6 +92,8 @@@
                   "path to git-upload-pack on the remote"),
        OPT_STRING(0, "depth", &option_depth, "depth",
                    "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",
@@@ -232,6 -230,9 +232,6 @@@ static int add_one_reference(struct str
  {
        char *ref_git;
        struct strbuf alternate = STRBUF_INIT;
 -      struct remote *remote;
 -      struct transport *transport;
 -      const struct ref *extra;
  
        /* Beware: real_path() and mkpath() return static buffer */
        ref_git = xstrdup(real_path(item->string));
        strbuf_addf(&alternate, "%s/objects", ref_git);
        add_to_alternates_file(alternate.buf);
        strbuf_release(&alternate);
 -
 -      remote = remote_get(ref_git);
 -      transport = transport_get(remote, ref_git);
 -      for (extra = transport_get_remote_refs(transport); extra;
 -           extra = extra->next)
 -              add_extra_ref(extra->name, extra->old_sha1, 0);
 -
 -      transport_disconnect(transport);
        free(ref_git);
        return 0;
  }
@@@ -342,7 -351,7 +342,7 @@@ static void copy_or_link_directory(stru
                if (!option_no_hardlinks) {
                        if (!link(src->buf, dest->buf))
                                continue;
 -                      if (option_local)
 +                      if (option_local > 0)
                                die_errno(_("failed to create link '%s'"), dest->buf);
                        option_no_hardlinks = 1;
                }
        closedir(dir);
  }
  
 -static const struct ref *clone_local(const char *src_repo,
 -                                   const char *dest_repo)
 +static void clone_local(const char *src_repo, const char *dest_repo)
  {
 -      const struct ref *ret;
 -      struct remote *remote;
 -      struct transport *transport;
 -
        if (option_shared) {
                struct strbuf alt = STRBUF_INIT;
                strbuf_addf(&alt, "%s/objects", src_repo);
                strbuf_release(&dest);
        }
  
 -      remote = remote_get(src_repo);
 -      transport = transport_get(remote, src_repo);
 -      ret = transport_get_remote_refs(transport);
 -      transport_disconnect(transport);
        if (0 <= option_verbosity)
                printf(_("done.\n"));
 -      return ret;
  }
  
  static const char *junk_work_tree;
@@@ -401,26 -420,6 +401,26 @@@ static void remove_junk_on_signal(int s
        raise(signo);
  }
  
 +static struct ref *find_remote_branch(const struct ref *refs, const char *branch)
 +{
 +      struct ref *ref;
 +      struct strbuf head = STRBUF_INIT;
 +      strbuf_addstr(&head, "refs/heads/");
 +      strbuf_addstr(&head, branch);
 +      ref = find_ref_by_name(refs, head.buf);
 +      strbuf_release(&head);
 +
 +      if (ref)
 +              return ref;
 +
 +      strbuf_addstr(&head, "refs/tags/");
 +      strbuf_addstr(&head, branch);
 +      ref = find_ref_by_name(refs, head.buf);
 +      strbuf_release(&head);
 +
 +      return ref;
 +}
 +
  static struct ref *wanted_peer_refs(const struct ref *refs,
                struct refspec *refspec)
  {
        struct ref *local_refs = head;
        struct ref **tail = head ? &head->next : &local_refs;
  
 -      get_fetch_map(refs, refspec, &tail, 0);
 -      if (!option_mirror)
 +      if (option_single_branch) {
 +              struct ref *remote_head = NULL;
 +
 +              if (!option_branch)
 +                      remote_head = guess_remote_head(head, refs, 0);
 +              else {
 +                      local_refs = NULL;
 +                      tail = &local_refs;
 +                      remote_head = copy_ref(find_remote_branch(refs, option_branch));
 +              }
 +
 +              if (!remote_head && option_branch)
 +                      warning(_("Could not find remote branch %s to clone."),
 +                              option_branch);
 +              else {
 +                      get_fetch_map(remote_head, refspec, &tail, 0);
 +
 +                      /* if --branch=tag, pull the requested tag explicitly */
 +                      get_fetch_map(remote_head, tag_refspec, &tail, 0);
 +              }
 +      } else
 +              get_fetch_map(refs, refspec, &tail, 0);
 +
 +      if (!option_mirror && !option_single_branch)
                get_fetch_map(refs, tag_refspec, &tail, 0);
  
        return local_refs;
@@@ -464,134 -441,11 +464,134 @@@ static void write_remote_refs(const str
        for (r = local_refs; r; r = r->next) {
                if (!r->peer_ref)
                        continue;
 -              add_extra_ref(r->peer_ref->name, r->old_sha1, 0);
 +              add_packed_ref(r->peer_ref->name, r->old_sha1);
        }
  
        pack_refs(PACK_REFS_ALL);
 -      clear_extra_refs();
 +}
 +
 +static void write_followtags(const struct ref *refs, const char *msg)
 +{
 +      const struct ref *ref;
 +      for (ref = refs; ref; ref = ref->next) {
 +              if (prefixcmp(ref->name, "refs/tags/"))
 +                      continue;
 +              if (!suffixcmp(ref->name, "^{}"))
 +                      continue;
 +              if (!has_sha1_file(ref->old_sha1))
 +                      continue;
 +              update_ref(msg, ref->name, ref->old_sha1,
 +                         NULL, 0, DIE_ON_ERR);
 +      }
 +}
 +
 +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)
 +{
 +      if (refs) {
 +              write_remote_refs(mapped_refs);
 +              if (option_single_branch)
 +                      write_followtags(refs, msg);
 +      }
 +
 +      if (remote_head_points_at && !option_bare) {
 +              struct strbuf head_ref = STRBUF_INIT;
 +              strbuf_addstr(&head_ref, branch_top);
 +              strbuf_addstr(&head_ref, "HEAD");
 +              create_symref(head_ref.buf,
 +                            remote_head_points_at->peer_ref->name,
 +                            msg);
 +      }
 +}
 +
 +static void update_head(const struct ref *our, const struct ref *remote,
 +                      const char *msg)
 +{
 +      if (our && !prefixcmp(our->name, "refs/heads/")) {
 +              /* 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);
 +                      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);
 +      } else if (remote) {
 +              /*
 +               * We know remote HEAD points to a non-branch, or
 +               * HEAD points to a branch but we don't know which one.
 +               * Detach HEAD in all these cases.
 +               */
 +              update_ref(msg, "HEAD", remote->old_sha1,
 +                         NULL, REF_NODEREF, DIE_ON_ERR);
 +      }
 +}
 +
 +static int checkout(void)
 +{
 +      unsigned char sha1[20];
 +      char *head;
 +      struct lock_file *lock_file;
 +      struct unpack_trees_options opts;
 +      struct tree *tree;
 +      struct tree_desc t;
 +      int err = 0, fd;
 +
 +      if (option_no_checkout)
 +              return 0;
 +
 +      head = resolve_refdup("HEAD", sha1, 1, NULL);
 +      if (!head) {
 +              warning(_("remote HEAD refers to nonexistent ref, "
 +                        "unable to checkout.\n"));
 +              return 0;
 +      }
 +      if (!strcmp(head, "HEAD")) {
 +              if (advice_detached_head)
 +                      detach_advice(sha1_to_hex(sha1));
 +      } else {
 +              if (prefixcmp(head, "refs/heads/"))
 +                      die(_("HEAD not found below refs/heads!"));
 +      }
 +      free(head);
 +
 +      /* We need to be in the new work tree for the checkout */
 +      setup_work_tree();
 +
 +      lock_file = xcalloc(1, sizeof(struct lock_file));
 +      fd = hold_locked_index(lock_file, 1);
 +
 +      memset(&opts, 0, sizeof opts);
 +      opts.update = 1;
 +      opts.merge = 1;
 +      opts.fn = oneway_merge;
 +      opts.verbose_update = (option_verbosity >= 0);
 +      opts.src_index = &the_index;
 +      opts.dst_index = &the_index;
 +
 +      tree = parse_tree_indirect(sha1);
 +      parse_tree(tree);
 +      init_tree_desc(&t, tree->buffer, tree->size);
 +      unpack_trees(1, &t, &opts);
 +
 +      if (write_cache(fd, active_cache, active_nr) ||
 +          commit_locked_index(lock_file))
 +              die(_("unable to write new index file"));
 +
 +      err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
 +                      sha1_to_hex(sha1), "1", NULL);
 +
 +      if (!err && option_recursive)
 +              err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
 +
 +      return err;
  }
  
  static int write_one_config(const char *key, const char *value, void *data)
@@@ -621,13 -475,11 +621,13 @@@ int cmd_clone(int argc, const char **ar
        const struct ref *remote_head_points_at;
        const struct ref *our_head_points_at;
        struct ref *mapped_refs;
 +      const struct ref *ref;
        struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
        struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
        struct transport *transport = NULL;
 -      char *src_ref_prefix = "refs/heads/";
 -      int err = 0;
 +      const char *src_ref_prefix = "refs/heads/";
 +      struct remote *remote;
 +      int err = 0, complete_refs_before_fetch = 1;
  
        struct refspec *refspec;
        const char *fetch_pattern;
                usage_msg_opt(_("You must specify a repository to clone."),
                        builtin_clone_usage, builtin_clone_options);
  
 +      if (option_single_branch == -1)
 +              option_single_branch = option_depth ? 1 : 0;
 +
        if (option_mirror)
                option_bare = 1;
  
                die(_("repository '%s' does not exist"), repo_name);
        else
                repo = repo_name;
 -      is_local = path && !is_bundle;
 +      is_local = option_local != 0 && path && !is_bundle;
        if (is_local && option_depth)
                warning(_("--depth is ignored in local clones; use file:// instead."));
  
                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, 0755))
+               if (!dest_exists && mkdir(work_tree, 0777))
                        die_errno(_("could not create work tree dir '%s'."),
                                  work_tree);
                set_git_work_tree(work_tree);
  
        strbuf_reset(&value);
  
 -      if (is_local) {
 -              refs = clone_local(path, git_dir);
 -              mapped_refs = wanted_peer_refs(refs, refspec);
 -      } else {
 -              struct remote *remote = remote_get(option_origin);
 -              transport = transport_get(remote, remote->url[0]);
 +      remote = remote_get(option_origin);
 +      transport = transport_get(remote, remote->url[0]);
  
 +      if (!is_local) {
                if (!transport->get_refs_list || !transport->fetch)
                        die(_("Don't know how to clone %s"), transport->url);
  
                if (option_depth)
                        transport_set_option(transport, TRANS_OPT_DEPTH,
                                             option_depth);
 +              if (option_single_branch)
 +                      transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
  
                transport_set_verbosity(transport, option_verbosity, option_progress);
  
                if (option_upload_pack)
                        transport_set_option(transport, TRANS_OPT_UPLOADPACK,
                                             option_upload_pack);
 -
 -              refs = transport_get_remote_refs(transport);
 -              if (refs) {
 -                      mapped_refs = wanted_peer_refs(refs, refspec);
 -                      transport_fetch_refs(transport, mapped_refs);
 -              }
        }
  
 +      refs = transport_get_remote_refs(transport);
 +
        if (refs) {
 -              clear_extra_refs();
 +              mapped_refs = wanted_peer_refs(refs, refspec);
 +              /*
 +               * transport_get_remote_refs() may return refs with null sha-1
 +               * in mapped_refs (see struct transport->get_refs_list
 +               * comment). In that case we need fetch it early because
 +               * remote_head code below relies on it.
 +               *
 +               * for normal clones, transport_get_remote_refs() should
 +               * return reliable ref set, we can delay cloning until after
 +               * remote HEAD check.
 +               */
 +              for (ref = refs; ref; ref = ref->next)
 +                      if (is_null_sha1(ref->old_sha1)) {
 +                              complete_refs_before_fetch = 0;
 +                              break;
 +                      }
  
 -              write_remote_refs(mapped_refs);
 +              if (!is_local && !complete_refs_before_fetch)
 +                      transport_fetch_refs(transport, mapped_refs);
  
                remote_head = find_ref_by_name(refs, "HEAD");
                remote_head_points_at =
                        guess_remote_head(remote_head, mapped_refs, 0);
  
                if (option_branch) {
 -                      struct strbuf head = STRBUF_INIT;
 -                      strbuf_addstr(&head, src_ref_prefix);
 -                      strbuf_addstr(&head, option_branch);
                        our_head_points_at =
 -                              find_ref_by_name(mapped_refs, head.buf);
 -                      strbuf_release(&head);
 -
 -                      if (!our_head_points_at) {
 -                              warning(_("Remote branch %s not found in "
 -                                      "upstream %s, using HEAD instead"),
 -                                      option_branch, option_origin);
 -                              our_head_points_at = remote_head_points_at;
 -                      }
 +                              find_remote_branch(mapped_refs, option_branch);
 +
 +                      if (!our_head_points_at)
 +                              die(_("Remote branch %s not found in upstream %s"),
 +                                  option_branch, option_origin);
                }
                else
                        our_head_points_at = remote_head_points_at;
        }
        else {
                warning(_("You appear to have cloned an empty repository."));
 +              mapped_refs = NULL;
                our_head_points_at = NULL;
                remote_head_points_at = NULL;
                remote_head = NULL;
                                              "refs/heads/master");
        }
  
 -      if (remote_head_points_at && !option_bare) {
 -              struct strbuf head_ref = STRBUF_INIT;
 -              strbuf_addstr(&head_ref, branch_top.buf);
 -              strbuf_addstr(&head_ref, "HEAD");
 -              create_symref(head_ref.buf,
 -                            remote_head_points_at->peer_ref->name,
 -                            reflog_msg.buf);
 -      }
 +      if (is_local)
 +              clone_local(path, git_dir);
 +      else if (refs && complete_refs_before_fetch)
 +              transport_fetch_refs(transport, mapped_refs);
  
 -      if (our_head_points_at) {
 -              /* Local default branch link */
 -              create_symref("HEAD", our_head_points_at->name, NULL);
 -              if (!option_bare) {
 -                      const char *head = skip_prefix(our_head_points_at->name,
 -                                                     "refs/heads/");
 -                      update_ref(reflog_msg.buf, "HEAD",
 -                                 our_head_points_at->old_sha1,
 -                                 NULL, 0, DIE_ON_ERR);
 -                      install_branch_config(0, head, option_origin,
 -                                            our_head_points_at->name);
 -              }
 -      } else if (remote_head) {
 -              /* Source had detached HEAD pointing somewhere. */
 -              if (!option_bare) {
 -                      update_ref(reflog_msg.buf, "HEAD",
 -                                 remote_head->old_sha1,
 -                                 NULL, REF_NODEREF, DIE_ON_ERR);
 -                      our_head_points_at = remote_head;
 -              }
 -      } else {
 -              /* Nothing to checkout out */
 -              if (!option_no_checkout)
 -                      warning(_("remote HEAD refers to nonexistent ref, "
 -                              "unable to checkout.\n"));
 -              option_no_checkout = 1;
 -      }
 +      update_remote_refs(refs, mapped_refs, remote_head_points_at,
 +                         branch_top.buf, reflog_msg.buf);
  
 -      if (transport) {
 -              transport_unlock_pack(transport);
 -              transport_disconnect(transport);
 -      }
 +      update_head(our_head_points_at, remote_head, reflog_msg.buf);
  
 -      if (!option_no_checkout) {
 -              struct lock_file *lock_file = xcalloc(1, sizeof(struct lock_file));
 -              struct unpack_trees_options opts;
 -              struct tree *tree;
 -              struct tree_desc t;
 -              int fd;
 -
 -              /* We need to be in the new work tree for the checkout */
 -              setup_work_tree();
 -
 -              fd = hold_locked_index(lock_file, 1);
 -
 -              memset(&opts, 0, sizeof opts);
 -              opts.update = 1;
 -              opts.merge = 1;
 -              opts.fn = oneway_merge;
 -              opts.verbose_update = (option_verbosity > 0);
 -              opts.src_index = &the_index;
 -              opts.dst_index = &the_index;
 -
 -              tree = parse_tree_indirect(our_head_points_at->old_sha1);
 -              parse_tree(tree);
 -              init_tree_desc(&t, tree->buffer, tree->size);
 -              unpack_trees(1, &t, &opts);
 -
 -              if (write_cache(fd, active_cache, active_nr) ||
 -                  commit_locked_index(lock_file))
 -                      die(_("unable to write new index file"));
 -
 -              err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
 -                              sha1_to_hex(our_head_points_at->old_sha1), "1",
 -                              NULL);
 -
 -              if (!err && option_recursive)
 -                      err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
 -      }
 +      transport_unlock_pack(transport);
 +      transport_disconnect(transport);
 +
 +      err = checkout();
  
        strbuf_release(&reflog_msg);
        strbuf_release(&branch_top);
diff --combined rerere.c
index da18fc37dfc6cd9071b0ef5fc057048724ff3025,651c5def92e3f864a99ed7e97d008aa04cce3782..b57a34daa718a5ecf9750d3068a8ecee4f3e261f
+++ b/rerere.c
@@@ -524,7 -524,7 +524,7 @@@ static int do_plain_rerere(struct strin
                                continue;
                        hex = xstrdup(sha1_to_hex(sha1));
                        string_list_insert(rr, path)->util = hex;
-                       if (mkdir(git_path("rr-cache/%s", hex), 0755))
+                       if (mkdir_in_gitdir(git_path("rr-cache/%s", hex)))
                                continue;
                        handle_file(path, NULL, rerere_path(hex, "preimage"));
                        fprintf(stderr, "Recorded preimage for '%s'\n", path);
  
                if (has_rerere_resolution(name)) {
                        if (!merge(name, path)) {
 -                              if (rerere_autoupdate)
 +                              const char *msg;
 +                              if (rerere_autoupdate) {
                                        string_list_insert(&update, path);
 -                              fprintf(stderr,
 -                                      "%s '%s' using previous resolution.\n",
 -                                      rerere_autoupdate
 -                                      ? "Staged" : "Resolved",
 -                                      path);
 +                                      msg = "Staged '%s' using previous resolution.\n";
 +                              } else
 +                                      msg = "Resolved '%s' using previous resolution.\n";
 +                              fprintf(stderr, msg, path);
                                goto mark_resolved;
                        }
                }