Merge branch 'nd/parseopt-completion'
authorJunio C Hamano <gitster@pobox.com>
Wed, 14 Mar 2018 19:01:06 +0000 (12:01 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 14 Mar 2018 19:01:07 +0000 (12:01 -0700)
Teach parse-options API an option to help the completion script,
and make use of the mechanism in command line completion.

* nd/parseopt-completion: (45 commits)
completion: more subcommands in _git_notes()
completion: complete --{reuse,reedit}-message= for all notes subcmds
completion: simplify _git_notes
completion: don't set PARSE_OPT_NOCOMPLETE on --rerere-autoupdate
completion: use __gitcomp_builtin in _git_worktree
completion: use __gitcomp_builtin in _git_tag
completion: use __gitcomp_builtin in _git_status
completion: use __gitcomp_builtin in _git_show_branch
completion: use __gitcomp_builtin in _git_rm
completion: use __gitcomp_builtin in _git_revert
completion: use __gitcomp_builtin in _git_reset
completion: use __gitcomp_builtin in _git_replace
remote: force completing --mirror= instead of --mirror
completion: use __gitcomp_builtin in _git_remote
completion: use __gitcomp_builtin in _git_push
completion: use __gitcomp_builtin in _git_pull
completion: use __gitcomp_builtin in _git_notes
completion: use __gitcomp_builtin in _git_name_rev
completion: use __gitcomp_builtin in _git_mv
completion: use __gitcomp_builtin in _git_merge_base
...

15 files changed:
1  2 
apply.c
builtin/checkout.c
builtin/fetch.c
builtin/gc.c
builtin/grep.c
builtin/mv.c
builtin/notes.c
builtin/pull.c
builtin/remote.c
builtin/replace.c
builtin/submodule--helper.c
builtin/tag.c
builtin/worktree.c
contrib/completion/git-completion.bash
parse-options.c
diff --combined apply.c
index f8f15edc7c7b2b409201b6168a68fdd96ed0c0ba,65eb37e6a4ce15c235940131881c8c3ac4be9919..134dc7ba78cddd99406b78a97898bd8a32393b4c
+++ b/apply.c
@@@ -950,7 -950,7 +950,7 @@@ static int gitdiff_verify_name(struct a
                }
                free(another);
        } else {
 -              if (!starts_with(line, "/dev/null\n"))
 +              if (!is_dev_null(line))
                        return error(_("git apply: bad git-diff - expected /dev/null on line %d"), state->linenr);
        }
  
@@@ -2263,8 -2263,8 +2263,8 @@@ static void show_stats(struct apply_sta
  static int read_old_data(struct stat *st, struct patch *patch,
                         const char *path, struct strbuf *buf)
  {
 -      enum safe_crlf safe_crlf = patch->crlf_in_old ?
 -              SAFE_CRLF_KEEP_CRLF : SAFE_CRLF_RENORMALIZE;
 +      int conv_flags = patch->crlf_in_old ?
 +              CONV_EOL_KEEP_CRLF : CONV_EOL_RENORMALIZE;
        switch (st->st_mode & S_IFMT) {
        case S_IFLNK:
                if (strbuf_readlink(buf, path, st->st_size) < 0)
                 * should never look at the index when explicit crlf option
                 * is given.
                 */
 -              convert_to_git(NULL, path, buf->buf, buf->len, buf, safe_crlf);
 +              convert_to_git(NULL, path, buf->buf, buf->len, buf, conv_flags);
                return 0;
        default:
                return -1;
@@@ -2301,7 -2301,7 +2301,7 @@@ static void update_pre_post_images(stru
                                   size_t len, size_t postlen)
  {
        int i, ctx, reduced;
 -      char *new, *old, *fixed;
 +      char *new_buf, *old_buf, *fixed;
        struct image fixed_preimage;
  
        /*
         * We trust the caller to tell us if the update can be done
         * in place (postlen==0) or not.
         */
 -      old = postimage->buf;
 +      old_buf = postimage->buf;
        if (postlen)
 -              new = postimage->buf = xmalloc(postlen);
 +              new_buf = postimage->buf = xmalloc(postlen);
        else
 -              new = old;
 +              new_buf = old_buf;
        fixed = preimage->buf;
  
        for (i = reduced = ctx = 0; i < postimage->nr; i++) {
                size_t l_len = postimage->line[i].len;
                if (!(postimage->line[i].flag & LINE_COMMON)) {
                        /* an added line -- no counterparts in preimage */
 -                      memmove(new, old, l_len);
 -                      old += l_len;
 -                      new += l_len;
 +                      memmove(new_buf, old_buf, l_len);
 +                      old_buf += l_len;
 +                      new_buf += l_len;
                        continue;
                }
  
                /* a common context -- skip it in the original postimage */
 -              old += l_len;
 +              old_buf += l_len;
  
                /* and find the corresponding one in the fixed preimage */
                while (ctx < preimage->nr &&
  
                /* and copy it in, while fixing the line length */
                l_len = preimage->line[ctx].len;
 -              memcpy(new, fixed, l_len);
 -              new += l_len;
 +              memcpy(new_buf, fixed, l_len);
 +              new_buf += l_len;
                fixed += l_len;
                postimage->line[i].len = l_len;
                ctx++;
        }
  
        if (postlen
 -          ? postlen < new - postimage->buf
 -          : postimage->len < new - postimage->buf)
 +          ? postlen < new_buf - postimage->buf
 +          : postimage->len < new_buf - postimage->buf)
                die("BUG: caller miscounted postlen: asked %d, orig = %d, used = %d",
 -                  (int)postlen, (int) postimage->len, (int)(new - postimage->buf));
 +                  (int)postlen, (int) postimage->len, (int)(new_buf - postimage->buf));
  
        /* Fix the length of the whole thing */
 -      postimage->len = new - postimage->buf;
 +      postimage->len = new_buf - postimage->buf;
        postimage->nr -= reduced;
  }
  
  static int line_by_line_fuzzy_match(struct image *img,
                                    struct image *preimage,
                                    struct image *postimage,
 -                                  unsigned long try,
 -                                  int try_lno,
 +                                  unsigned long current,
 +                                  int current_lno,
                                    int preimage_limit)
  {
        int i;
  
        for (i = 0; i < preimage_limit; i++) {
                size_t prelen = preimage->line[i].len;
 -              size_t imglen = img->line[try_lno+i].len;
 +              size_t imglen = img->line[current_lno+i].len;
  
 -              if (!fuzzy_matchlines(img->buf + try + imgoff, imglen,
 +              if (!fuzzy_matchlines(img->buf + current + imgoff, imglen,
                                      preimage->buf + preoff, prelen))
                        return 0;
                if (preimage->line[i].flag & LINE_COMMON)
         */
        extra_chars = preimage_end - preimage_eof;
        strbuf_init(&fixed, imgoff + extra_chars);
 -      strbuf_add(&fixed, img->buf + try, imgoff);
 +      strbuf_add(&fixed, img->buf + current, imgoff);
        strbuf_add(&fixed, preimage_eof, extra_chars);
        fixed_buf = strbuf_detach(&fixed, &fixed_len);
        update_pre_post_images(preimage, postimage,
@@@ -2455,8 -2455,8 +2455,8 @@@ static int match_fragment(struct apply_
                          struct image *img,
                          struct image *preimage,
                          struct image *postimage,
 -                        unsigned long try,
 -                        int try_lno,
 +                        unsigned long current,
 +                        int current_lno,
                          unsigned ws_rule,
                          int match_beginning, int match_end)
  {
        size_t fixed_len, postlen;
        int preimage_limit;
  
 -      if (preimage->nr + try_lno <= img->nr) {
 +      if (preimage->nr + current_lno <= img->nr) {
                /*
                 * The hunk falls within the boundaries of img.
                 */
                preimage_limit = preimage->nr;
 -              if (match_end && (preimage->nr + try_lno != img->nr))
 +              if (match_end && (preimage->nr + current_lno != img->nr))
                        return 0;
        } else if (state->ws_error_action == correct_ws_error &&
                   (ws_rule & WS_BLANK_AT_EOF)) {
                 * match with img, and the remainder of the preimage
                 * must be blank.
                 */
 -              preimage_limit = img->nr - try_lno;
 +              preimage_limit = img->nr - current_lno;
        } else {
                /*
                 * The hunk extends beyond the end of the img and
                return 0;
        }
  
 -      if (match_beginning && try_lno)
 +      if (match_beginning && current_lno)
                return 0;
  
        /* Quick hash check */
        for (i = 0; i < preimage_limit; i++)
 -              if ((img->line[try_lno + i].flag & LINE_PATCHED) ||
 -                  (preimage->line[i].hash != img->line[try_lno + i].hash))
 +              if ((img->line[current_lno + i].flag & LINE_PATCHED) ||
 +                  (preimage->line[i].hash != img->line[current_lno + i].hash))
                        return 0;
  
        if (preimage_limit == preimage->nr) {
                /*
                 * Do we have an exact match?  If we were told to match
 -               * at the end, size must be exactly at try+fragsize,
 -               * otherwise try+fragsize must be still within the preimage,
 +               * at the end, size must be exactly at current+fragsize,
 +               * otherwise current+fragsize must be still within the preimage,
                 * and either case, the old piece should match the preimage
                 * exactly.
                 */
                if ((match_end
 -                   ? (try + preimage->len == img->len)
 -                   : (try + preimage->len <= img->len)) &&
 -                  !memcmp(img->buf + try, preimage->buf, preimage->len))
 +                   ? (current + preimage->len == img->len)
 +                   : (current + preimage->len <= img->len)) &&
 +                  !memcmp(img->buf + current, preimage->buf, preimage->len))
                        return 1;
        } else {
                /*
         */
        if (state->ws_ignore_action == ignore_ws_change)
                return line_by_line_fuzzy_match(img, preimage, postimage,
 -                                              try, try_lno, preimage_limit);
 +                                              current, current_lno, preimage_limit);
  
        if (state->ws_error_action != correct_ws_error)
                return 0;
         */
        strbuf_init(&fixed, preimage->len + 1);
        orig = preimage->buf;
 -      target = img->buf + try;
 +      target = img->buf + current;
        for (i = 0; i < preimage_limit; i++) {
                size_t oldlen = preimage->line[i].len;
 -              size_t tgtlen = img->line[try_lno + i].len;
 +              size_t tgtlen = img->line[current_lno + i].len;
                size_t fixstart = fixed.len;
                struct strbuf tgtfix;
                int match;
@@@ -2666,8 -2666,8 +2666,8 @@@ static int find_pos(struct apply_state 
                    int match_beginning, int match_end)
  {
        int i;
 -      unsigned long backwards, forwards, try;
 -      int backwards_lno, forwards_lno, try_lno;
 +      unsigned long backwards, forwards, current;
 +      int backwards_lno, forwards_lno, current_lno;
  
        /*
         * If match_beginning or match_end is specified, there is no
        if ((size_t) line > img->nr)
                line = img->nr;
  
 -      try = 0;
 +      current = 0;
        for (i = 0; i < line; i++)
 -              try += img->line[i].len;
 +              current += img->line[i].len;
  
        /*
         * There's probably some smart way to do this, but I'll leave
         * that to the smart and beautiful people. I'm simple and stupid.
         */
 -      backwards = try;
 +      backwards = current;
        backwards_lno = line;
 -      forwards = try;
 +      forwards = current;
        forwards_lno = line;
 -      try_lno = line;
 +      current_lno = line;
  
        for (i = 0; ; i++) {
                if (match_fragment(state, img, preimage, postimage,
 -                                 try, try_lno, ws_rule,
 +                                 current, current_lno, ws_rule,
                                   match_beginning, match_end))
 -                      return try_lno;
 +                      return current_lno;
  
        again:
                if (backwards_lno == 0 && forwards_lno == img->nr)
                        }
                        backwards_lno--;
                        backwards -= img->line[backwards_lno].len;
 -                      try = backwards;
 -                      try_lno = backwards_lno;
 +                      current = backwards;
 +                      current_lno = backwards_lno;
                } else {
                        if (forwards_lno == img->nr) {
                                i++;
                        }
                        forwards += img->line[forwards_lno].len;
                        forwards_lno++;
 -                      try = forwards;
 -                      try_lno = forwards_lno;
 +                      current = forwards;
 +                      current_lno = forwards_lno;
                }
  
        }
@@@ -3154,7 -3154,7 +3154,7 @@@ static int apply_binary(struct apply_st
                 * See if the old one matches what the patch
                 * applies to.
                 */
 -              hash_sha1_file(img->buf, img->len, blob_type, oid.hash);
 +              hash_object_file(img->buf, img->len, blob_type, &oid);
                if (strcmp(oid_to_hex(&oid), patch->old_sha1_prefix))
                        return error(_("the patch applies to '%s' (%s), "
                                       "which does not match the "
                                     name);
  
                /* verify that the result matches */
 -              hash_sha1_file(img->buf, img->len, blob_type, oid.hash);
 +              hash_object_file(img->buf, img->len, blob_type, &oid);
                if (strcmp(oid_to_hex(&oid), patch->new_sha1_prefix))
                        return error(_("binary patch to '%s' creates incorrect result (expecting %s, got %s)"),
                                name, patch->new_sha1_prefix, oid_to_hex(&oid));
@@@ -3554,7 -3554,7 +3554,7 @@@ static int try_threeway(struct apply_st
  
        /* Preimage the patch was prepared for */
        if (patch->is_new)
 -              write_sha1_file("", 0, blob_type, pre_oid.hash);
 +              write_object_file("", 0, blob_type, &pre_oid);
        else if (get_oid(patch->old_sha1_prefix, &pre_oid) ||
                 read_blob_object(&buf, &pre_oid, patch->old_mode))
                return error(_("repository lacks the necessary blob to fall back on 3-way merge."));
                return -1;
        }
        /* post_oid is theirs */
 -      write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, post_oid.hash);
 +      write_object_file(tmp_image.buf, tmp_image.len, blob_type, &post_oid);
        clear_image(&tmp_image);
  
        /* our_oid is ours */
                        return error(_("cannot read the current contents of '%s'"),
                                     patch->old_name);
        }
 -      write_sha1_file(tmp_image.buf, tmp_image.len, blob_type, our_oid.hash);
 +      write_object_file(tmp_image.buf, tmp_image.len, blob_type, &our_oid);
        clear_image(&tmp_image);
  
        /* in-core three-way merge between post and our using pre as base */
@@@ -4163,30 -4163,30 +4163,30 @@@ static void show_mode_change(struct pat
  static void show_rename_copy(struct patch *p)
  {
        const char *renamecopy = p->is_rename ? "rename" : "copy";
 -      const char *old, *new;
 +      const char *old_name, *new_name;
  
        /* Find common prefix */
 -      old = p->old_name;
 -      new = p->new_name;
 +      old_name = p->old_name;
 +      new_name = p->new_name;
        while (1) {
                const char *slash_old, *slash_new;
 -              slash_old = strchr(old, '/');
 -              slash_new = strchr(new, '/');
 +              slash_old = strchr(old_name, '/');
 +              slash_new = strchr(new_name, '/');
                if (!slash_old ||
                    !slash_new ||
 -                  slash_old - old != slash_new - new ||
 -                  memcmp(old, new, slash_new - new))
 +                  slash_old - old_name != slash_new - new_name ||
 +                  memcmp(old_name, new_name, slash_new - new_name))
                        break;
 -              old = slash_old + 1;
 -              new = slash_new + 1;
 +              old_name = slash_old + 1;
 +              new_name = slash_new + 1;
        }
 -      /* p->old_name thru old is the common prefix, and old and new
 +      /* p->old_name thru old_name is the common prefix, and old_name and new_name
         * through the end of names are renames
         */
 -      if (old != p->old_name)
 +      if (old_name != p->old_name)
                printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
 -                     (int)(old - p->old_name), p->old_name,
 -                     old, new, p->score);
 +                     (int)(old_name - p->old_name), p->old_name,
 +                     old_name, new_name, p->score);
        else
                printf(" %s %s => %s (%d%%)\n", renamecopy,
                       p->old_name, p->new_name, p->score);
@@@ -4291,7 -4291,7 +4291,7 @@@ static int add_index_file(struct apply_
                        }
                        fill_stat_cache_info(ce, &st);
                }
 -              if (write_sha1_file(buf, size, blob_type, ce->oid.hash) < 0) {
 +              if (write_object_file(buf, size, blob_type, &ce->oid) < 0) {
                        free(ce);
                        return error(_("unable to create backing store "
                                       "for newly created file %s"), path);
@@@ -4943,8 -4943,9 +4943,9 @@@ int apply_parse_options(int argc, cons
                        N_("make sure the patch is applicable to the current index")),
                OPT_BOOL(0, "cached", &state->cached,
                        N_("apply a patch without touching the working tree")),
-               OPT_BOOL(0, "unsafe-paths", &state->unsafe_paths,
-                       N_("accept a patch that touches outside the working area")),
+               OPT_BOOL_F(0, "unsafe-paths", &state->unsafe_paths,
+                          N_("accept a patch that touches outside the working area"),
+                          PARSE_OPT_NOCOMPLETE),
                OPT_BOOL(0, "apply", force_apply,
                        N_("also apply the patch (use with --stat/--summary/--check)")),
                OPT_BOOL('3', "3way", &state->threeway,
diff --combined builtin/checkout.c
index 8f4dfb104662baa3f32c350cf58e0f2b797dff29,a6218024a6c0dc73a48a1d950483eba8c826b67f..d76e13c8524003fcc5c55d706c1177f66520b9d4
@@@ -54,14 -54,14 +54,14 @@@ struct checkout_opts 
        struct tree *source_tree;
  };
  
 -static int post_checkout_hook(struct commit *old, struct commit *new,
 +static int post_checkout_hook(struct commit *old_commit, struct commit *new_commit,
                              int changed)
  {
        return run_hook_le(NULL, "post-checkout",
 -                         oid_to_hex(old ? &old->object.oid : &null_oid),
 -                         oid_to_hex(new ? &new->object.oid : &null_oid),
 +                         oid_to_hex(old_commit ? &old_commit->object.oid : &null_oid),
 +                         oid_to_hex(new_commit ? &new_commit->object.oid : &null_oid),
                           changed ? "1" : "0", NULL);
 -      /* "new" can be NULL when checking out from the index before
 +      /* "new_commit" can be NULL when checking out from the index before
           a commit exists. */
  
  }
@@@ -227,7 -227,8 +227,7 @@@ static int checkout_merged(int pos, con
         * (it also writes the merge result to the object database even
         * when it may contain conflicts).
         */
 -      if (write_sha1_file(result_buf.ptr, result_buf.size,
 -                          blob_type, oid.hash))
 +      if (write_object_file(result_buf.ptr, result_buf.size, blob_type, &oid))
                die(_("Unable to add merge result for '%s'"), path);
        free(result_buf.ptr);
        ce = make_cache_entry(mode, oid.hash, path, 2, 0);
@@@ -471,8 -472,8 +471,8 @@@ static void setup_branch_path(struct br
  }
  
  static int merge_working_tree(const struct checkout_opts *opts,
 -                            struct branch_info *old,
 -                            struct branch_info *new,
 +                            struct branch_info *old_branch_info,
 +                            struct branch_info *new_branch_info,
                              int *writeout_error)
  {
        int ret;
  
        resolve_undo_clear();
        if (opts->force) {
 -              ret = reset_tree(new->commit->tree, opts, 1, writeout_error);
 +              ret = reset_tree(new_branch_info->commit->tree, opts, 1, writeout_error);
                if (ret)
                        return ret;
        } else {
                topts.initial_checkout = is_cache_unborn();
                topts.update = 1;
                topts.merge = 1;
 -              topts.gently = opts->merge && old->commit;
 +              topts.gently = opts->merge && old_branch_info->commit;
                topts.verbose_update = opts->show_progress;
                topts.fn = twoway_merge;
                if (opts->overwrite_ignore) {
                        topts.dir->flags |= DIR_SHOW_IGNORED;
                        setup_standard_excludes(topts.dir);
                }
 -              tree = parse_tree_indirect(old->commit ?
 -                                         &old->commit->object.oid :
 +              tree = parse_tree_indirect(old_branch_info->commit ?
 +                                         &old_branch_info->commit->object.oid :
                                           the_hash_algo->empty_tree);
                init_tree_desc(&trees[0], tree->buffer, tree->size);
 -              tree = parse_tree_indirect(&new->commit->object.oid);
 +              tree = parse_tree_indirect(&new_branch_info->commit->object.oid);
                init_tree_desc(&trees[1], tree->buffer, tree->size);
  
                ret = unpack_trees(2, trees, &topts);
                                return 1;
  
                        /*
 -                       * Without old->commit, the below is the same as
 +                       * Without old_branch_info->commit, the below is the same as
                         * the two-tree unpack we already tried and failed.
                         */
 -                      if (!old->commit)
 +                      if (!old_branch_info->commit)
                                return 1;
  
                        /* Do more real merge */
                        o.verbosity = 0;
                        work = write_tree_from_memory(&o);
  
 -                      ret = reset_tree(new->commit->tree, opts, 1,
 +                      ret = reset_tree(new_branch_info->commit->tree, opts, 1,
                                         writeout_error);
                        if (ret)
                                return ret;
 -                      o.ancestor = old->name;
 -                      o.branch1 = new->name;
 +                      o.ancestor = old_branch_info->name;
 +                      o.branch1 = new_branch_info->name;
                        o.branch2 = "local";
 -                      ret = merge_trees(&o, new->commit->tree, work,
 -                              old->commit->tree, &result);
 +                      ret = merge_trees(&o, new_branch_info->commit->tree, work,
 +                              old_branch_info->commit->tree, &result);
                        if (ret < 0)
                                exit(128);
 -                      ret = reset_tree(new->commit->tree, opts, 0,
 +                      ret = reset_tree(new_branch_info->commit->tree, opts, 0,
                                         writeout_error);
                        strbuf_release(&o.obuf);
                        if (ret)
                die(_("unable to write new index file"));
  
        if (!opts->force && !opts->quiet)
 -              show_local_changes(&new->commit->object, &opts->diff_options);
 +              show_local_changes(&new_branch_info->commit->object, &opts->diff_options);
  
        return 0;
  }
  
 -static void report_tracking(struct branch_info *new)
 +static void report_tracking(struct branch_info *new_branch_info)
  {
        struct strbuf sb = STRBUF_INIT;
 -      struct branch *branch = branch_get(new->name);
 +      struct branch *branch = branch_get(new_branch_info->name);
  
 -      if (!format_tracking_info(branch, &sb))
 +      if (!format_tracking_info(branch, &sb, AHEAD_BEHIND_FULL))
                return;
        fputs(sb.buf, stdout);
        strbuf_release(&sb);
  }
  
  static void update_refs_for_switch(const struct checkout_opts *opts,
 -                                 struct branch_info *old,
 -                                 struct branch_info *new)
 +                                 struct branch_info *old_branch_info,
 +                                 struct branch_info *new_branch_info)
  {
        struct strbuf msg = STRBUF_INIT;
        const char *old_desc, *reflog_msg;
                        free(refname);
                }
                else
 -                      create_branch(opts->new_branch, new->name,
 +                      create_branch(opts->new_branch, new_branch_info->name,
                                      opts->new_branch_force ? 1 : 0,
                                      opts->new_branch_force ? 1 : 0,
                                      opts->new_branch_log,
                                      opts->quiet,
                                      opts->track);
 -              new->name = opts->new_branch;
 -              setup_branch_path(new);
 +              new_branch_info->name = opts->new_branch;
 +              setup_branch_path(new_branch_info);
        }
  
 -      old_desc = old->name;
 -      if (!old_desc && old->commit)
 -              old_desc = oid_to_hex(&old->commit->object.oid);
 +      old_desc = old_branch_info->name;
 +      if (!old_desc && old_branch_info->commit)
 +              old_desc = oid_to_hex(&old_branch_info->commit->object.oid);
  
        reflog_msg = getenv("GIT_REFLOG_ACTION");
        if (!reflog_msg)
                strbuf_addf(&msg, "checkout: moving from %s to %s",
 -                      old_desc ? old_desc : "(invalid)", new->name);
 +                      old_desc ? old_desc : "(invalid)", new_branch_info->name);
        else
                strbuf_insert(&msg, 0, reflog_msg, strlen(reflog_msg));
  
 -      if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
 +      if (!strcmp(new_branch_info->name, "HEAD") && !new_branch_info->path && !opts->force_detach) {
                /* Nothing to do. */
 -      } else if (opts->force_detach || !new->path) {  /* No longer on any branch. */
 -              update_ref(msg.buf, "HEAD", &new->commit->object.oid, NULL,
 +      } else if (opts->force_detach || !new_branch_info->path) {      /* No longer on any branch. */
 +              update_ref(msg.buf, "HEAD", &new_branch_info->commit->object.oid, NULL,
                           REF_NO_DEREF, UPDATE_REFS_DIE_ON_ERR);
                if (!opts->quiet) {
 -                      if (old->path &&
 +                      if (old_branch_info->path &&
                            advice_detached_head && !opts->force_detach)
 -                              detach_advice(new->name);
 -                      describe_detached_head(_("HEAD is now at"), new->commit);
 +                              detach_advice(new_branch_info->name);
 +                      describe_detached_head(_("HEAD is now at"), new_branch_info->commit);
                }
 -      } else if (new->path) { /* Switch branches. */
 -              if (create_symref("HEAD", new->path, msg.buf) < 0)
 +      } else if (new_branch_info->path) {     /* Switch branches. */
 +              if (create_symref("HEAD", new_branch_info->path, msg.buf) < 0)
                        die(_("unable to update HEAD"));
                if (!opts->quiet) {
 -                      if (old->path && !strcmp(new->path, old->path)) {
 +                      if (old_branch_info->path && !strcmp(new_branch_info->path, old_branch_info->path)) {
                                if (opts->new_branch_force)
                                        fprintf(stderr, _("Reset branch '%s'\n"),
 -                                              new->name);
 +                                              new_branch_info->name);
                                else
                                        fprintf(stderr, _("Already on '%s'\n"),
 -                                              new->name);
 +                                              new_branch_info->name);
                        } else if (opts->new_branch) {
                                if (opts->branch_exists)
 -                                      fprintf(stderr, _("Switched to and reset branch '%s'\n"), new->name);
 +                                      fprintf(stderr, _("Switched to and reset branch '%s'\n"), new_branch_info->name);
                                else
 -                                      fprintf(stderr, _("Switched to a new branch '%s'\n"), new->name);
 +                                      fprintf(stderr, _("Switched to a new branch '%s'\n"), new_branch_info->name);
                        } else {
                                fprintf(stderr, _("Switched to branch '%s'\n"),
 -                                      new->name);
 +                                      new_branch_info->name);
                        }
                }
 -              if (old->path && old->name) {
 -                      if (!ref_exists(old->path) && reflog_exists(old->path))
 -                              delete_reflog(old->path);
 +              if (old_branch_info->path && old_branch_info->name) {
 +                      if (!ref_exists(old_branch_info->path) && reflog_exists(old_branch_info->path))
 +                              delete_reflog(old_branch_info->path);
                }
        }
        remove_branch_state();
        strbuf_release(&msg);
        if (!opts->quiet &&
 -          (new->path || (!opts->force_detach && !strcmp(new->name, "HEAD"))))
 -              report_tracking(new);
 +          (new_branch_info->path || (!opts->force_detach && !strcmp(new_branch_info->name, "HEAD"))))
 +              report_tracking(new_branch_info);
  }
  
  static int add_pending_uninteresting_ref(const char *refname,
@@@ -786,10 -787,10 +786,10 @@@ static void suggest_reattach(struct com
   * HEAD.  If it is not reachable from any ref, this is the last chance
   * for the user to do so without resorting to reflog.
   */
 -static void orphaned_commit_warning(struct commit *old, struct commit *new)
 +static void orphaned_commit_warning(struct commit *old_commit, struct commit *new_commit)
  {
        struct rev_info revs;
 -      struct object *object = &old->object;
 +      struct object *object = &old_commit->object;
  
        init_revisions(&revs, NULL);
        setup_revisions(0, NULL, &revs, NULL);
        add_pending_object(&revs, object, oid_to_hex(&object->oid));
  
        for_each_ref(add_pending_uninteresting_ref, &revs);
 -      add_pending_oid(&revs, "HEAD", &new->object.oid, UNINTERESTING);
 +      add_pending_oid(&revs, "HEAD", &new_commit->object.oid, UNINTERESTING);
  
        if (prepare_revision_walk(&revs))
                die(_("internal error in revision walk"));
 -      if (!(old->object.flags & UNINTERESTING))
 -              suggest_reattach(old, &revs);
 +      if (!(old_commit->object.flags & UNINTERESTING))
 +              suggest_reattach(old_commit, &revs);
        else
 -              describe_detached_head(_("Previous HEAD position was"), old);
 +              describe_detached_head(_("Previous HEAD position was"), old_commit);
  
        /* Clean up objects used, as they will be reused. */
        clear_commit_marks_all(ALL_REV_FLAGS);
  }
  
  static int switch_branches(const struct checkout_opts *opts,
 -                         struct branch_info *new)
 +                         struct branch_info *new_branch_info)
  {
        int ret = 0;
 -      struct branch_info old;
 +      struct branch_info old_branch_info;
        void *path_to_free;
        struct object_id rev;
        int flag, writeout_error = 0;
 -      memset(&old, 0, sizeof(old));
 -      old.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag);
 -      if (old.path)
 -              old.commit = lookup_commit_reference_gently(&rev, 1);
 +      memset(&old_branch_info, 0, sizeof(old_branch_info));
 +      old_branch_info.path = path_to_free = resolve_refdup("HEAD", 0, &rev, &flag);
 +      if (old_branch_info.path)
 +              old_branch_info.commit = lookup_commit_reference_gently(&rev, 1);
        if (!(flag & REF_ISSYMREF))
 -              old.path = NULL;
 +              old_branch_info.path = NULL;
  
 -      if (old.path)
 -              skip_prefix(old.path, "refs/heads/", &old.name);
 +      if (old_branch_info.path)
 +              skip_prefix(old_branch_info.path, "refs/heads/", &old_branch_info.name);
  
 -      if (!new->name) {
 -              new->name = "HEAD";
 -              new->commit = old.commit;
 -              if (!new->commit)
 +      if (!new_branch_info->name) {
 +              new_branch_info->name = "HEAD";
 +              new_branch_info->commit = old_branch_info.commit;
 +              if (!new_branch_info->commit)
                        die(_("You are on a branch yet to be born"));
 -              parse_commit_or_die(new->commit);
 +              parse_commit_or_die(new_branch_info->commit);
        }
  
 -      ret = merge_working_tree(opts, &old, new, &writeout_error);
 +      ret = merge_working_tree(opts, &old_branch_info, new_branch_info, &writeout_error);
        if (ret) {
                free(path_to_free);
                return ret;
        }
  
 -      if (!opts->quiet && !old.path && old.commit && new->commit != old.commit)
 -              orphaned_commit_warning(old.commit, new->commit);
 +      if (!opts->quiet && !old_branch_info.path && old_branch_info.commit && new_branch_info->commit != old_branch_info.commit)
 +              orphaned_commit_warning(old_branch_info.commit, new_branch_info->commit);
  
 -      update_refs_for_switch(opts, &old, new);
 +      update_refs_for_switch(opts, &old_branch_info, new_branch_info);
  
 -      ret = post_checkout_hook(old.commit, new->commit, 1);
 +      ret = post_checkout_hook(old_branch_info.commit, new_branch_info->commit, 1);
        free(path_to_free);
        return ret || writeout_error;
  }
@@@ -869,7 -870,7 +869,7 @@@ static int git_checkout_config(const ch
  
  static int parse_branchname_arg(int argc, const char **argv,
                                int dwim_new_local_branch_ok,
 -                              struct branch_info *new,
 +                              struct branch_info *new_branch_info,
                                struct checkout_opts *opts,
                                struct object_id *rev)
  {
        argv++;
        argc--;
  
 -      new->name = arg;
 -      setup_branch_path(new);
 +      new_branch_info->name = arg;
 +      setup_branch_path(new_branch_info);
  
 -      if (!check_refname_format(new->path, 0) &&
 -          !read_ref(new->path, &branch_rev))
 +      if (!check_refname_format(new_branch_info->path, 0) &&
 +          !read_ref(new_branch_info->path, &branch_rev))
                oidcpy(rev, &branch_rev);
        else
 -              new->path = NULL; /* not an existing branch */
 +              new_branch_info->path = NULL; /* not an existing branch */
  
 -      new->commit = lookup_commit_reference_gently(rev, 1);
 -      if (!new->commit) {
 +      new_branch_info->commit = lookup_commit_reference_gently(rev, 1);
 +      if (!new_branch_info->commit) {
                /* not a commit */
                *source_tree = parse_tree_indirect(rev);
        } else {
 -              parse_commit_or_die(new->commit);
 -              *source_tree = new->commit->tree;
 +              parse_commit_or_die(new_branch_info->commit);
 +              *source_tree = new_branch_info->commit->tree;
        }
  
        if (!*source_tree)                   /* case (1): want a tree */
@@@ -1042,7 -1043,7 +1042,7 @@@ static int switch_unborn_to_new_branch(
  }
  
  static int checkout_branch(struct checkout_opts *opts,
 -                         struct branch_info *new)
 +                         struct branch_info *new_branch_info)
  {
        if (opts->pathspec.nr)
                die(_("paths cannot be used with switching branches"));
        } else if (opts->track == BRANCH_TRACK_UNSPECIFIED)
                opts->track = git_branch_track;
  
 -      if (new->name && !new->commit)
 +      if (new_branch_info->name && !new_branch_info->commit)
                die(_("Cannot switch branch to a non-commit '%s'"),
 -                  new->name);
 +                  new_branch_info->name);
  
 -      if (new->path && !opts->force_detach && !opts->new_branch &&
 +      if (new_branch_info->path && !opts->force_detach && !opts->new_branch &&
            !opts->ignore_other_worktrees) {
                int flag;
                char *head_ref = resolve_refdup("HEAD", 0, NULL, &flag);
                if (head_ref &&
 -                  (!(flag & REF_ISSYMREF) || strcmp(head_ref, new->path)))
 -                      die_if_checked_out(new->path, 1);
 +                  (!(flag & REF_ISSYMREF) || strcmp(head_ref, new_branch_info->path)))
 +                      die_if_checked_out(new_branch_info->path, 1);
                free(head_ref);
        }
  
 -      if (!new->commit && opts->new_branch) {
 +      if (!new_branch_info->commit && opts->new_branch) {
                struct object_id rev;
                int flag;
  
                    (flag & REF_ISSYMREF) && is_null_oid(&rev))
                        return switch_unborn_to_new_branch(opts);
        }
 -      return switch_branches(opts, new);
 +      return switch_branches(opts, new_branch_info);
  }
  
  int cmd_checkout(int argc, const char **argv, const char *prefix)
  {
        struct checkout_opts opts;
 -      struct branch_info new;
 +      struct branch_info new_branch_info;
        char *conflict_style = NULL;
        int dwim_new_local_branch = 1;
        struct option options[] = {
                            2),
                OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"),
                            3),
-               OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)")),
+               OPT__FORCE(&opts.force, N_("force checkout (throw away local modifications)"),
+                          PARSE_OPT_NOCOMPLETE),
                OPT_BOOL('m', "merge", &opts.merge, N_("perform a 3-way merge with the new branch")),
-               OPT_BOOL(0, "overwrite-ignore", &opts.overwrite_ignore, N_("update ignored files (default)")),
+               OPT_BOOL_F(0, "overwrite-ignore", &opts.overwrite_ignore,
+                          N_("update ignored files (default)"),
+                          PARSE_OPT_NOCOMPLETE),
                OPT_STRING(0, "conflict", &conflict_style, N_("style"),
                           N_("conflict style (merge or diff3)")),
                OPT_BOOL('p', "patch", &opts.patch_mode, N_("select hunks interactively")),
        };
  
        memset(&opts, 0, sizeof(opts));
 -      memset(&new, 0, sizeof(new));
 +      memset(&new_branch_info, 0, sizeof(new_branch_info));
        opts.overwrite_ignore = 1;
        opts.prefix = prefix;
        opts.show_progress = -1;
                        opts.track == BRANCH_TRACK_UNSPECIFIED &&
                        !opts.new_branch;
                int n = parse_branchname_arg(argc, argv, dwim_ok,
 -                                           &new, &opts, &rev);
 +                                           &new_branch_info, &opts, &rev);
                argv += n;
                argc -= n;
        }
  
        UNLEAK(opts);
        if (opts.patch_mode || opts.pathspec.nr)
 -              return checkout_paths(&opts, new.name);
 +              return checkout_paths(&opts, new_branch_info.name);
        else
 -              return checkout_branch(&opts, &new);
 +              return checkout_branch(&opts, &new_branch_info);
  }
diff --combined builtin/fetch.c
index d32d94692c7eae8372c4a8abac45f172e6693247,6a603174fbaf5495ee176c70d913924accb432c7..6d73656a486fed1afd031e6cf5e26e1c2039e0cb
@@@ -19,7 -19,6 +19,7 @@@
  #include "argv-array.h"
  #include "utf8.h"
  #include "packfile.h"
 +#include "list-objects-filter-options.h"
  
  static const char * const builtin_fetch_usage[] = {
        N_("git fetch [<options>] [<repository> [<refspec>...]]"),
@@@ -39,10 -38,6 +39,10 @@@ static int fetch_prune_config = -1; /* 
  static int prune = -1; /* unspecified */
  #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */
  
 +static int fetch_prune_tags_config = -1; /* unspecified */
 +static int prune_tags = -1; /* unspecified */
 +#define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
 +
  static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
  static int progress = -1;
  static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
@@@ -61,7 -56,6 +61,7 @@@ static int recurse_submodules_default 
  static int shown_url = 0;
  static int refmap_alloc, refmap_nr;
  static const char **refmap_array;
 +static struct list_objects_filter_options filter_options;
  
  static int git_fetch_config(const char *k, const char *v, void *cb)
  {
                return 0;
        }
  
 +      if (!strcmp(k, "fetch.prunetags")) {
 +              fetch_prune_tags_config = git_config_bool(k, v);
 +              return 0;
 +      }
 +
        if (!strcmp(k, "submodule.recurse")) {
                int r = git_config_bool(k, v) ?
                        RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
@@@ -126,7 -115,7 +126,7 @@@ static struct option builtin_fetch_opti
                 N_("append to .git/FETCH_HEAD instead of overwriting")),
        OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
                   N_("path to upload pack on remote end")),
-       OPT__FORCE(&force, N_("force overwrite of local branch")),
+       OPT__FORCE(&force, N_("force overwrite of local branch"), 0),
        OPT_BOOL('m', "multiple", &multiple,
                 N_("fetch from multiple remotes")),
        OPT_SET_INT('t', "tags", &tags,
                    N_("number of submodules fetched in parallel")),
        OPT_BOOL('p', "prune", &prune,
                 N_("prune remote-tracking branches no longer on remote")),
 +      OPT_BOOL('P', "prune-tags", &prune_tags,
 +               N_("prune local tags no longer on remote and clobber changed tags")),
        { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules, N_("on-demand"),
                    N_("control recursive fetching of submodules"),
                    PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules },
                        TRANSPORT_FAMILY_IPV4),
        OPT_SET_INT('6', "ipv6", &family, N_("use IPv6 addresses only"),
                        TRANSPORT_FAMILY_IPV6),
 +      OPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),
        OPT_END()
  };
  
@@@ -1059,11 -1045,6 +1059,11 @@@ static struct transport *prepare_transp
                set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, "yes");
        if (update_shallow)
                set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
 +      if (filter_options.choice) {
 +              set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
 +                         filter_options.filter_spec);
 +              set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
 +      }
        return transport;
  }
  
@@@ -1231,8 -1212,6 +1231,8 @@@ static void add_options_to_argv(struct 
                argv_array_push(argv, "--dry-run");
        if (prune != -1)
                argv_array_push(argv, prune ? "--prune" : "--no-prune");
 +      if (prune_tags != -1)
 +              argv_array_push(argv, prune_tags ? "--prune-tags" : "--no-prune-tags");
        if (update_head_ok)
                argv_array_push(argv, "--update-head-ok");
        if (force)
@@@ -1286,65 -1265,12 +1286,65 @@@ static int fetch_multiple(struct string
        return result;
  }
  
 -static int fetch_one(struct remote *remote, int argc, const char **argv)
 +/*
 + * Fetching from the promisor remote should use the given filter-spec
 + * or inherit the default filter-spec from the config.
 + */
 +static inline void fetch_one_setup_partial(struct remote *remote)
 +{
 +      /*
 +       * Explicit --no-filter argument overrides everything, regardless
 +       * of any prior partial clones and fetches.
 +       */
 +      if (filter_options.no_filter)
 +              return;
 +
 +      /*
 +       * If no prior partial clone/fetch and the current fetch DID NOT
 +       * request a partial-fetch, do a normal fetch.
 +       */
 +      if (!repository_format_partial_clone && !filter_options.choice)
 +              return;
 +
 +      /*
 +       * If this is the FIRST partial-fetch request, we enable partial
 +       * on this repo and remember the given filter-spec as the default
 +       * for subsequent fetches to this remote.
 +       */
 +      if (!repository_format_partial_clone && filter_options.choice) {
 +              partial_clone_register(remote->name, &filter_options);
 +              return;
 +      }
 +
 +      /*
 +       * We are currently limited to only ONE promisor remote and only
 +       * allow partial-fetches from the promisor remote.
 +       */
 +      if (strcmp(remote->name, repository_format_partial_clone)) {
 +              if (filter_options.choice)
 +                      die(_("--filter can only be used with the remote configured in core.partialClone"));
 +              return;
 +      }
 +
 +      /*
 +       * Do a partial-fetch from the promisor remote using either the
 +       * explicitly given filter-spec or inherit the filter-spec from
 +       * the config.
 +       */
 +      if (!filter_options.choice)
 +              partial_clone_get_default_filter_spec(&filter_options);
 +      return;
 +}
 +
 +static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok)
  {
        static const char **refs = NULL;
        struct refspec *refspec;
        int ref_nr = 0;
 +      int j = 0;
        int exit_code;
 +      int maybe_prune_tags;
 +      int remote_via_config = remote_is_configured(remote, 0);
  
        if (!remote)
                die(_("No remote repository specified.  Please, specify either a URL or a\n"
  
        if (prune < 0) {
                /* no command line request */
 -              if (0 <= gtransport->remote->prune)
 -                      prune = gtransport->remote->prune;
 +              if (0 <= remote->prune)
 +                      prune = remote->prune;
                else if (0 <= fetch_prune_config)
                        prune = fetch_prune_config;
                else
                        prune = PRUNE_BY_DEFAULT;
        }
  
 +      if (prune_tags < 0) {
 +              /* no command line request */
 +              if (0 <= remote->prune_tags)
 +                      prune_tags = remote->prune_tags;
 +              else if (0 <= fetch_prune_tags_config)
 +                      prune_tags = fetch_prune_tags_config;
 +              else
 +                      prune_tags = PRUNE_TAGS_BY_DEFAULT;
 +      }
 +
 +      maybe_prune_tags = prune_tags_ok && prune_tags;
 +      if (maybe_prune_tags && remote_via_config)
 +              add_prune_tags_to_fetch_refspec(remote);
 +
 +      if (argc > 0 || (maybe_prune_tags && !remote_via_config)) {
 +              size_t nr_alloc = st_add3(argc, maybe_prune_tags, 1);
 +              refs = xcalloc(nr_alloc, sizeof(const char *));
 +              if (maybe_prune_tags) {
 +                      refs[j++] = xstrdup("refs/tags/*:refs/tags/*");
 +                      ref_nr++;
 +              }
 +      }
 +
        if (argc > 0) {
 -              int j = 0;
                int i;
 -              refs = xcalloc(st_add(argc, 1), sizeof(const char *));
                for (i = 0; i < argc; i++) {
                        if (!strcmp(argv[i], "tag")) {
                                i++;
                                                    argv[i], argv[i]);
                        } else
                                refs[j++] = argv[i];
 +                      ref_nr++;
                }
 -              refs[j] = NULL;
 -              ref_nr = j;
        }
  
        sigchain_push_common(unlock_pack_on_signal);
@@@ -1414,15 -1320,12 +1414,15 @@@ int cmd_fetch(int argc, const char **ar
  {
        int i;
        struct string_list list = STRING_LIST_INIT_DUP;
 -      struct remote *remote;
 +      struct remote *remote = NULL;
        int result = 0;
 +      int prune_tags_ok = 1;
        struct argv_array argv_gc_auto = ARGV_ARRAY_INIT;
  
        packet_trace_identity("fetch");
  
 +      fetch_if_missing = 0;
 +
        /* Record the command line for the reflog */
        strbuf_addstr(&default_rla, "fetch");
        for (i = 1; i < argc; i++)
        if (depth || deepen_since || deepen_not.nr)
                deepen = 1;
  
 +      if (filter_options.choice && !repository_format_partial_clone)
 +              die("--filter can only be used when extensions.partialClone is set");
 +
        if (all) {
                if (argc == 1)
                        die(_("fetch --all does not take a repository argument"));
                else if (argc > 1)
                        die(_("fetch --all does not make sense with refspecs"));
                (void) for_each_remote(get_one_remote_for_fetch, &list);
 -              result = fetch_multiple(&list);
        } else if (argc == 0) {
                /* No arguments -- use default remote */
                remote = remote_get(NULL);
 -              result = fetch_one(remote, argc, argv);
        } else if (multiple) {
                /* All arguments are assumed to be remotes or groups */
                for (i = 0; i < argc; i++)
                        if (!add_remote_or_group(argv[i], &list))
                                die(_("No such remote or remote group: %s"), argv[i]);
 -              result = fetch_multiple(&list);
        } else {
                /* Single remote or group */
                (void) add_remote_or_group(argv[0], &list);
                        /* More than one remote */
                        if (argc > 1)
                                die(_("Fetching a group and specifying refspecs does not make sense"));
 -                      result = fetch_multiple(&list);
                } else {
                        /* Zero or one remotes */
                        remote = remote_get(argv[0]);
 -                      result = fetch_one(remote, argc-1, argv+1);
 +                      prune_tags_ok = (argc == 1);
 +                      argc--;
 +                      argv++;
                }
        }
  
 +      if (remote) {
 +              if (filter_options.choice || repository_format_partial_clone)
 +                      fetch_one_setup_partial(remote);
 +              result = fetch_one(remote, argc, argv, prune_tags_ok);
 +      } else {
 +              if (filter_options.choice)
 +                      die(_("--filter can only be used with the remote configured in core.partialClone"));
 +              /* TODO should this also die if we have a previous partial-clone? */
 +              result = fetch_multiple(&list);
 +      }
 +
        if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
                struct argv_array options = ARGV_ARRAY_INIT;
  
diff --combined builtin/gc.c
index 77fa720bd0bf374db8a24338bde8686ee0d60377,7fc5c16254beb4175458b200a1316565ebf47bf3..f51e5a6500fc294cb719716671259de42f31bfe7
@@@ -360,8 -360,11 +360,11 @@@ int cmd_gc(int argc, const char **argv
                        N_("prune unreferenced objects"),
                        PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
                OPT_BOOL(0, "aggressive", &aggressive, N_("be more thorough (increased runtime)")),
-               OPT_BOOL(0, "auto", &auto_gc, N_("enable auto-gc mode")),
-               OPT_BOOL(0, "force", &force, N_("force running gc even if there may be another gc running")),
+               OPT_BOOL_F(0, "auto", &auto_gc, N_("enable auto-gc mode"),
+                          PARSE_OPT_NOCOMPLETE),
+               OPT_BOOL_F(0, "force", &force,
+                          N_("force running gc even if there may be another gc running"),
+                          PARSE_OPT_NOCOMPLETE),
                OPT_END()
        };
  
                        argv_array_push(&prune, prune_expire);
                        if (quiet)
                                argv_array_push(&prune, "--no-progress");
 +                      if (repository_format_partial_clone)
 +                              argv_array_push(&prune,
 +                                              "--exclude-promisor-objects");
                        if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
                                return error(FAILED_RUN, prune.argv[0]);
                }
diff --combined builtin/grep.c
index 9a0973e8f158c881059914614f006645646a2545,496f33336ef7d882bef1ea2da87c276db78a2160..789a89133aca7b8eeb93a936fd2301bd3f37d0c7
@@@ -92,7 -92,8 +92,7 @@@ static pthread_cond_t cond_result
  
  static int skip_first_line;
  
 -static void add_work(struct grep_opt *opt, enum grep_source_type type,
 -                   const char *name, const char *path, const void *id)
 +static void add_work(struct grep_opt *opt, const struct grep_source *gs)
  {
        grep_lock();
  
                pthread_cond_wait(&cond_write, &grep_mutex);
        }
  
 -      grep_source_init(&todo[todo_end].source, type, name, path, id);
 +      todo[todo_end].source = *gs;
        if (opt->binary != GREP_BINARY_TEXT)
                grep_source_load_driver(&todo[todo_end].source);
        todo[todo_end].done = 0;
@@@ -316,7 -317,6 +316,7 @@@ static int grep_oid(struct grep_opt *op
                     const char *path)
  {
        struct strbuf pathbuf = STRBUF_INIT;
 +      struct grep_source gs;
  
        if (opt->relative && opt->prefix_length) {
                quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
                strbuf_addstr(&pathbuf, filename);
        }
  
 +      grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid);
 +      strbuf_release(&pathbuf);
 +
  #ifndef NO_PTHREADS
        if (num_threads) {
 -              add_work(opt, GREP_SOURCE_OID, pathbuf.buf, path, oid);
 -              strbuf_release(&pathbuf);
 +              /*
 +               * add_work() copies gs and thus assumes ownership of
 +               * its fields, so do not call grep_source_clear()
 +               */
 +              add_work(opt, &gs);
                return 0;
        } else
  #endif
        {
 -              struct grep_source gs;
                int hit;
  
 -              grep_source_init(&gs, GREP_SOURCE_OID, pathbuf.buf, path, oid);
 -              strbuf_release(&pathbuf);
                hit = grep_source(opt, &gs);
  
                grep_source_clear(&gs);
  static int grep_file(struct grep_opt *opt, const char *filename)
  {
        struct strbuf buf = STRBUF_INIT;
 +      struct grep_source gs;
  
        if (opt->relative && opt->prefix_length)
                quote_path_relative(filename, opt->prefix, &buf);
        else
                strbuf_addstr(&buf, filename);
  
 +      grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename);
 +      strbuf_release(&buf);
 +
  #ifndef NO_PTHREADS
        if (num_threads) {
 -              add_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename);
 -              strbuf_release(&buf);
 +              /*
 +               * add_work() copies gs and thus assumes ownership of
 +               * its fields, so do not call grep_source_clear()
 +               */
 +              add_work(opt, &gs);
                return 0;
        } else
  #endif
        {
 -              struct grep_source gs;
                int hit;
  
 -              grep_source_init(&gs, GREP_SOURCE_FILE, buf.buf, filename, filename);
 -              strbuf_release(&buf);
                hit = grep_source(opt, &gs);
  
                grep_source_clear(&gs);
@@@ -634,7 -627,7 +634,7 @@@ static int grep_object(struct grep_opt 
                free(data);
                return hit;
        }
 -      die(_("unable to grep from object of type %s"), typename(obj->type));
 +      die(_("unable to grep from object of type %s"), type_name(obj->type));
  }
  
  static int grep_objects(struct grep_opt *opt, const struct pathspec *pathspec,
@@@ -839,8 -832,9 +839,9 @@@ int cmd_grep(int argc, const char **arg
                OPT_BOOL('L', "files-without-match",
                        &opt.unmatch_name_only,
                        N_("show only the names of files without match")),
-               OPT_BOOL('z', "null", &opt.null_following_name,
-                       N_("print NUL after filenames")),
+               OPT_BOOL_F('z', "null", &opt.null_following_name,
+                          N_("print NUL after filenames"),
+                          PARSE_OPT_NOCOMPLETE),
                OPT_BOOL('c', "count", &opt.count,
                        N_("show the number of matches instead of matching lines")),
                OPT__COLOR(&opt.color, N_("highlight matches")),
                OPT_GROUP(""),
                { OPTION_STRING, 'O', "open-files-in-pager", &show_in_pager,
                        N_("pager"), N_("show matching files in the pager"),
-                       PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager },
-               OPT_BOOL(0, "ext-grep", &external_grep_allowed__ignored,
-                        N_("allow calling of grep(1) (ignored by this build)")),
+                       PARSE_OPT_OPTARG | PARSE_OPT_NOCOMPLETE,
+                       NULL, (intptr_t)default_pager },
+               OPT_BOOL_F(0, "ext-grep", &external_grep_allowed__ignored,
+                          N_("allow calling of grep(1) (ignored by this build)"),
+                          PARSE_OPT_NOCOMPLETE),
                OPT_END()
        };
  
diff --combined builtin/mv.c
index 8ce6a2ddd4c5008c9d32d45dc21e11d97ff8b79d,e3e308d282f45ac0e8e68be6732043f0cd039d96..8eceb310aae5f8007475f571a0146832eab0be49
@@@ -122,7 -122,8 +122,8 @@@ int cmd_mv(int argc, const char **argv
        struct option builtin_mv_options[] = {
                OPT__VERBOSE(&verbose, N_("be verbose")),
                OPT__DRY_RUN(&show_only, N_("dry run")),
-               OPT__FORCE(&force, N_("force move/rename even if target exists")),
+               OPT__FORCE(&force, N_("force move/rename even if target exists"),
+                          PARSE_OPT_NOCOMPLETE),
                OPT_BOOL('k', NULL, &ignore_errors, N_("skip move/rename errors")),
                OPT_END(),
        };
  
                pos = cache_name_pos(src, strlen(src));
                assert(pos >= 0);
 -              if (!show_only)
 -                      rename_cache_entry_at(pos, dst);
 +              rename_cache_entry_at(pos, dst);
        }
  
        if (gitmodules_modified)
diff --combined builtin/notes.c
index 39304ba743b34ad9ff73583f691c6f8d79379eb5,6990683bd482b6902af19324ddda60fecf300b2f..6d2fda4a7d7ba89633f5cfee73f39cacb9f27898
@@@ -198,9 -198,9 +198,9 @@@ static void prepare_note_data(const str
        }
  }
  
 -static void write_note_data(struct note_data *d, unsigned char *sha1)
 +static void write_note_data(struct note_data *d, struct object_id *oid)
  {
 -      if (write_sha1_file(d->buf.buf, d->buf.len, blob_type, sha1)) {
 +      if (write_object_file(d->buf.buf, d->buf.len, blob_type, oid)) {
                error(_("unable to write note object"));
                if (d->edit_path)
                        error(_("the note contents have been left in %s"),
@@@ -413,7 -413,7 +413,7 @@@ static int add(int argc, const char **a
                        parse_reuse_arg},
                OPT_BOOL(0, "allow-empty", &allow_empty,
                        N_("allow storing empty note")),
-               OPT__FORCE(&force, N_("replace existing notes")),
+               OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
                OPT_END()
        };
  
  
        prepare_note_data(&object, &d, note ? note->hash : NULL);
        if (d.buf.len || allow_empty) {
 -              write_note_data(&d, new_note.hash);
 +              write_note_data(&d, &new_note);
                if (add_note(t, &object, &new_note, combine_notes_overwrite))
                        die("BUG: combine_notes_overwrite failed");
                commit_notes(t, "Notes added by 'git notes add'");
@@@ -484,7 -484,7 +484,7 @@@ static int copy(int argc, const char **
        struct notes_tree *t;
        const char *rewrite_cmd = NULL;
        struct option options[] = {
-               OPT__FORCE(&force, N_("replace existing notes")),
+               OPT__FORCE(&force, N_("replace existing notes"), PARSE_OPT_NOCOMPLETE),
                OPT_BOOL(0, "stdin", &from_stdin, N_("read objects from stdin")),
                OPT_STRING(0, "for-rewrite", &rewrite_cmd, N_("command"),
                           N_("load rewriting config for <command> (implies "
@@@ -619,7 -619,7 +619,7 @@@ static int append_edit(int argc, const 
        }
  
        if (d.buf.len || allow_empty) {
 -              write_note_data(&d, new_note.hash);
 +              write_note_data(&d, &new_note);
                if (add_note(t, &object, &new_note, combine_notes_overwrite))
                        die("BUG: combine_notes_overwrite failed");
                logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]);
diff --combined builtin/pull.c
index 1876271af9423dc58bb66d00bdd1eaa62a983dab,652239aae99e99fddcc127d8f1c6a426271af541..e32d6cd5b4c999bc45b961c1387af066c72a823a
@@@ -193,7 -193,7 +193,7 @@@ static struct option pull_options[] = 
        OPT_PASSTHRU(0, "upload-pack", &opt_upload_pack, N_("path"),
                N_("path to upload pack on remote end"),
                0),
-       OPT__FORCE(&opt_force, N_("force overwrite of local branch")),
+       OPT__FORCE(&opt_force, N_("force overwrite of local branch"), 0),
        OPT_PASSTHRU('t', "tags", &opt_tags, NULL,
                N_("fetch all tags and associated objects"),
                PARSE_OPT_NOARG),
@@@ -574,7 -574,6 +574,7 @@@ static int rebase_submodules(void
        cp.no_stdin = 1;
        argv_array_pushl(&cp.args, "submodule", "update",
                                   "--recursive", "--rebase", NULL);
 +      argv_push_verbosity(&cp.args);
  
        return run_command(&cp);
  }
@@@ -587,7 -586,6 +587,7 @@@ static int update_submodules(void
        cp.no_stdin = 1;
        argv_array_pushl(&cp.args, "submodule", "update",
                                   "--recursive", "--checkout", NULL);
 +      argv_push_verbosity(&cp.args);
  
        return run_command(&cp);
  }
diff --combined builtin/remote.c
index 6487d92abdc8a9d86749e9e00af9f919d4b89486,fce9e5c0f6e0d94ff2e5fc27ade0c005e2e728e7..805ffc05cdb80e4a69de4134e757f9c71e8033dc
@@@ -168,7 -168,7 +168,7 @@@ static int add(int argc, const char **a
                OPT_STRING('m', "master", &master, N_("branch"), N_("master branch")),
                { OPTION_CALLBACK, 0, "mirror", &mirror, N_("push|fetch"),
                        N_("set up remote as a mirror to push to or fetch from"),
-                       PARSE_OPT_OPTARG, parse_mirror_opt },
+                       PARSE_OPT_OPTARG | PARSE_OPT_COMP_ARG, parse_mirror_opt },
                OPT_END()
        };
  
@@@ -322,7 -322,7 +322,7 @@@ static void read_branches(void
  
  struct ref_states {
        struct remote *remote;
 -      struct string_list new, stale, tracked, heads, push;
 +      struct string_list new_refs, stale, tracked, heads, push;
        int queried;
  };
  
@@@ -337,12 -337,12 +337,12 @@@ static int get_ref_states(const struct 
                        die(_("Could not get fetch map for refspec %s"),
                                states->remote->fetch_refspec[i]);
  
 -      states->new.strdup_strings = 1;
 +      states->new_refs.strdup_strings = 1;
        states->tracked.strdup_strings = 1;
        states->stale.strdup_strings = 1;
        for (ref = fetch_map; ref; ref = ref->next) {
                if (!ref->peer_ref || !ref_exists(ref->peer_ref->name))
 -                      string_list_append(&states->new, abbrev_branch(ref->name));
 +                      string_list_append(&states->new_refs, abbrev_branch(ref->name));
                else
                        string_list_append(&states->tracked, abbrev_branch(ref->name));
        }
        free_refs(stale_refs);
        free_refs(fetch_map);
  
 -      string_list_sort(&states->new);
 +      string_list_sort(&states->new_refs);
        string_list_sort(&states->tracked);
        string_list_sort(&states->stale);
  
@@@ -546,8 -546,8 +546,8 @@@ static int add_branch_for_removal(cons
  }
  
  struct rename_info {
 -      const char *old;
 -      const char *new;
 +      const char *old_name;
 +      const char *new_name;
        struct string_list *remote_branches;
  };
  
@@@ -560,7 -560,7 +560,7 @@@ static int read_remote_branches(const c
        int flag;
        const char *symref;
  
 -      strbuf_addf(&buf, "refs/remotes/%s/", rename->old);
 +      strbuf_addf(&buf, "refs/remotes/%s/", rename->old_name);
        if (starts_with(refname, buf.buf)) {
                item = string_list_append(rename->remote_branches, xstrdup(refname));
                symref = resolve_ref_unsafe(refname, RESOLVE_REF_READING,
@@@ -615,36 -615,36 +615,36 @@@ static int mv(int argc, const char **ar
        if (argc != 3)
                usage_with_options(builtin_remote_rename_usage, options);
  
 -      rename.old = argv[1];
 -      rename.new = argv[2];
 +      rename.old_name = argv[1];
 +      rename.new_name = argv[2];
        rename.remote_branches = &remote_branches;
  
 -      oldremote = remote_get(rename.old);
 +      oldremote = remote_get(rename.old_name);
        if (!remote_is_configured(oldremote, 1))
 -              die(_("No such remote: %s"), rename.old);
 +              die(_("No such remote: %s"), rename.old_name);
  
 -      if (!strcmp(rename.old, rename.new) && oldremote->origin != REMOTE_CONFIG)
 +      if (!strcmp(rename.old_name, rename.new_name) && oldremote->origin != REMOTE_CONFIG)
                return migrate_file(oldremote);
  
 -      newremote = remote_get(rename.new);
 +      newremote = remote_get(rename.new_name);
        if (remote_is_configured(newremote, 1))
 -              die(_("remote %s already exists."), rename.new);
 +              die(_("remote %s already exists."), rename.new_name);
  
 -      strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new);
 +      strbuf_addf(&buf, "refs/heads/test:refs/remotes/%s/test", rename.new_name);
        if (!valid_fetch_refspec(buf.buf))
 -              die(_("'%s' is not a valid remote name"), rename.new);
 +              die(_("'%s' is not a valid remote name"), rename.new_name);
  
        strbuf_reset(&buf);
 -      strbuf_addf(&buf, "remote.%s", rename.old);
 -      strbuf_addf(&buf2, "remote.%s", rename.new);
 +      strbuf_addf(&buf, "remote.%s", rename.old_name);
 +      strbuf_addf(&buf2, "remote.%s", rename.new_name);
        if (git_config_rename_section(buf.buf, buf2.buf) < 1)
                return error(_("Could not rename config section '%s' to '%s'"),
                                buf.buf, buf2.buf);
  
        strbuf_reset(&buf);
 -      strbuf_addf(&buf, "remote.%s.fetch", rename.new);
 +      strbuf_addf(&buf, "remote.%s.fetch", rename.new_name);
        git_config_set_multivar(buf.buf, NULL, NULL, 1);
 -      strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old);
 +      strbuf_addf(&old_remote_context, ":refs/remotes/%s/", rename.old_name);
        for (i = 0; i < oldremote->fetch_refspec_nr; i++) {
                char *ptr;
  
                        refspec_updated = 1;
                        strbuf_splice(&buf2,
                                      ptr-buf2.buf + strlen(":refs/remotes/"),
 -                                    strlen(rename.old), rename.new,
 -                                    strlen(rename.new));
 +                                    strlen(rename.old_name), rename.new_name,
 +                                    strlen(rename.new_name));
                } else
                        warning(_("Not updating non-default fetch refspec\n"
                                  "\t%s\n"
        for (i = 0; i < branch_list.nr; i++) {
                struct string_list_item *item = branch_list.items + i;
                struct branch_info *info = item->util;
 -              if (info->remote_name && !strcmp(info->remote_name, rename.old)) {
 +              if (info->remote_name && !strcmp(info->remote_name, rename.old_name)) {
                        strbuf_reset(&buf);
                        strbuf_addf(&buf, "branch.%s.remote", item->string);
 -                      git_config_set(buf.buf, rename.new);
 +                      git_config_set(buf.buf, rename.new_name);
                }
        }
  
                        continue;
                strbuf_reset(&buf);
                strbuf_addstr(&buf, item->string);
 -              strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
 -                              rename.new, strlen(rename.new));
 +              strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name),
 +                              rename.new_name, strlen(rename.new_name));
                strbuf_reset(&buf2);
                strbuf_addf(&buf2, "remote: renamed %s to %s",
                                item->string, buf.buf);
                        continue;
                strbuf_reset(&buf);
                strbuf_addstr(&buf, item->string);
 -              strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old),
 -                              rename.new, strlen(rename.new));
 +              strbuf_splice(&buf, strlen("refs/remotes/"), strlen(rename.old_name),
 +                              rename.new_name, strlen(rename.new_name));
                strbuf_reset(&buf2);
                strbuf_addstr(&buf2, item->util);
 -              strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old),
 -                              rename.new, strlen(rename.new));
 +              strbuf_splice(&buf2, strlen("refs/remotes/"), strlen(rename.old_name),
 +                              rename.new_name, strlen(rename.new_name));
                strbuf_reset(&buf3);
                strbuf_addf(&buf3, "remote: renamed %s to %s",
                                item->string, buf.buf);
@@@ -822,7 -822,7 +822,7 @@@ static void clear_push_info(void *util
  
  static void free_remote_ref_states(struct ref_states *states)
  {
 -      string_list_clear(&states->new, 0);
 +      string_list_clear(&states->new_refs, 0);
        string_list_clear(&states->stale, 1);
        string_list_clear(&states->tracked, 0);
        string_list_clear(&states->heads, 0);
@@@ -907,7 -907,7 +907,7 @@@ static int show_remote_info_item(struc
        if (states->queried) {
                const char *fmt = "%s";
                const char *arg = "";
 -              if (string_list_has_string(&states->new, name)) {
 +              if (string_list_has_string(&states->new_refs, name)) {
                        fmt = _(" new (next fetch will store in remotes/%s)");
                        arg = states->remote->name;
                } else if (string_list_has_string(&states->tracked, name))
@@@ -1176,7 -1176,7 +1176,7 @@@ static int show(int argc, const char **
  
                /* remote branch info */
                info.width = 0;
 -              for_each_string_list(&states.new, add_remote_to_show_info, &info);
 +              for_each_string_list(&states.new_refs, add_remote_to_show_info, &info);
                for_each_string_list(&states.tracked, add_remote_to_show_info, &info);
                for_each_string_list(&states.stale, add_remote_to_show_info, &info);
                if (info.list->nr)
diff --combined builtin/replace.c
index 9f01f3fc7f86ef6e13933524441f330d3dae1573,0c1d8e1b081333b5f582dd42b5ef97101b54eb9e..482f12018fa912eeea860721af2acbf5f8a2e87d
@@@ -56,8 -56,8 +56,8 @@@ static int show_reference(const char *r
                        obj_type = sha1_object_info(object.hash, NULL);
                        repl_type = sha1_object_info(oid->hash, NULL);
  
 -                      printf("%s (%s) -> %s (%s)\n", refname, typename(obj_type),
 -                             oid_to_hex(oid), typename(repl_type));
 +                      printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
 +                             oid_to_hex(oid), type_name(repl_type));
                }
        }
  
@@@ -168,8 -168,8 +168,8 @@@ static int replace_object_oid(const cha
                die("Objects must be of the same type.\n"
                    "'%s' points to a replaced object of type '%s'\n"
                    "while '%s' points to a replacement object of type '%s'.",
 -                  object_ref, typename(obj_type),
 -                  replace_ref, typename(repl_type));
 +                  object_ref, type_name(obj_type),
 +                  replace_ref, type_name(repl_type));
  
        check_ref_valid(object, &prev, &ref, force);
  
@@@ -215,7 -215,7 +215,7 @@@ static void export_object(const struct 
        argv_array_push(&cmd.args, "--no-replace-objects");
        argv_array_push(&cmd.args, "cat-file");
        if (raw)
 -              argv_array_push(&cmd.args, typename(type));
 +              argv_array_push(&cmd.args, type_name(type));
        else
                argv_array_push(&cmd.args, "-p");
        argv_array_push(&cmd.args, oid_to_hex(oid));
@@@ -284,30 -284,30 +284,30 @@@ static int edit_and_replace(const char 
  {
        char *tmpfile = git_pathdup("REPLACE_EDITOBJ");
        enum object_type type;
 -      struct object_id old, new, prev;
 +      struct object_id old_oid, new_oid, prev;
        struct strbuf ref = STRBUF_INIT;
  
 -      if (get_oid(object_ref, &old) < 0)
 +      if (get_oid(object_ref, &old_oid) < 0)
                die("Not a valid object name: '%s'", object_ref);
  
 -      type = sha1_object_info(old.hash, NULL);
 +      type = sha1_object_info(old_oid.hash, NULL);
        if (type < 0)
 -              die("unable to get object type for %s", oid_to_hex(&old));
 +              die("unable to get object type for %s", oid_to_hex(&old_oid));
  
 -      check_ref_valid(&old, &prev, &ref, force);
 +      check_ref_valid(&old_oid, &prev, &ref, force);
        strbuf_release(&ref);
  
 -      export_object(&old, type, raw, tmpfile);
 +      export_object(&old_oid, type, raw, tmpfile);
        if (launch_editor(tmpfile, NULL, NULL) < 0)
                die("editing object file failed");
 -      import_object(&new, type, raw, tmpfile);
 +      import_object(&new_oid, type, raw, tmpfile);
  
        free(tmpfile);
  
 -      if (!oidcmp(&old, &new))
 -              return error("new object is the same as the old one: '%s'", oid_to_hex(&old));
 +      if (!oidcmp(&old_oid, &new_oid))
 +              return error("new object is the same as the old one: '%s'", oid_to_hex(&old_oid));
  
 -      return replace_object_oid(object_ref, &old, "replacement", &new, force);
 +      return replace_object_oid(object_ref, &old_oid, "replacement", &new_oid, force);
  }
  
  static void replace_parents(struct strbuf *buf, int argc, const char **argv)
@@@ -355,7 -355,7 +355,7 @@@ static void check_one_mergetag(struct c
        struct tag *tag;
        int i;
  
 -      hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), tag_oid.hash);
 +      hash_object_file(extra->value, extra->len, type_name(OBJ_TAG), &tag_oid);
        tag = lookup_tag(&tag_oid);
        if (!tag)
                die(_("bad mergetag in commit '%s'"), ref);
@@@ -386,16 -386,16 +386,16 @@@ static void check_mergetags(struct comm
  
  static int create_graft(int argc, const char **argv, int force)
  {
 -      struct object_id old, new;
 +      struct object_id old_oid, new_oid;
        const char *old_ref = argv[0];
        struct commit *commit;
        struct strbuf buf = STRBUF_INIT;
        const char *buffer;
        unsigned long size;
  
 -      if (get_oid(old_ref, &old) < 0)
 +      if (get_oid(old_ref, &old_oid) < 0)
                die(_("Not a valid object name: '%s'"), old_ref);
 -      commit = lookup_commit_or_die(&old, old_ref);
 +      commit = lookup_commit_or_die(&old_oid, old_ref);
  
        buffer = get_commit_buffer(commit, &size);
        strbuf_add(&buf, buffer, size);
  
        check_mergetags(commit, argc, argv);
  
 -      if (write_sha1_file(buf.buf, buf.len, commit_type, new.hash))
 +      if (write_object_file(buf.buf, buf.len, commit_type, &new_oid))
                die(_("could not write replacement commit for: '%s'"), old_ref);
  
        strbuf_release(&buf);
  
 -      if (!oidcmp(&old, &new))
 -              return error("new commit is the same as the old one: '%s'", oid_to_hex(&old));
 +      if (!oidcmp(&old_oid, &new_oid))
 +              return error("new commit is the same as the old one: '%s'", oid_to_hex(&old_oid));
  
 -      return replace_object_oid(old_ref, &old, "replacement", &new, force);
 +      return replace_object_oid(old_ref, &old_oid, "replacement", &new_oid, force);
  }
  
  int cmd_replace(int argc, const char **argv, const char *prefix)
                OPT_CMDMODE('d', "delete", &cmdmode, N_("delete replace refs"), MODE_DELETE),
                OPT_CMDMODE('e', "edit", &cmdmode, N_("edit existing object"), MODE_EDIT),
                OPT_CMDMODE('g', "graft", &cmdmode, N_("change a commit's parents"), MODE_GRAFT),
-               OPT_BOOL('f', "force", &force, N_("replace the ref if it exists")),
+               OPT_BOOL_F('f', "force", &force, N_("replace the ref if it exists"),
+                          PARSE_OPT_NOCOMPLETE),
                OPT_BOOL(0, "raw", &raw, N_("do not pretty-print contents for --edit")),
                OPT_STRING(0, "format", &format, N_("format"), N_("use this format")),
                OPT_END()
index b1daca995f2f46ceb15148b609c4a89ccaa13de4,a5c4a8a6941d46c78e4dc58d0df47532f0bb2b45..ee020d4749623ba57a3a72d236504f0636ee9062
@@@ -20,7 -20,6 +20,7 @@@
  #define OPT_QUIET (1 << 0)
  #define OPT_CACHED (1 << 1)
  #define OPT_RECURSIVE (1 << 2)
 +#define OPT_FORCE (1 << 3)
  
  typedef void (*each_submodule_fn)(const struct cache_entry *list_item,
                                  void *cb_data);
@@@ -51,20 -50,6 +51,20 @@@ static char *get_default_remote(void
        return ret;
  }
  
 +static int print_default_remote(int argc, const char **argv, const char *prefix)
 +{
 +      const char *remote;
 +
 +      if (argc != 1)
 +              die(_("submodule--helper print-default-remote takes no arguments"));
 +
 +      remote = get_default_remote();
 +      if (remote)
 +              printf("%s\n", remote);
 +
 +      return 0;
 +}
 +
  static int starts_with_dot_slash(const char *str)
  {
        return str[0] == '.' && is_dir_sep(str[1]);
@@@ -373,25 -358,6 +373,25 @@@ static void module_list_active(struct m
        *list = active_modules;
  }
  
 +static char *get_up_path(const char *path)
 +{
 +      int i;
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      for (i = count_slashes(path); i; i--)
 +              strbuf_addstr(&sb, "../");
 +
 +      /*
 +       * Check if 'path' ends with slash or not
 +       * for having the same output for dir/sub_dir
 +       * and dir/sub_dir/
 +       */
 +      if (!is_dir_sep(path[strlen(path) - 1]))
 +              strbuf_addstr(&sb, "../");
 +
 +      return strbuf_detach(&sb, NULL);
 +}
 +
  static int module_list(int argc, const char **argv, const char *prefix)
  {
        int i;
@@@ -752,309 -718,6 +752,309 @@@ static int module_name(int argc, const 
        return 0;
  }
  
-               OPT__FORCE(&force, N_("Remove submodule working trees even if they contain local changes")),
 +struct sync_cb {
 +      const char *prefix;
 +      unsigned int flags;
 +};
 +
 +#define SYNC_CB_INIT { NULL, 0 }
 +
 +static void sync_submodule(const char *path, const char *prefix,
 +                         unsigned int flags)
 +{
 +      const struct submodule *sub;
 +      char *remote_key = NULL;
 +      char *sub_origin_url, *super_config_url, *displaypath;
 +      struct strbuf sb = STRBUF_INIT;
 +      struct child_process cp = CHILD_PROCESS_INIT;
 +      char *sub_config_path = NULL;
 +
 +      if (!is_submodule_active(the_repository, path))
 +              return;
 +
 +      sub = submodule_from_path(&null_oid, path);
 +
 +      if (sub && sub->url) {
 +              if (starts_with_dot_dot_slash(sub->url) ||
 +                  starts_with_dot_slash(sub->url)) {
 +                      char *remote_url, *up_path;
 +                      char *remote = get_default_remote();
 +                      strbuf_addf(&sb, "remote.%s.url", remote);
 +
 +                      if (git_config_get_string(sb.buf, &remote_url))
 +                              remote_url = xgetcwd();
 +
 +                      up_path = get_up_path(path);
 +                      sub_origin_url = relative_url(remote_url, sub->url, up_path);
 +                      super_config_url = relative_url(remote_url, sub->url, NULL);
 +
 +                      free(remote);
 +                      free(up_path);
 +                      free(remote_url);
 +              } else {
 +                      sub_origin_url = xstrdup(sub->url);
 +                      super_config_url = xstrdup(sub->url);
 +              }
 +      } else {
 +              sub_origin_url = xstrdup("");
 +              super_config_url = xstrdup("");
 +      }
 +
 +      displaypath = get_submodule_displaypath(path, prefix);
 +
 +      if (!(flags & OPT_QUIET))
 +              printf(_("Synchronizing submodule url for '%s'\n"),
 +                       displaypath);
 +
 +      strbuf_reset(&sb);
 +      strbuf_addf(&sb, "submodule.%s.url", sub->name);
 +      if (git_config_set_gently(sb.buf, super_config_url))
 +              die(_("failed to register url for submodule path '%s'"),
 +                    displaypath);
 +
 +      if (!is_submodule_populated_gently(path, NULL))
 +              goto cleanup;
 +
 +      prepare_submodule_repo_env(&cp.env_array);
 +      cp.git_cmd = 1;
 +      cp.dir = path;
 +      argv_array_pushl(&cp.args, "submodule--helper",
 +                       "print-default-remote", NULL);
 +
 +      strbuf_reset(&sb);
 +      if (capture_command(&cp, &sb, 0))
 +              die(_("failed to get the default remote for submodule '%s'"),
 +                    path);
 +
 +      strbuf_strip_suffix(&sb, "\n");
 +      remote_key = xstrfmt("remote.%s.url", sb.buf);
 +
 +      strbuf_reset(&sb);
 +      submodule_to_gitdir(&sb, path);
 +      strbuf_addstr(&sb, "/config");
 +
 +      if (git_config_set_in_file_gently(sb.buf, remote_key, sub_origin_url))
 +              die(_("failed to update remote for submodule '%s'"),
 +                    path);
 +
 +      if (flags & OPT_RECURSIVE) {
 +              struct child_process cpr = CHILD_PROCESS_INIT;
 +
 +              cpr.git_cmd = 1;
 +              cpr.dir = path;
 +              prepare_submodule_repo_env(&cpr.env_array);
 +
 +              argv_array_push(&cpr.args, "--super-prefix");
 +              argv_array_pushf(&cpr.args, "%s/", displaypath);
 +              argv_array_pushl(&cpr.args, "submodule--helper", "sync",
 +                               "--recursive", NULL);
 +
 +              if (flags & OPT_QUIET)
 +                      argv_array_push(&cpr.args, "--quiet");
 +
 +              if (run_command(&cpr))
 +                      die(_("failed to recurse into submodule '%s'"),
 +                            path);
 +      }
 +
 +cleanup:
 +      free(super_config_url);
 +      free(sub_origin_url);
 +      strbuf_release(&sb);
 +      free(remote_key);
 +      free(displaypath);
 +      free(sub_config_path);
 +}
 +
 +static void sync_submodule_cb(const struct cache_entry *list_item, void *cb_data)
 +{
 +      struct sync_cb *info = cb_data;
 +      sync_submodule(list_item->name, info->prefix, info->flags);
 +
 +}
 +
 +static int module_sync(int argc, const char **argv, const char *prefix)
 +{
 +      struct sync_cb info = SYNC_CB_INIT;
 +      struct pathspec pathspec;
 +      struct module_list list = MODULE_LIST_INIT;
 +      int quiet = 0;
 +      int recursive = 0;
 +
 +      struct option module_sync_options[] = {
 +              OPT__QUIET(&quiet, N_("Suppress output of synchronizing submodule url")),
 +              OPT_BOOL(0, "recursive", &recursive,
 +                      N_("Recurse into nested submodules")),
 +              OPT_END()
 +      };
 +
 +      const char *const git_submodule_helper_usage[] = {
 +              N_("git submodule--helper sync [--quiet] [--recursive] [<path>]"),
 +              NULL
 +      };
 +
 +      argc = parse_options(argc, argv, prefix, module_sync_options,
 +                           git_submodule_helper_usage, 0);
 +
 +      if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
 +              return 1;
 +
 +      info.prefix = prefix;
 +      if (quiet)
 +              info.flags |= OPT_QUIET;
 +      if (recursive)
 +              info.flags |= OPT_RECURSIVE;
 +
 +      for_each_listed_submodule(&list, sync_submodule_cb, &info);
 +
 +      return 0;
 +}
 +
 +struct deinit_cb {
 +      const char *prefix;
 +      unsigned int flags;
 +};
 +#define DEINIT_CB_INIT { NULL, 0 }
 +
 +static void deinit_submodule(const char *path, const char *prefix,
 +                           unsigned int flags)
 +{
 +      const struct submodule *sub;
 +      char *displaypath = NULL;
 +      struct child_process cp_config = CHILD_PROCESS_INIT;
 +      struct strbuf sb_config = STRBUF_INIT;
 +      char *sub_git_dir = xstrfmt("%s/.git", path);
 +
 +      sub = submodule_from_path(&null_oid, path);
 +
 +      if (!sub || !sub->name)
 +              goto cleanup;
 +
 +      displaypath = get_submodule_displaypath(path, prefix);
 +
 +      /* remove the submodule work tree (unless the user already did it) */
 +      if (is_directory(path)) {
 +              struct strbuf sb_rm = STRBUF_INIT;
 +              const char *format;
 +
 +              /*
 +               * protect submodules containing a .git directory
 +               * NEEDSWORK: instead of dying, automatically call
 +               * absorbgitdirs and (possibly) warn.
 +               */
 +              if (is_directory(sub_git_dir))
 +                      die(_("Submodule work tree '%s' contains a .git "
 +                            "directory (use 'rm -rf' if you really want "
 +                            "to remove it including all of its history)"),
 +                          displaypath);
 +
 +              if (!(flags & OPT_FORCE)) {
 +                      struct child_process cp_rm = CHILD_PROCESS_INIT;
 +                      cp_rm.git_cmd = 1;
 +                      argv_array_pushl(&cp_rm.args, "rm", "-qn",
 +                                       path, NULL);
 +
 +                      if (run_command(&cp_rm))
 +                              die(_("Submodule work tree '%s' contains local "
 +                                    "modifications; use '-f' to discard them"),
 +                                    displaypath);
 +              }
 +
 +              strbuf_addstr(&sb_rm, path);
 +
 +              if (!remove_dir_recursively(&sb_rm, 0))
 +                      format = _("Cleared directory '%s'\n");
 +              else
 +                      format = _("Could not remove submodule work tree '%s'\n");
 +
 +              if (!(flags & OPT_QUIET))
 +                      printf(format, displaypath);
 +
 +              strbuf_release(&sb_rm);
 +      }
 +
 +      if (mkdir(path, 0777))
 +              printf(_("could not create empty submodule directory %s"),
 +                    displaypath);
 +
 +      cp_config.git_cmd = 1;
 +      argv_array_pushl(&cp_config.args, "config", "--get-regexp", NULL);
 +      argv_array_pushf(&cp_config.args, "submodule.%s\\.", sub->name);
 +
 +      /* remove the .git/config entries (unless the user already did it) */
 +      if (!capture_command(&cp_config, &sb_config, 0) && sb_config.len) {
 +              char *sub_key = xstrfmt("submodule.%s", sub->name);
 +              /*
 +               * remove the whole section so we have a clean state when
 +               * the user later decides to init this submodule again
 +               */
 +              git_config_rename_section_in_file(NULL, sub_key, NULL);
 +              if (!(flags & OPT_QUIET))
 +                      printf(_("Submodule '%s' (%s) unregistered for path '%s'\n"),
 +                               sub->name, sub->url, displaypath);
 +              free(sub_key);
 +      }
 +
 +cleanup:
 +      free(displaypath);
 +      free(sub_git_dir);
 +      strbuf_release(&sb_config);
 +}
 +
 +static void deinit_submodule_cb(const struct cache_entry *list_item,
 +                              void *cb_data)
 +{
 +      struct deinit_cb *info = cb_data;
 +      deinit_submodule(list_item->name, info->prefix, info->flags);
 +}
 +
 +static int module_deinit(int argc, const char **argv, const char *prefix)
 +{
 +      struct deinit_cb info = DEINIT_CB_INIT;
 +      struct pathspec pathspec;
 +      struct module_list list = MODULE_LIST_INIT;
 +      int quiet = 0;
 +      int force = 0;
 +      int all = 0;
 +
 +      struct option module_deinit_options[] = {
 +              OPT__QUIET(&quiet, N_("Suppress submodule status output")),
++              OPT__FORCE(&force, N_("Remove submodule working trees even if they contain local changes"), 0),
 +              OPT_BOOL(0, "all", &all, N_("Unregister all submodules")),
 +              OPT_END()
 +      };
 +
 +      const char *const git_submodule_helper_usage[] = {
 +              N_("git submodule deinit [--quiet] [-f | --force] [--all | [--] [<path>...]]"),
 +              NULL
 +      };
 +
 +      argc = parse_options(argc, argv, prefix, module_deinit_options,
 +                           git_submodule_helper_usage, 0);
 +
 +      if (all && argc) {
 +              error("pathspec and --all are incompatible");
 +              usage_with_options(git_submodule_helper_usage,
 +                                 module_deinit_options);
 +      }
 +
 +      if (!argc && !all)
 +              die(_("Use '--all' if you really want to deinitialize all submodules"));
 +
 +      if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
 +              BUG("module_list_compute should not choke on empty pathspec");
 +
 +      info.prefix = prefix;
 +      if (quiet)
 +              info.flags |= OPT_QUIET;
 +      if (force)
 +              info.flags |= OPT_FORCE;
 +
 +      for_each_listed_submodule(&list, deinit_submodule_cb, &info);
 +
 +      return 0;
 +}
 +
  static int clone_submodule(const char *path, const char *gitdir, const char *url,
                           const char *depth, struct string_list *reference,
                           int quiet, int progress)
@@@ -1835,9 -1498,6 +1835,9 @@@ static struct cmd_struct commands[] = 
        {"resolve-relative-url-test", resolve_relative_url_test, 0},
        {"init", module_init, SUPPORT_SUPER_PREFIX},
        {"status", module_status, SUPPORT_SUPER_PREFIX},
 +      {"print-default-remote", print_default_remote, 0},
 +      {"sync", module_sync, SUPPORT_SUPER_PREFIX},
 +      {"deinit", module_deinit, 0},
        {"remote-branch", resolve_remote_submodule_branch, 0},
        {"push-check", push_check, 0},
        {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
diff --combined builtin/tag.c
index 8c493a569f75825133f611abc17ed267d2b1e08c,98d3956bbeaf6570cac705ca7401029ee4775056..da186691ed8853bc5848c4106c752b015756eda3
@@@ -187,14 -187,13 +187,14 @@@ static int build_tag_object(struct strb
  {
        if (sign && do_sign(buf) < 0)
                return error(_("unable to sign the tag"));
 -      if (write_sha1_file(buf->buf, buf->len, tag_type, result->hash) < 0)
 +      if (write_object_file(buf->buf, buf->len, tag_type, result) < 0)
                return error(_("unable to write tag file"));
        return 0;
  }
  
  struct create_tag_options {
        unsigned int message_given:1;
 +      unsigned int use_editor:1;
        unsigned int sign;
        enum {
                CLEANUP_NONE,
@@@ -221,11 -220,11 +221,11 @@@ static void create_tag(const struct obj
                    "tag %s\n"
                    "tagger %s\n\n",
                    oid_to_hex(object),
 -                  typename(type),
 +                  type_name(type),
                    tag,
                    git_committer_info(IDENT_STRICT));
  
 -      if (!opt->message_given) {
 +      if (!opt->message_given || opt->use_editor) {
                int fd;
  
                /* write the template message before editing: */
                if (fd < 0)
                        die_errno(_("could not create file '%s'"), path);
  
 -              if (!is_null_oid(prev)) {
 +              if (opt->message_given) {
 +                      write_or_die(fd, buf->buf, buf->len);
 +                      strbuf_reset(buf);
 +              } else if (!is_null_oid(prev)) {
                        write_tag_body(fd, prev);
                } else {
                        struct strbuf buf = STRBUF_INIT;
@@@ -376,7 -372,6 +376,7 @@@ int cmd_tag(int argc, const char **argv
        static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
        struct ref_format format = REF_FORMAT_INIT;
        int icase = 0;
 +      int edit_flag = 0;
        struct option options[] = {
                OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
                { OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
                OPT_CALLBACK('m', "message", &msg, N_("message"),
                             N_("tag message"), parse_msg_arg),
                OPT_FILENAME('F', "file", &msgfile, N_("read message from file")),
 +              OPT_BOOL('e', "edit", &edit_flag, N_("force edit of tag message")),
                OPT_BOOL('s', "sign", &opt.sign, N_("annotated and GPG-signed tag")),
                OPT_STRING(0, "cleanup", &cleanup_arg, N_("mode"),
                        N_("how to strip spaces and #comments from message")),
                OPT_STRING('u', "local-user", &keyid, N_("key-id"),
                                        N_("use another key to sign the tag")),
-               OPT__FORCE(&force, N_("replace the tag if exists")),
+               OPT__FORCE(&force, N_("replace the tag if exists"), 0),
                OPT_BOOL(0, "create-reflog", &create_reflog, N_("create a reflog")),
  
                OPT_GROUP(N_("Tag listing options")),
                die(_("tag '%s' already exists"), tag);
  
        opt.message_given = msg.given || msgfile;
 +      opt.use_editor = edit_flag;
  
        if (!cleanup_arg || !strcmp(cleanup_arg, "strip"))
                opt.cleanup_mode = CLEANUP_ALL;
diff --combined builtin/worktree.c
index 1c1e576fdb01ef1adc4207799d2d7ac97932a8cf,76dc6b8cb56ca85dcbab7a2e9ab539d3ff82749c..670555deddaca8ff8050c03ea68c83c7b4e459e6
  #include "worktree.h"
  
  static const char * const worktree_usage[] = {
 -      N_("git worktree add [<options>] <path> [<branch>]"),
 +      N_("git worktree add [<options>] <path> [<commit-ish>]"),
        N_("git worktree list [<options>]"),
        N_("git worktree lock [<options>] <path>"),
 +      N_("git worktree move <worktree> <new-path>"),
        N_("git worktree prune [<options>]"),
 +      N_("git worktree remove [<options>] <worktree>"),
        N_("git worktree unlock <path>"),
        NULL
  };
@@@ -347,23 -345,9 +347,23 @@@ done
         * Hook failure does not warrant worktree deletion, so run hook after
         * is_junk is cleared, but do return appropriate code when hook fails.
         */
 -      if (!ret && opts->checkout)
 -              ret = run_hook_le(NULL, "post-checkout", oid_to_hex(&null_oid),
 -                                oid_to_hex(&commit->object.oid), "1", NULL);
 +      if (!ret && opts->checkout) {
 +              const char *hook = find_hook("post-checkout");
 +              if (hook) {
 +                      const char *env[] = { "GIT_DIR", "GIT_WORK_TREE", NULL };
 +                      cp.git_cmd = 0;
 +                      cp.no_stdin = 1;
 +                      cp.stdout_to_stderr = 1;
 +                      cp.dir = path;
 +                      cp.env = env;
 +                      cp.argv = NULL;
 +                      argv_array_pushl(&cp.args, absolute_path(hook),
 +                                       oid_to_hex(&null_oid),
 +                                       oid_to_hex(&commit->object.oid),
 +                                       "1", NULL);
 +                      ret = run_command(&cp);
 +              }
 +      }
  
        argv_array_clear(&child_env);
        strbuf_release(&sb);
@@@ -381,7 -365,9 +381,9 @@@ static int add(int ac, const char **av
        const char *branch;
        const char *opt_track = NULL;
        struct option options[] = {
-               OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
+               OPT__FORCE(&opts.force,
+                          N_("checkout <branch> even if already checked out in other worktree"),
+                          PARSE_OPT_NOCOMPLETE),
                OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
                           N_("create a new branch")),
                OPT_STRING('B', NULL, &new_branch_force, N_("branch"),
@@@ -621,220 -607,6 +623,220 @@@ static int unlock_worktree(int ac, cons
        return ret;
  }
  
 +static void validate_no_submodules(const struct worktree *wt)
 +{
 +      struct index_state istate = { NULL };
 +      int i, found_submodules = 0;
 +
 +      if (read_index_from(&istate, worktree_git_path(wt, "index"),
 +                          get_worktree_git_dir(wt)) > 0) {
 +              for (i = 0; i < istate.cache_nr; i++) {
 +                      struct cache_entry *ce = istate.cache[i];
 +
 +                      if (S_ISGITLINK(ce->ce_mode)) {
 +                              found_submodules = 1;
 +                              break;
 +                      }
 +              }
 +      }
 +      discard_index(&istate);
 +
 +      if (found_submodules)
 +              die(_("working trees containing submodules cannot be moved or removed"));
 +}
 +
 +static int move_worktree(int ac, const char **av, const char *prefix)
 +{
 +      struct option options[] = {
 +              OPT_END()
 +      };
 +      struct worktree **worktrees, *wt;
 +      struct strbuf dst = STRBUF_INIT;
 +      struct strbuf errmsg = STRBUF_INIT;
 +      const char *reason;
 +      char *path;
 +
 +      ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 +      if (ac != 2)
 +              usage_with_options(worktree_usage, options);
 +
 +      path = prefix_filename(prefix, av[1]);
 +      strbuf_addstr(&dst, path);
 +      free(path);
 +
 +      worktrees = get_worktrees(0);
 +      wt = find_worktree(worktrees, prefix, av[0]);
 +      if (!wt)
 +              die(_("'%s' is not a working tree"), av[0]);
 +      if (is_main_worktree(wt))
 +              die(_("'%s' is a main working tree"), av[0]);
 +      if (is_directory(dst.buf)) {
 +              const char *sep = find_last_dir_sep(wt->path);
 +
 +              if (!sep)
 +                      die(_("could not figure out destination name from '%s'"),
 +                          wt->path);
 +              strbuf_trim_trailing_dir_sep(&dst);
 +              strbuf_addstr(&dst, sep);
 +      }
 +      if (file_exists(dst.buf))
 +              die(_("target '%s' already exists"), dst.buf);
 +
 +      validate_no_submodules(wt);
 +
 +      reason = is_worktree_locked(wt);
 +      if (reason) {
 +              if (*reason)
 +                      die(_("cannot move a locked working tree, lock reason: %s"),
 +                          reason);
 +              die(_("cannot move a locked working tree"));
 +      }
 +      if (validate_worktree(wt, &errmsg, 0))
 +              die(_("validation failed, cannot move working tree: %s"),
 +                  errmsg.buf);
 +      strbuf_release(&errmsg);
 +
 +      if (rename(wt->path, dst.buf) == -1)
 +              die_errno(_("failed to move '%s' to '%s'"), wt->path, dst.buf);
 +
 +      update_worktree_location(wt, dst.buf);
 +
 +      strbuf_release(&dst);
 +      free_worktrees(worktrees);
 +      return 0;
 +}
 +
 +/*
 + * Note, "git status --porcelain" is used to determine if it's safe to
 + * delete a whole worktree. "git status" does not ignore user
 + * configuration, so if a normal "git status" shows "clean" for the
 + * user, then it's ok to remove it.
 + *
 + * This assumption may be a bad one. We may want to ignore
 + * (potentially bad) user settings and only delete a worktree when
 + * it's absolutely safe to do so from _our_ point of view because we
 + * know better.
 + */
 +static void check_clean_worktree(struct worktree *wt,
 +                               const char *original_path)
 +{
 +      struct argv_array child_env = ARGV_ARRAY_INIT;
 +      struct child_process cp;
 +      char buf[1];
 +      int ret;
 +
 +      /*
 +       * Until we sort this out, all submodules are "dirty" and
 +       * will abort this function.
 +       */
 +      validate_no_submodules(wt);
 +
 +      argv_array_pushf(&child_env, "%s=%s/.git",
 +                       GIT_DIR_ENVIRONMENT, wt->path);
 +      argv_array_pushf(&child_env, "%s=%s",
 +                       GIT_WORK_TREE_ENVIRONMENT, wt->path);
 +      memset(&cp, 0, sizeof(cp));
 +      argv_array_pushl(&cp.args, "status",
 +                       "--porcelain", "--ignore-submodules=none",
 +                       NULL);
 +      cp.env = child_env.argv;
 +      cp.git_cmd = 1;
 +      cp.dir = wt->path;
 +      cp.out = -1;
 +      ret = start_command(&cp);
 +      if (ret)
 +              die_errno(_("failed to run 'git status' on '%s'"),
 +                        original_path);
 +      ret = xread(cp.out, buf, sizeof(buf));
 +      if (ret)
 +              die(_("'%s' is dirty, use --force to delete it"),
 +                  original_path);
 +      close(cp.out);
 +      ret = finish_command(&cp);
 +      if (ret)
 +              die_errno(_("failed to run 'git status' on '%s', code %d"),
 +                        original_path, ret);
 +}
 +
 +static int delete_git_work_tree(struct worktree *wt)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      int ret = 0;
 +
 +      strbuf_addstr(&sb, wt->path);
 +      if (remove_dir_recursively(&sb, 0)) {
 +              error_errno(_("failed to delete '%s'"), sb.buf);
 +              ret = -1;
 +      }
 +      strbuf_release(&sb);
 +      return ret;
 +}
 +
 +static int delete_git_dir(struct worktree *wt)
 +{
 +      struct strbuf sb = STRBUF_INIT;
 +      int ret = 0;
 +
 +      strbuf_addstr(&sb, git_common_path("worktrees/%s", wt->id));
 +      if (remove_dir_recursively(&sb, 0)) {
 +              error_errno(_("failed to delete '%s'"), sb.buf);
 +              ret = -1;
 +      }
 +      strbuf_release(&sb);
 +      return ret;
 +}
 +
 +static int remove_worktree(int ac, const char **av, const char *prefix)
 +{
 +      int force = 0;
 +      struct option options[] = {
 +              OPT_BOOL(0, "force", &force,
 +                       N_("force removing even if the worktree is dirty")),
 +              OPT_END()
 +      };
 +      struct worktree **worktrees, *wt;
 +      struct strbuf errmsg = STRBUF_INIT;
 +      const char *reason;
 +      int ret = 0;
 +
 +      ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
 +      if (ac != 1)
 +              usage_with_options(worktree_usage, options);
 +
 +      worktrees = get_worktrees(0);
 +      wt = find_worktree(worktrees, prefix, av[0]);
 +      if (!wt)
 +              die(_("'%s' is not a working tree"), av[0]);
 +      if (is_main_worktree(wt))
 +              die(_("'%s' is a main working tree"), av[0]);
 +      reason = is_worktree_locked(wt);
 +      if (reason) {
 +              if (*reason)
 +                      die(_("cannot remove a locked working tree, lock reason: %s"),
 +                          reason);
 +              die(_("cannot remove a locked working tree"));
 +      }
 +      if (validate_worktree(wt, &errmsg, WT_VALIDATE_WORKTREE_MISSING_OK))
 +              die(_("validation failed, cannot remove working tree: %s"),
 +                  errmsg.buf);
 +      strbuf_release(&errmsg);
 +
 +      if (file_exists(wt->path)) {
 +              if (!force)
 +                      check_clean_worktree(wt, av[0]);
 +
 +              ret |= delete_git_work_tree(wt);
 +      }
 +      /*
 +       * continue on even if ret is non-zero, there's no going back
 +       * from here.
 +       */
 +      ret |= delete_git_dir(wt);
 +
 +      free_worktrees(worktrees);
 +      return ret;
 +}
 +
  int cmd_worktree(int ac, const char **av, const char *prefix)
  {
        struct option options[] = {
                return lock_worktree(ac - 1, av + 1, prefix);
        if (!strcmp(av[1], "unlock"))
                return unlock_worktree(ac - 1, av + 1, prefix);
 +      if (!strcmp(av[1], "move"))
 +              return move_worktree(ac - 1, av + 1, prefix);
 +      if (!strcmp(av[1], "remove"))
 +              return remove_worktree(ac - 1, av + 1, prefix);
        usage_with_options(worktree_usage, options);
  }
index 66010707df2c15d9f79b797bafd62e48cc8a923e,2e30950299fd74a8f11b0cea86d6d8ffb6ca3226..f1981aa75df2128f93a3d6b5b3434ab6bda86576
@@@ -280,6 -280,39 +280,39 @@@ __gitcomp (
        esac
  }
  
+ # This function is equivalent to
+ #
+ #    __gitcomp "$(git xxx --git-completion-helper) ..."
+ #
+ # except that the output is cached. Accept 1-3 arguments:
+ # 1: the git command to execute, this is also the cache key
+ # 2: extra options to be added on top (e.g. negative forms)
+ # 3: options to be excluded
+ __gitcomp_builtin ()
+ {
+       # spaces must be replaced with underscore for multi-word
+       # commands, e.g. "git remote add" becomes remote_add.
+       local cmd="$1"
+       local incl="$2"
+       local excl="$3"
+       local var=__gitcomp_builtin_"${cmd/-/_}"
+       local options
+       eval "options=\$$var"
+       if [ -z "$options" ]; then
+               # leading and trailing spaces are significant to make
+               # option removal work correctly.
+               options=" $(__git ${cmd/_/ } --git-completion-helper) $incl "
+               for i in $excl; do
+                       options="${options/ $i / }"
+               done
+               eval "$var=\"$options\""
+       fi
+       __gitcomp "$options"
+ }
  # Variation of __gitcomp_nl () that appends to the existing list of
  # completion candidates, COMPREPLY.
  __gitcomp_nl_append ()
@@@ -439,7 -472,7 +472,7 @@@ __git_refs (
                        track=""
                        ;;
                *)
 -                      for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
 +                      for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD REBASE_HEAD; do
                                case "$i" in
                                $match*)
                                        if [ -e "$dir/$i" ]; then
@@@ -594,7 -627,7 +627,7 @@@ __git_is_configured_remote (
  
  __git_list_merge_strategies ()
  {
 -      git merge -s help 2>&1 |
 +      LANG=C LC_ALL=C git merge -s help 2>&1 |
        sed -n -e '/[Aa]vailable strategies are: /,/^$/{
                s/\.$//
                s/.*://
@@@ -1072,12 -1105,13 +1105,13 @@@ __git_count_arguments (
  }
  
  __git_whitespacelist="nowarn warn error error-all fix"
 -__git_am_inprogress_options="--skip --continue --resolved --abort"
++__git_am_inprogress_options="--skip --continue --resolved --abort --quit --show-current-patch"
  
  _git_am ()
  {
        __git_find_repo_path
        if [ -d "$__git_repo_path"/rebase-apply ]; then
-               __gitcomp "--skip --continue --resolved --abort --quit --show-current-patch"
+               __gitcomp "$__git_am_inprogress_options"
                return
        fi
        case "$cur" in
                return
                ;;
        --*)
-               __gitcomp "
-                       --3way --committer-date-is-author-date --ignore-date
-                       --ignore-whitespace --ignore-space-change
-                       --interactive --keep --no-utf8 --signoff --utf8
-                       --whitespace= --scissors
-                       "
+               __gitcomp_builtin am "--no-utf8" \
+                       "$__git_am_inprogress_options"
                return
        esac
  }
@@@ -1104,14 -1134,7 +1134,7 @@@ _git_apply (
                return
                ;;
        --*)
-               __gitcomp "
-                       --stat --numstat --summary --check --index
-                       --cached --index-info --reverse --reject --unidiff-zero
-                       --apply --no-add --exclude=
-                       --ignore-whitespace --ignore-space-change
-                       --whitespace= --inaccurate-eof --verbose
-                       --recount --directory=
-                       "
+               __gitcomp_builtin apply
                return
        esac
  }
@@@ -1120,10 -1143,7 +1143,7 @@@ _git_add (
  {
        case "$cur" in
        --*)
-               __gitcomp "
-                       --interactive --refresh --patch --update --dry-run
-                       --ignore-errors --intent-to-add --force --edit --chmod=
-                       "
+               __gitcomp_builtin add
                return
        esac
  
@@@ -1200,12 -1220,8 +1220,8 @@@ _git_branch (
                __git_complete_refs --cur="${cur##--set-upstream-to=}"
                ;;
        --*)
-               __gitcomp "
-                       --color --no-color --verbose --abbrev= --no-abbrev
-                       --track --no-track --contains --no-contains --merged --no-merged
-                       --set-upstream-to= --edit-description --list
-                       --unset-upstream --delete --move --copy --remotes
-                       --column --no-column --sort= --points-at
+               __gitcomp_builtin branch "--no-color --no-abbrev
+                       --no-track --no-column
                        "
                ;;
        *)
@@@ -1247,11 -1263,7 +1263,7 @@@ _git_checkout (
                __gitcomp "diff3 merge" "" "${cur##--conflict=}"
                ;;
        --*)
-               __gitcomp "
-                       --quiet --ours --theirs --track --no-track --merge
-                       --conflict= --orphan --patch --detach --ignore-skip-worktree-bits
-                       --recurse-submodules --no-recurse-submodules
-                       "
+               __gitcomp_builtin checkout "--no-track --no-recurse-submodules"
                ;;
        *)
                # check if --track, --no-track, or --no-guess was specified
@@@ -1271,16 -1283,19 +1283,19 @@@ _git_cherry (
        __git_complete_refs
  }
  
+ __git_cherry_pick_inprogress_options="--continue --quit --abort"
  _git_cherry_pick ()
  {
        __git_find_repo_path
        if [ -f "$__git_repo_path"/CHERRY_PICK_HEAD ]; then
-               __gitcomp "--continue --quit --abort"
+               __gitcomp "$__git_cherry_pick_inprogress_options"
                return
        fi
        case "$cur" in
        --*)
-               __gitcomp "--edit --no-commit --signoff --strategy= --mainline"
+               __gitcomp_builtin cherry-pick "" \
+                       "$__git_cherry_pick_inprogress_options"
                ;;
        *)
                __git_complete_refs
@@@ -1292,7 -1307,7 +1307,7 @@@ _git_clean (
  {
        case "$cur" in
        --*)
-               __gitcomp "--dry-run --quiet"
+               __gitcomp_builtin clean
                return
                ;;
        esac
@@@ -1305,26 -1320,7 +1320,7 @@@ _git_clone (
  {
        case "$cur" in
        --*)
-               __gitcomp "
-                       --local
-                       --no-hardlinks
-                       --shared
-                       --reference
-                       --quiet
-                       --no-checkout
-                       --bare
-                       --mirror
-                       --origin
-                       --upload-pack
-                       --template=
-                       --depth
-                       --single-branch
-                       --no-tags
-                       --branch
-                       --recurse-submodules
-                       --no-single-branch
-                       --shallow-submodules
-                       "
+               __gitcomp_builtin clone "--no-single-branch"
                return
                ;;
        esac
@@@ -1357,16 -1353,7 +1353,7 @@@ _git_commit (
                return
                ;;
        --*)
-               __gitcomp "
-                       --all --author= --signoff --verify --no-verify
-                       --edit --no-edit
-                       --amend --include --only --interactive
-                       --dry-run --reuse-message= --reedit-message=
-                       --reset-author --file= --message= --template=
-                       --cleanup= --untracked-files --untracked-files=
-                       --verbose --quiet --fixup= --squash=
-                       --patch --short --date --allow-empty
-                       "
+               __gitcomp_builtin commit "--no-edit --verify"
                return
        esac
  
@@@ -1382,11 -1369,7 +1369,7 @@@ _git_describe (
  {
        case "$cur" in
        --*)
-               __gitcomp "
-                       --all --tags --contains --abbrev= --candidates=
-                       --exact-match --debug --long --match --always --first-parent
-                       --exclude --dirty --broken
-                       "
+               __gitcomp_builtin describe
                return
        esac
        __git_complete_refs
@@@ -1411,7 -1394,7 +1394,7 @@@ __git_diff_common_options="--stat --num
                        --dirstat --dirstat= --dirstat-by-file
                        --dirstat-by-file= --cumulative
                        --diff-algorithm=
-                       --submodule --submodule=
+                       --submodule --submodule= --ignore-submodules
  "
  
  _git_diff ()
@@@ -1452,11 -1435,11 +1435,11 @@@ _git_difftool (
                return
                ;;
        --*)
-               __gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
-                       --base --ours --theirs
-                       --no-renames --diff-filter= --find-copies-harder
-                       --relative --ignore-submodules
-                       --tool="
+               __gitcomp_builtin difftool "$__git_diff_common_options
+                                       --base --cached --ours --theirs
+                                       --pickaxe-all --pickaxe-regex
+                                       --relative --staged
+                                       "
                return
                ;;
        esac
  
  __git_fetch_recurse_submodules="yes on-demand no"
  
- __git_fetch_options="
-       --quiet --verbose --append --upload-pack --force --keep --depth=
-       --tags --no-tags --all --prune --dry-run --recurse-submodules=
-       --unshallow --update-shallow --prune-tags
- "
  _git_fetch ()
  {
        case "$cur" in
                return
                ;;
        --*)
-               __gitcomp "$__git_fetch_options"
+               __gitcomp_builtin fetch "--no-tags"
                return
                ;;
        esac
@@@ -1516,10 -1493,7 +1493,7 @@@ _git_fsck (
  {
        case "$cur" in
        --*)
-               __gitcomp "
-                       --tags --root --unreachable --cache --no-reflogs --full
-                       --strict --verbose --lost-found --name-objects
-                       "
+               __gitcomp_builtin fsck "--no-reflogs"
                return
                ;;
        esac
@@@ -1529,7 -1503,7 +1503,7 @@@ _git_gc (
  {
        case "$cur" in
        --*)
-               __gitcomp "--prune --aggressive"
+               __gitcomp_builtin gc
                return
                ;;
        esac
@@@ -1585,21 -1559,7 +1559,7 @@@ _git_grep (
  
        case "$cur" in
        --*)
-               __gitcomp "
-                       --cached
-                       --text --ignore-case --word-regexp --invert-match
-                       --full-name --line-number
-                       --extended-regexp --basic-regexp --fixed-strings
-                       --perl-regexp
-                       --threads
-                       --files-with-matches --name-only
-                       --files-without-match
-                       --max-depth
-                       --count
-                       --and --or --not --all-match
-                       --break --heading --show-function --function-context
-                       --untracked --no-index
-                       "
+               __gitcomp_builtin grep
                return
                ;;
        esac
@@@ -1617,7 -1577,7 +1577,7 @@@ _git_help (
  {
        case "$cur" in
        --*)
-               __gitcomp "--all --guides --info --man --web"
+               __gitcomp_builtin help
                return
                ;;
        esac
@@@ -1640,7 -1600,7 +1600,7 @@@ _git_init (
                return
                ;;
        --*)
-               __gitcomp "--quiet --bare --template= --shared --shared="
+               __gitcomp_builtin init
                return
                ;;
        esac
@@@ -1650,13 -1610,7 +1610,7 @@@ _git_ls_files (
  {
        case "$cur" in
        --*)
-               __gitcomp "--cached --deleted --modified --others --ignored
-                       --stage --directory --no-empty-directory --unmerged
-                       --killed --exclude= --exclude-from=
-                       --exclude-per-directory= --exclude-standard
-                       --error-unmatch --with-tree= --full-name
-                       --abbrev --ignored --exclude-per-directory
-                       "
+               __gitcomp_builtin ls-files "--no-empty-directory"
                return
                ;;
        esac
@@@ -1670,7 -1624,7 +1624,7 @@@ _git_ls_remote (
  {
        case "$cur" in
        --*)
-               __gitcomp "--heads --tags --refs --get-url --symref"
+               __gitcomp_builtin ls-remote
                return
                ;;
        esac
@@@ -1794,22 -1748,18 +1748,18 @@@ _git_log (
        __git_complete_revlist
  }
  
- # Common merge options shared by git-merge(1) and git-pull(1).
- __git_merge_options="
-       --no-commit --no-stat --log --no-log --squash --strategy
-       --commit --stat --no-squash --ff --no-ff --ff-only --edit --no-edit
-       --verify-signatures --no-verify-signatures --gpg-sign
-       --quiet --verbose --progress --no-progress
- "
  _git_merge ()
  {
        __git_complete_strategy && return
  
        case "$cur" in
        --*)
-               __gitcomp "$__git_merge_options
-                       --rerere-autoupdate --no-rerere-autoupdate --abort --continue"
+               __gitcomp_builtin merge "--no-rerere-autoupdate
+                               --no-commit --no-edit --no-ff
+                               --no-log --no-progress
+                               --no-squash --no-stat
+                               --no-verify-signatures
+                               "
                return
        esac
        __git_complete_refs
@@@ -1833,7 -1783,7 +1783,7 @@@ _git_merge_base (
  {
        case "$cur" in
        --*)
-               __gitcomp "--octopus --independent --is-ancestor --fork-point"
+               __gitcomp_builtin merge-base
                return
                ;;
        esac
@@@ -1844,7 -1794,7 +1794,7 @@@ _git_mv (
  {
        case "$cur" in
        --*)
-               __gitcomp "--dry-run"
+               __gitcomp_builtin mv
                return
                ;;
        esac
  
  _git_name_rev ()
  {
-       __gitcomp "--tags --all --stdin"
+       __gitcomp_builtin name-rev
  }
  
  _git_notes ()
  {
-       local subcommands='add append copy edit list prune remove show'
+       local subcommands='add append copy edit get-ref list merge prune remove show'
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
  
        case "$subcommand,$cur" in
        ,--*)
-               __gitcomp '--ref'
+               __gitcomp_builtin notes
                ;;
        ,*)
                case "$prev" in
                        ;;
                esac
                ;;
-       add,--reuse-message=*|append,--reuse-message=*|\
-       add,--reedit-message=*|append,--reedit-message=*)
+       *,--reuse-message=*|*,--reedit-message=*)
                __git_complete_refs --cur="${cur#*=}"
                ;;
-       add,--*|append,--*)
-               __gitcomp '--file= --message= --reedit-message=
-                               --reuse-message='
+       *,--*)
+               __gitcomp_builtin notes_$subcommand
                ;;
-       copy,--*)
-               __gitcomp '--stdin'
-               ;;
-       prune,--*)
-               __gitcomp '--dry-run --verbose'
-               ;;
-       prune,*)
+       prune,*|get-ref,*)
+               # this command does not take a ref, do not complete it
                ;;
        *)
                case "$prev" in
@@@ -1920,12 -1863,11 +1863,11 @@@ _git_pull (
                return
                ;;
        --*)
-               __gitcomp "
-                       --rebase --no-rebase
-                       --autostash --no-autostash
-                       $__git_merge_options
-                       $__git_fetch_options
-               "
+               __gitcomp_builtin pull "--no-autostash --no-commit --no-edit
+                                       --no-ff --no-log --no-progress --no-rebase
+                                       --no-squash --no-stat --no-tags
+                                       --no-verify-signatures"
                return
                ;;
        esac
@@@ -1976,12 -1918,7 +1918,7 @@@ _git_push (
                return
                ;;
        --*)
-               __gitcomp "
-                       --all --mirror --tags --dry-run --force --verbose
-                       --quiet --prune --delete --follow-tags
-                       --receive-pack= --repo= --set-upstream
-                       --force-with-lease --force-with-lease= --recurse-submodules=
-               "
+               __gitcomp_builtin push
                return
                ;;
        esac
@@@ -1992,11 -1929,11 +1929,11 @@@ _git_rebase (
  {
        __git_find_repo_path
        if [ -f "$__git_repo_path"/rebase-merge/interactive ]; then
 -              __gitcomp "--continue --skip --abort --quit --edit-todo"
 +              __gitcomp "--continue --skip --abort --quit --edit-todo --show-current-patch"
                return
        elif [ -d "$__git_repo_path"/rebase-apply ] || \
             [ -d "$__git_repo_path"/rebase-merge ]; then
 -              __gitcomp "--continue --skip --abort --quit"
 +              __gitcomp "--continue --skip --abort --quit --show-current-patch"
                return
        fi
        __git_complete_strategy && return
                        --autostash --no-autostash
                        --verify --no-verify
                        --keep-empty --root --force-rebase --no-ff
+                       --rerere-autoupdate
                        --exec
                        "
  
@@@ -2119,11 -2057,7 +2057,7 @@@ _git_status (
                return
                ;;
        --*)
-               __gitcomp "
-                       --short --branch --porcelain --long --verbose
-                       --untracked-files= --ignore-submodules= --ignored
-                       --column= --no-column
-                       "
+               __gitcomp_builtin status "--no-column"
                return
                ;;
        esac
@@@ -2265,14 -2199,7 +2199,7 @@@ _git_config (
        esac
        case "$cur" in
        --*)
-               __gitcomp "
-                       --system --global --local --file=
-                       --list --replace-all
-                       --get --get-all --get-regexp
-                       --add --unset --unset-all
-                       --remove-section --rename-section
-                       --name-only
-                       "
+               __gitcomp_builtin config
                return
                ;;
        branch.*.*)
@@@ -2672,7 -2599,7 +2599,7 @@@ _git_remote (
        if [ -z "$subcommand" ]; then
                case "$cur" in
                --*)
-                       __gitcomp "--verbose"
+                       __gitcomp_builtin remote
                        ;;
                *)
                        __gitcomp "$subcommands"
  
        case "$subcommand,$cur" in
        add,--*)
-               __gitcomp "--track --master --fetch --tags --no-tags --mirror="
+               __gitcomp_builtin remote_add "--no-tags"
                ;;
        add,*)
                ;;
        set-head,--*)
-               __gitcomp "--auto --delete"
+               __gitcomp_builtin remote_set-head
                ;;
        set-branches,--*)
-               __gitcomp "--add"
+               __gitcomp_builtin remote_set-branches
                ;;
        set-head,*|set-branches,*)
                __git_complete_remote_or_refspec
                ;;
        update,--*)
-               __gitcomp "--prune"
+               __gitcomp_builtin remote_update
                ;;
        update,*)
                __gitcomp "$(__git_get_config_variables "remotes")"
                ;;
        set-url,--*)
-               __gitcomp "--push --add --delete"
+               __gitcomp_builtin remote_set-url
                ;;
        get-url,--*)
-               __gitcomp "--push --all"
+               __gitcomp_builtin remote_get-url
                ;;
        prune,--*)
-               __gitcomp "--dry-run"
+               __gitcomp_builtin remote_prune
                ;;
        *)
                __gitcomp_nl "$(__git_remotes)"
@@@ -2721,7 -2648,7 +2648,7 @@@ _git_replace (
  {
        case "$cur" in
        --*)
-               __gitcomp "--edit --graft --format= --list --delete"
+               __gitcomp_builtin replace
                return
                ;;
        esac
@@@ -2745,26 -2672,26 +2672,26 @@@ _git_reset (
  
        case "$cur" in
        --*)
-               __gitcomp "--merge --mixed --hard --soft --patch --keep"
+               __gitcomp_builtin reset
                return
                ;;
        esac
        __git_complete_refs
  }
  
+ __git_revert_inprogress_options="--continue --quit --abort"
  _git_revert ()
  {
        __git_find_repo_path
        if [ -f "$__git_repo_path"/REVERT_HEAD ]; then
-               __gitcomp "--continue --quit --abort"
+               __gitcomp "$__git_revert_inprogress_options"
                return
        fi
        case "$cur" in
        --*)
-               __gitcomp "
-                       --edit --mainline --no-edit --no-commit --signoff
-                       --strategy= --strategy-option=
-                       "
+               __gitcomp_builtin revert "--no-edit" \
+                       "$__git_revert_inprogress_options"
                return
                ;;
        esac
@@@ -2775,7 -2702,7 +2702,7 @@@ _git_rm (
  {
        case "$cur" in
        --*)
-               __gitcomp "--cached --dry-run --ignore-unmatch --quiet"
+               __gitcomp_builtin rm
                return
                ;;
        esac
@@@ -2833,12 -2760,7 +2760,7 @@@ _git_show_branch (
  {
        case "$cur" in
        --*)
-               __gitcomp "
-                       --all --remotes --topo-order --date-order --current --more=
-                       --list --independent --merge-base --no-name
-                       --color --no-color
-                       --sha1-name --sparse --topics --reflog
-                       "
+               __gitcomp_builtin show-branch "--no-color"
                return
                ;;
        esac
@@@ -3071,11 -2993,7 +2993,7 @@@ _git_tag (
  
        case "$cur" in
        --*)
-               __gitcomp "
-                       --list --delete --verify --annotate --message --file
-                       --sign --cleanup --local-user --force --column --sort=
-                       --contains --no-contains --points-at --merged --no-merged --create-reflog
-                       "
+               __gitcomp_builtin tag
                ;;
        esac
  }
@@@ -3087,27 -3005,24 +3005,27 @@@ _git_whatchanged (
  
  _git_worktree ()
  {
 -      local subcommands="add list lock prune unlock"
 +      local subcommands="add list lock move prune remove unlock"
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
        else
                case "$subcommand,$cur" in
                add,--*)
-                       __gitcomp "--detach"
+                       __gitcomp_builtin worktree_add
                        ;;
                list,--*)
-                       __gitcomp "--porcelain"
+                       __gitcomp_builtin worktree_list
                        ;;
                lock,--*)
-                       __gitcomp "--reason"
+                       __gitcomp_builtin worktree_lock
                        ;;
                prune,--*)
-                       __gitcomp "--dry-run --expire --verbose"
+                       __gitcomp_builtin worktree_prune
                        ;;
 +              remove,--*)
 +                      __gitcomp "--force"
 +                      ;;
                *)
                        ;;
                esac
diff --combined parse-options.c
index d02eb8b0151626bab833489d7139a9be34209a32,979577ba2c7a045ddd098f3bfb5cfb96706599d3..125e84f98451b4eb12e9d8a6cb4da58b2d8db51e
@@@ -425,6 -425,48 +425,48 @@@ void parse_options_start(struct parse_o
        parse_options_check(options);
  }
  
+ /*
+  * TODO: we are not completing the --no-XXX form yet because there are
+  * many options that do not suppress it properly.
+  */
+ static int show_gitcomp(struct parse_opt_ctx_t *ctx,
+                       const struct option *opts)
+ {
+       for (; opts->type != OPTION_END; opts++) {
+               const char *suffix = "";
+               if (!opts->long_name)
+                       continue;
+               if (opts->flags & (PARSE_OPT_HIDDEN | PARSE_OPT_NOCOMPLETE))
+                       continue;
+               switch (opts->type) {
+               case OPTION_GROUP:
+                       continue;
+               case OPTION_STRING:
+               case OPTION_FILENAME:
+               case OPTION_INTEGER:
+               case OPTION_MAGNITUDE:
+               case OPTION_CALLBACK:
+                       if (opts->flags & PARSE_OPT_NOARG)
+                               break;
+                       if (opts->flags & PARSE_OPT_OPTARG)
+                               break;
+                       if (opts->flags & PARSE_OPT_LASTARG_DEFAULT)
+                               break;
+                       suffix = "=";
+                       break;
+               default:
+                       break;
+               }
+               if (opts->flags & PARSE_OPT_COMP_ARG)
+                       suffix = "=";
+               printf(" --%s%s", opts->long_name, suffix);
+       }
+       fputc('\n', stdout);
+       exit(0);
+ }
  static int usage_with_options_internal(struct parse_opt_ctx_t *,
                                       const char * const *,
                                       const struct option *, int, int);
@@@ -455,6 -497,10 +497,10 @@@ int parse_options_step(struct parse_opt
                if (internal_help && ctx->total == 1 && !strcmp(arg + 1, "h"))
                        goto show_usage;
  
+               /* lone --git-completion-helper is asked by git-completion.bash */
+               if (ctx->total == 1 && !strcmp(arg + 1, "-git-completion-helper"))
+                       return show_gitcomp(ctx, options);
                if (arg[1] != '-') {
                        ctx->opt = arg + 1;
                        switch (parse_short_opt(ctx, options)) {
@@@ -525,7 -571,7 +571,7 @@@ unknown
  
  int parse_options_end(struct parse_opt_ctx_t *ctx)
  {
 -      memmove(ctx->out + ctx->cpidx, ctx->argv, ctx->argc * sizeof(*ctx->out));
 +      MOVE_ARRAY(ctx->out + ctx->cpidx, ctx->argv, ctx->argc);
        ctx->out[ctx->cpidx + ctx->argc] = NULL;
        return ctx->cpidx + ctx->argc;
  }