Merge branch 'nd/worktree-cleanup-post-head-protection'
authorJunio C Hamano <gitster@pobox.com>
Wed, 6 Jul 2016 20:38:11 +0000 (13:38 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 6 Jul 2016 20:38:11 +0000 (13:38 -0700)
Further preparatory clean-up for "worktree" feature continues.

* nd/worktree-cleanup-post-head-protection:
worktree: simplify prefixing paths
worktree: avoid 0{40}, too many zeroes, hard to read
worktree.c: use is_dot_or_dotdot()
git-worktree.txt: keep subcommand listing in alphabetical order
worktree.c: rewrite mark_current_worktree() to avoid strbuf
completion: support git-worktree

1  2 
Documentation/git-worktree.txt
builtin/worktree.c
contrib/completion/git-completion.bash
worktree.c
index 23d8d2ace04e7867a5b147d3d59c2d1b0d7a36ae,27feff6dba6f8b54ef0141f452b88203198fb283..7c4cfb0885f44619761708cb2a2d12356fbe011e
@@@ -10,8 -10,8 +10,8 @@@ SYNOPSI
  --------
  [verse]
  'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
- 'git worktree prune' [-n] [-v] [--expire <expire>]
  'git worktree list' [--porcelain]
+ 'git worktree prune' [-n] [-v] [--expire <expire>]
  
  DESCRIPTION
  -----------
@@@ -48,17 -48,12 +48,13 @@@ add <path> [<branch>]:
  
  Create `<path>` and checkout `<branch>` into it. The new working directory
  is linked to the current repository, sharing everything except working
 -directory specific files such as HEAD, index, etc.
 +directory specific files such as HEAD, index, etc. `-` may also be
 +specified as `<branch>`; it is synonymous with `@{-1}`.
  +
  If `<branch>` is omitted and neither `-b` nor `-B` nor `--detached` used,
  then, as a convenience, a new branch based at HEAD is created automatically,
  as if `-b $(basename <path>)` was specified.
  
- prune::
- Prune working tree information in $GIT_DIR/worktrees.
  list::
  
  List details of each worktree.  The main worktree is listed first, followed by
@@@ -66,6 -61,10 +62,10 @@@ each of the linked worktrees.  The outp
  bare, the revision currently checked out, and the branch currently checked out
  (or 'detached HEAD' if none).
  
+ prune::
+ Prune working tree information in $GIT_DIR/worktrees.
  OPTIONS
  -------
  
diff --combined builtin/worktree.c
index e3199a22e5a1111366b03ad0a2564e8d4f0790c7,f9dac376f748480c72023e9f4375edc90069a2ce..e866844685d519e2406c050da497c42506077f7d
@@@ -13,8 -13,8 +13,8 @@@
  
  static const char * const worktree_usage[] = {
        N_("git worktree add [<options>] <path> [<branch>]"),
-       N_("git worktree prune [<options>]"),
        N_("git worktree list [<options>]"),
+       N_("git worktree prune [<options>]"),
        NULL
  };
  
@@@ -95,7 -95,7 +95,7 @@@ static void prune_worktrees(void
        if (!dir)
                return;
        while ((d = readdir(dir)) != NULL) {
-               if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+               if (is_dot_or_dotdot(d->d_name))
                        continue;
                strbuf_reset(&reason);
                if (!prune_worktree(d->d_name, &reason))
                if (ret < 0 && errno == ENOTDIR)
                        ret = unlink(path.buf);
                if (ret)
 -                      error(_("failed to remove: %s"), strerror(errno));
 +                      error_errno(_("failed to remove '%s'"), path.buf);
        }
        closedir(dir);
        if (!show_only)
@@@ -262,7 -262,7 +262,7 @@@ static int add_worktree(const char *pat
         */
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/HEAD", sb_repo.buf);
-       write_file(sb.buf, "0000000000000000000000000000000000000000");
+       write_file(sb.buf, sha1_to_hex(null_sha1));
        strbuf_reset(&sb);
        strbuf_addf(&sb, "%s/commondir", sb_repo.buf);
        write_file(sb.buf, "../..");
@@@ -337,12 -337,9 +337,12 @@@ static int add(int ac, const char **av
        if (ac < 1 || ac > 2)
                usage_with_options(worktree_usage, options);
  
-       path = prefix ? prefix_filename(prefix, strlen(prefix), av[0]) : av[0];
+       path = prefix_filename(prefix, strlen(prefix), av[0]);
        branch = ac < 2 ? "HEAD" : av[1];
  
 +      if (!strcmp(branch, "-"))
 +              branch = "@{-1}";
 +
        opts.force_new_branch = !!new_branch_force;
        if (opts.force_new_branch) {
                struct strbuf symref = STRBUF_INIT;
@@@ -470,6 -467,8 +470,8 @@@ int cmd_worktree(int ac, const char **a
  
        if (ac < 2)
                usage_with_options(worktree_usage, options);
+       if (!prefix)
+               prefix = "";
        if (!strcmp(av[1], "add"))
                return add(ac - 1, av + 1, prefix);
        if (!strcmp(av[1], "prune"))
index ddda5e5e27a1f0dcf0513e4c1c0037f218f1286d,951a186df0d6a695cb7ec58d85dd1682dfe9a70a..37888f4e570e7f268bdc3e735c63f7a136774aa3
@@@ -803,50 -803,6 +803,50 @@@ __git_find_on_cmdline (
        done
  }
  
 +# Echo the value of an option set on the command line or config
 +#
 +# $1: short option name
 +# $2: long option name including =
 +# $3: list of possible values
 +# $4: config string (optional)
 +#
 +# example:
 +# result="$(__git_get_option_value "-d" "--do-something=" \
 +#     "yes no" "core.doSomething")"
 +#
 +# result is then either empty (no option set) or "yes" or "no"
 +#
 +# __git_get_option_value requires 3 arguments
 +__git_get_option_value ()
 +{
 +      local c short_opt long_opt val
 +      local result= values config_key word
 +
 +      short_opt="$1"
 +      long_opt="$2"
 +      values="$3"
 +      config_key="$4"
 +
 +      ((c = $cword - 1))
 +      while [ $c -ge 0 ]; do
 +              word="${words[c]}"
 +              for val in $values; do
 +                      if [ "$short_opt$val" = "$word" ] ||
 +                         [ "$long_opt$val"  = "$word" ]; then
 +                              result="$val"
 +                              break 2
 +                      fi
 +              done
 +              ((c--))
 +      done
 +
 +      if [ -n "$config_key" ] && [ -z "$result" ]; then
 +              result="$(git --git-dir="$(__gitdir)" config "$config_key")"
 +      fi
 +
 +      echo "$result"
 +}
 +
  __git_has_doubledash ()
  {
        local c=1
@@@ -1142,8 -1098,6 +1142,8 @@@ _git_clone (
        esac
  }
  
 +__git_untracked_file_modes="all no normal"
 +
  _git_commit ()
  {
        case "$prev" in
                return
                ;;
        --untracked-files=*)
 -              __gitcomp "all no normal" "" "${cur##--untracked-files=}"
 +              __gitcomp "$__git_untracked_file_modes" "" "${cur##--untracked-files=}"
                return
                ;;
        --*)
@@@ -1826,56 -1780,6 +1826,56 @@@ _git_stage (
        _git_add
  }
  
 +_git_status ()
 +{
 +      local complete_opt
 +      local untracked_state
 +
 +      case "$cur" in
 +      --ignore-submodules=*)
 +              __gitcomp "none untracked dirty all" "" "${cur##--ignore-submodules=}"
 +              return
 +              ;;
 +      --untracked-files=*)
 +              __gitcomp "$__git_untracked_file_modes" "" "${cur##--untracked-files=}"
 +              return
 +              ;;
 +      --column=*)
 +              __gitcomp "
 +                      always never auto column row plain dense nodense
 +                      " "" "${cur##--column=}"
 +              return
 +              ;;
 +      --*)
 +              __gitcomp "
 +                      --short --branch --porcelain --long --verbose
 +                      --untracked-files= --ignore-submodules= --ignored
 +                      --column= --no-column
 +                      "
 +              return
 +              ;;
 +      esac
 +
 +      untracked_state="$(__git_get_option_value "-u" "--untracked-files=" \
 +              "$__git_untracked_file_modes" "status.showUntrackedFiles")"
 +
 +      case "$untracked_state" in
 +      no)
 +              # --ignored option does not matter
 +              complete_opt=
 +              ;;
 +      all|normal|*)
 +              complete_opt="--cached --directory --no-empty-directory --others"
 +
 +              if [ -n "$(__git_find_on_cmdline "--ignored")" ]; then
 +                      complete_opt="$complete_opt --ignored --exclude=*"
 +              fi
 +              ;;
 +      esac
 +
 +      __git_complete_index_file "$complete_opt"
 +}
 +
  __git_config_get_set_variables ()
  {
        local prevword word config_file= c=$cword
@@@ -2691,6 -2595,29 +2691,29 @@@ _git_whatchanged (
        _git_log
  }
  
+ _git_worktree ()
+ {
+       local subcommands="add list prune"
+       local subcommand="$(__git_find_on_cmdline "$subcommands")"
+       if [ -z "$subcommand" ]; then
+               __gitcomp "$subcommands"
+       else
+               case "$subcommand,$cur" in
+               add,--*)
+                       __gitcomp "--detach"
+                       ;;
+               list,--*)
+                       __gitcomp "--porcelain"
+                       ;;
+               prune,--*)
+                       __gitcomp "--dry-run --expire --verbose"
+                       ;;
+               *)
+                       ;;
+               esac
+       fi
+ }
  __git_main ()
  {
        local i c=1 command __git_dir
diff --combined worktree.c
index 199b1ef94ba84aad094a5a0fe95360c2df631003,f4a4f3809298023250e51b780687c0305e67cef6..e2a94e04768b7484285ed2aa84259c6818b2cd02
@@@ -20,7 -20,7 +20,7 @@@ void free_worktrees(struct worktree **w
  
  /*
   * read 'path_to_ref' into 'ref'.  Also if is_detached is not NULL,
 - * set is_detached to 1 (0) if the ref is detatched (is not detached).
 + * set is_detached to 1 (0) if the ref is detached (is not detached).
   *
   * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so
   * for linked worktrees, `resolve_ref_unsafe()` won't work (it uses
@@@ -153,21 -153,19 +153,19 @@@ done
  
  static void mark_current_worktree(struct worktree **worktrees)
  {
-       struct strbuf git_dir = STRBUF_INIT;
-       struct strbuf path = STRBUF_INIT;
+       char *git_dir = xstrdup(absolute_path(get_git_dir()));
        int i;
  
-       strbuf_addstr(&git_dir, absolute_path(get_git_dir()));
        for (i = 0; worktrees[i]; i++) {
                struct worktree *wt = worktrees[i];
-               strbuf_addstr(&path, absolute_path(get_worktree_git_dir(wt)));
-               wt->is_current = !fspathcmp(git_dir.buf, path.buf);
-               strbuf_reset(&path);
-               if (wt->is_current)
+               const char *wt_git_dir = get_worktree_git_dir(wt);
+               if (!fspathcmp(git_dir, absolute_path(wt_git_dir))) {
+                       wt->is_current = 1;
                        break;
+               }
        }
-       strbuf_release(&git_dir);
-       strbuf_release(&path);
+       free(git_dir);
  }
  
  struct worktree **get_worktrees(void)
        if (dir) {
                while ((d = readdir(dir)) != NULL) {
                        struct worktree *linked = NULL;
-                       if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+                       if (is_dot_or_dotdot(d->d_name))
                                continue;
  
                        if ((linked = get_linked_worktree(d->d_name))) {