Merge branch 'jn/help-everywhere'
authorJunio C Hamano <gitster@pobox.com>
Sat, 21 Nov 2009 07:44:52 +0000 (23:44 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sat, 21 Nov 2009 07:44:52 +0000 (23:44 -0800)
* jn/help-everywhere: (23 commits)
diff --no-index: make the usage string less scary
merge-{recursive,subtree}: use usagef() to print usage
Introduce usagef() that takes a printf-style format
Let 'git <command> -h' show usage without a git dir
Show usage string for 'git http-push -h'
Let 'git http-fetch -h' show usage outside any git repository
Show usage string for 'git stripspace -h'
Show usage string for 'git unpack-file -h'
Show usage string for 'git show-index -h'
Show usage string for 'git rev-parse -h'
Show usage string for 'git merge-one-file -h'
Show usage string for 'git mailsplit -h'
Show usage string for 'git imap-send -h'
Show usage string for 'git get-tar-commit-id -h'
Show usage string for 'git fast-import -h'
Show usage string for 'git check-ref-format -h'
http-fetch: add missing initialization of argv0_path
Show usage string for 'git show-ref -h'
Show usage string for 'git merge-ours -h'
Show usage string for 'git commit-tree -h'
...

Conflicts:
imap-send.c

1  2 
Makefile
builtin-log.c
builtin-rev-parse.c
contrib/examples/builtin-fetch--tool.c
imap-send.c
diff --combined Makefile
index 457f9274d75fc04e874548051506a2f32616492e,c0ba479d216503a3718c5d4b39784b996d074a8f..d045223837ebd79426572d4bac53450910ae5ab6
+++ b/Makefile
@@@ -358,7 -358,6 +358,7 @@@ EXTRA_PROGRAMS 
  PROGRAMS += $(EXTRA_PROGRAMS)
  PROGRAMS += git-fast-import$X
  PROGRAMS += git-hash-object$X
 +PROGRAMS += git-imap-send$X
  PROGRAMS += git-index-pack$X
  PROGRAMS += git-merge-index$X
  PROGRAMS += git-merge-tree$X
@@@ -602,7 -601,6 +602,6 @@@ BUILTIN_OBJS += builtin-diff-index.
  BUILTIN_OBJS += builtin-diff-tree.o
  BUILTIN_OBJS += builtin-diff.o
  BUILTIN_OBJS += builtin-fast-export.o
- BUILTIN_OBJS += builtin-fetch--tool.o
  BUILTIN_OBJS += builtin-fetch-pack.o
  BUILTIN_OBJS += builtin-fetch.o
  BUILTIN_OBJS += builtin-fmt-merge-msg.o
@@@ -907,7 -905,7 +906,7 @@@ ifdef MSV
        GIT_VERSION := $(GIT_VERSION).MSVC
        pathsep = ;
        NO_PREAD = YesPlease
 -      NO_OPENSSL = YesPlease
 +      NEEDS_CRYPTO_WITH_SSL = YesPlease
        NO_LIBGEN_H = YesPlease
        NO_SYMLINK_HEAD = YesPlease
        NO_IPV6 = YesPlease
        NO_REGEX = YesPlease
        NO_CURL = YesPlease
        NO_PTHREADS = YesPlease
 +      BLK_SHA1 = YesPlease
  
        CC = compat/vcbuild/scripts/clink.pl
        AR = compat/vcbuild/scripts/lib.pl
@@@ -960,7 -957,7 +959,7 @@@ els
  ifneq (,$(findstring MINGW,$(uname_S)))
        pathsep = ;
        NO_PREAD = YesPlease
 -      NO_OPENSSL = YesPlease
 +      NEEDS_CRYPTO_WITH_SSL = YesPlease
        NO_LIBGEN_H = YesPlease
        NO_SYMLINK_HEAD = YesPlease
        NO_IPV6 = YesPlease
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        NO_REGEX = YesPlease
 +      BLK_SHA1 = YesPlease
        COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
        COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o
@@@ -1085,6 -1081,7 +1084,6 @@@ EXTLIBS += -l
  
  ifndef NO_POSIX_ONLY_PROGRAMS
        PROGRAMS += git-daemon$X
 -      PROGRAMS += git-imap-send$X
  endif
  ifndef NO_OPENSSL
        OPENSSL_LIBSSL = -lssl
@@@ -1808,10 -1805,7 +1807,10 @@@ dist: git.spec git-archive$(X) configur
        gzip -f -9 $(GIT_TARNAME).tar
  
  rpm: dist
 -      $(RPMBUILD) -ta $(GIT_TARNAME).tar.gz
 +      $(RPMBUILD) \
 +              --define "_source_filedigest_algorithm md5" \
 +              --define "_binary_filedigest_algorithm md5" \
 +              -ta $(GIT_TARNAME).tar.gz
  
  htmldocs = git-htmldocs-$(GIT_VERSION)
  manpages = git-manpages-$(GIT_VERSION)
diff --combined builtin-log.c
index fb5308220dc36ef9324326638dd67756c9930503,a0fa30c408090ba4ec5ceefff974a8008ce18aaa..ef5b3c3ae393b09f2dfb643ea1dd3bfc623ddc55
@@@ -50,6 -50,12 +50,12 @@@ static void cmd_log_init(int argc, cons
        if (default_date_mode)
                rev->date_mode = parse_date_format(default_date_mode);
  
+       /*
+        * Check for -h before setup_revisions(), or "git log -h" will
+        * fail when run without a git directory.
+        */
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage(builtin_log_usage);
        argc = setup_revisions(argc, argv, rev, "HEAD");
  
        if (rev->diffopt.pickaxe || rev->diffopt.filter)
@@@ -891,7 -897,6 +897,7 @@@ int cmd_format_patch(int argc, const ch
        struct patch_ids ids;
        char *add_signoff = NULL;
        struct strbuf buf = STRBUF_INIT;
 +      int use_patch_format = 0;
        const struct option builtin_format_patch_options[] = {
                { OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
                            "use [PATCH n/m] even with a single patch",
                            PARSE_OPT_NOARG | PARSE_OPT_NONEG, keep_callback },
                OPT_BOOLEAN(0, "no-binary", &no_binary_diff,
                            "don't output binary diffs"),
 +              OPT_BOOLEAN('p', NULL, &use_patch_format,
 +                      "show patch format instead of default (patch + stat)"),
                OPT_BOOLEAN(0, "ignore-if-in-upstream", &ignore_if_in_upstream,
                            "don't include a patch matching a commit upstream"),
                OPT_GROUP("Messaging"),
        if (argc > 1)
                die ("unrecognized argument: %s", argv[1]);
  
 -      if (!rev.diffopt.output_format
 -              || rev.diffopt.output_format == DIFF_FORMAT_PATCH)
 +      if (use_patch_format)
 +              rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
 +      else if (!rev.diffopt.output_format ||
 +                rev.diffopt.output_format == DIFF_FORMAT_PATCH)
                rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_SUMMARY | DIFF_FORMAT_PATCH;
  
        if (!DIFF_OPT_TST(&rev.diffopt, TEXT) && !no_binary_diff)
@@@ -1242,6 -1243,9 +1248,9 @@@ int cmd_cherry(int argc, const char **a
                argv++;
        }
  
+       if (argc > 1 && !strcmp(argv[1], "-h"))
+               usage(cherry_usage);
        switch (argc) {
        case 4:
                limit = argv[3];
diff --combined builtin-rev-parse.c
index 9526aafc6c732de60f05f799c46fbf139377cccf,24ee8b32421a487cba7db768981943471f7a36fd..37d02335212dea9672e2472970f66dc4ac95dfd1
@@@ -180,12 -180,6 +180,12 @@@ static int show_reference(const char *r
        return 0;
  }
  
 +static int anti_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 +{
 +      show_rev(REVERSED, sha1, refname);
 +      return 0;
 +}
 +
  static void show_datestring(const char *flag, const char *datestr)
  {
        static char buffer[100];
@@@ -432,6 -426,13 +432,13 @@@ static void die_no_single_rev(int quiet
                die("Needed a single revision");
  }
  
+ static const char builtin_rev_parse_usage[] =
+ "git rev-parse --parseopt [options] -- [<args>...]\n"
+ "   or: git rev-parse --sq-quote [<arg>...]\n"
+ "   or: git rev-parse [options] [<arg>...]\n"
+ "\n"
+ "Run \"git rev-parse --parseopt -h\" for more information on the first usage.";
  int cmd_rev_parse(int argc, const char **argv, const char *prefix)
  {
        int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
        if (argc > 1 && !strcmp("--sq-quote", argv[1]))
                return cmd_sq_quote(argc - 2, argv + 2);
  
+       if (argc > 1 && !strcmp("-h", argv[1]))
+               usage(builtin_rev_parse_usage);
        prefix = setup_git_directory();
        git_config(git_default_config, NULL);
        for (i = 1; i < argc; i++) {
                                for_each_ref(show_reference, NULL);
                                continue;
                        }
 +                      if (!strcmp(arg, "--bisect")) {
 +                              for_each_ref_in("refs/bisect/bad", show_reference, NULL);
 +                              for_each_ref_in("refs/bisect/good", anti_reference, NULL);
 +                              continue;
 +                      }
                        if (!strcmp(arg, "--branches")) {
                                for_each_branch_ref(show_reference, NULL);
                                continue;
index 0000000000000000000000000000000000000000,3dbdf7a2887002f0d7a67a1db35bd3c72d7a9d30..cd10dbcbc90ee155e05cffb49e88defcf35c5a59
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,574 +1,574 @@@
 -              fprintf(stderr, "* %s: fast forward to %s\n",
+ #include "builtin.h"
+ #include "cache.h"
+ #include "refs.h"
+ #include "commit.h"
+ #include "sigchain.h"
+ static char *get_stdin(void)
+ {
+       struct strbuf buf = STRBUF_INIT;
+       if (strbuf_read(&buf, 0, 1024) < 0) {
+               die_errno("error reading standard input");
+       }
+       return strbuf_detach(&buf, NULL);
+ }
+ static void show_new(enum object_type type, unsigned char *sha1_new)
+ {
+       fprintf(stderr, "  %s: %s\n", typename(type),
+               find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
+ }
+ static int update_ref_env(const char *action,
+                     const char *refname,
+                     unsigned char *sha1,
+                     unsigned char *oldval)
+ {
+       char msg[1024];
+       const char *rla = getenv("GIT_REFLOG_ACTION");
+       if (!rla)
+               rla = "(reflog update)";
+       if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg))
+               warning("reflog message too long: %.*s...", 50, msg);
+       return update_ref(msg, refname, sha1, oldval, 0, QUIET_ON_ERR);
+ }
+ static int update_local_ref(const char *name,
+                           const char *new_head,
+                           const char *note,
+                           int verbose, int force)
+ {
+       unsigned char sha1_old[20], sha1_new[20];
+       char oldh[41], newh[41];
+       struct commit *current, *updated;
+       enum object_type type;
+       if (get_sha1_hex(new_head, sha1_new))
+               die("malformed object name %s", new_head);
+       type = sha1_object_info(sha1_new, NULL);
+       if (type < 0)
+               die("object %s not found", new_head);
+       if (!*name) {
+               /* Not storing */
+               if (verbose) {
+                       fprintf(stderr, "* fetched %s\n", note);
+                       show_new(type, sha1_new);
+               }
+               return 0;
+       }
+       if (get_sha1(name, sha1_old)) {
+               const char *msg;
+       just_store:
+               /* new ref */
+               if (!strncmp(name, "refs/tags/", 10))
+                       msg = "storing tag";
+               else
+                       msg = "storing head";
+               fprintf(stderr, "* %s: storing %s\n",
+                       name, note);
+               show_new(type, sha1_new);
+               return update_ref_env(msg, name, sha1_new, NULL);
+       }
+       if (!hashcmp(sha1_old, sha1_new)) {
+               if (verbose) {
+                       fprintf(stderr, "* %s: same as %s\n", name, note);
+                       show_new(type, sha1_new);
+               }
+               return 0;
+       }
+       if (!strncmp(name, "refs/tags/", 10)) {
+               fprintf(stderr, "* %s: updating with %s\n", name, note);
+               show_new(type, sha1_new);
+               return update_ref_env("updating tag", name, sha1_new, NULL);
+       }
+       current = lookup_commit_reference(sha1_old);
+       updated = lookup_commit_reference(sha1_new);
+       if (!current || !updated)
+               goto just_store;
+       strcpy(oldh, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
+       strcpy(newh, find_unique_abbrev(sha1_new, DEFAULT_ABBREV));
+       if (in_merge_bases(current, &updated, 1)) {
 -              return update_ref_env("fast forward", name, sha1_new, sha1_old);
++              fprintf(stderr, "* %s: fast-forward to %s\n",
+                       name, note);
+               fprintf(stderr, "  old..new: %s..%s\n", oldh, newh);
 -                      "* %s: not updating to non-fast forward %s\n",
++              return update_ref_env("fast-forward", name, sha1_new, sha1_old);
+       }
+       if (!force) {
+               fprintf(stderr,
 -              "* %s: forcing update to non-fast forward %s\n",
++                      "* %s: not updating to non-fast-forward %s\n",
+                       name, note);
+               fprintf(stderr,
+                       "  old...new: %s...%s\n", oldh, newh);
+               return 1;
+       }
+       fprintf(stderr,
++              "* %s: forcing update to non-fast-forward %s\n",
+               name, note);
+       fprintf(stderr, "  old...new: %s...%s\n", oldh, newh);
+       return update_ref_env("forced-update", name, sha1_new, sha1_old);
+ }
+ static int append_fetch_head(FILE *fp,
+                            const char *head, const char *remote,
+                            const char *remote_name, const char *remote_nick,
+                            const char *local_name, int not_for_merge,
+                            int verbose, int force)
+ {
+       struct commit *commit;
+       int remote_len, i, note_len;
+       unsigned char sha1[20];
+       char note[1024];
+       const char *what, *kind;
+       if (get_sha1(head, sha1))
+               return error("Not a valid object name: %s", head);
+       commit = lookup_commit_reference_gently(sha1, 1);
+       if (!commit)
+               not_for_merge = 1;
+       if (!strcmp(remote_name, "HEAD")) {
+               kind = "";
+               what = "";
+       }
+       else if (!strncmp(remote_name, "refs/heads/", 11)) {
+               kind = "branch";
+               what = remote_name + 11;
+       }
+       else if (!strncmp(remote_name, "refs/tags/", 10)) {
+               kind = "tag";
+               what = remote_name + 10;
+       }
+       else if (!strncmp(remote_name, "refs/remotes/", 13)) {
+               kind = "remote branch";
+               what = remote_name + 13;
+       }
+       else {
+               kind = "";
+               what = remote_name;
+       }
+       remote_len = strlen(remote);
+       for (i = remote_len - 1; remote[i] == '/' && 0 <= i; i--)
+               ;
+       remote_len = i + 1;
+       if (4 < i && !strncmp(".git", remote + i - 3, 4))
+               remote_len = i - 3;
+       note_len = 0;
+       if (*what) {
+               if (*kind)
+                       note_len += sprintf(note + note_len, "%s ", kind);
+               note_len += sprintf(note + note_len, "'%s' of ", what);
+       }
+       note_len += sprintf(note + note_len, "%.*s", remote_len, remote);
+       fprintf(fp, "%s\t%s\t%s\n",
+               sha1_to_hex(commit ? commit->object.sha1 : sha1),
+               not_for_merge ? "not-for-merge" : "",
+               note);
+       return update_local_ref(local_name, head, note, verbose, force);
+ }
+ static char *keep;
+ static void remove_keep(void)
+ {
+       if (keep && *keep)
+               unlink(keep);
+ }
+ static void remove_keep_on_signal(int signo)
+ {
+       remove_keep();
+       sigchain_pop(signo);
+       raise(signo);
+ }
+ static char *find_local_name(const char *remote_name, const char *refs,
+                            int *force_p, int *not_for_merge_p)
+ {
+       const char *ref = refs;
+       int len = strlen(remote_name);
+       while (ref) {
+               const char *next;
+               int single_force, not_for_merge;
+               while (*ref == '\n')
+                       ref++;
+               if (!*ref)
+                       break;
+               next = strchr(ref, '\n');
+               single_force = not_for_merge = 0;
+               if (*ref == '+') {
+                       single_force = 1;
+                       ref++;
+               }
+               if (*ref == '.') {
+                       not_for_merge = 1;
+                       ref++;
+                       if (*ref == '+') {
+                               single_force = 1;
+                               ref++;
+                       }
+               }
+               if (!strncmp(remote_name, ref, len) && ref[len] == ':') {
+                       const char *local_part = ref + len + 1;
+                       int retlen;
+                       if (!next)
+                               retlen = strlen(local_part);
+                       else
+                               retlen = next - local_part;
+                       *force_p = single_force;
+                       *not_for_merge_p = not_for_merge;
+                       return xmemdupz(local_part, retlen);
+               }
+               ref = next;
+       }
+       return NULL;
+ }
+ static int fetch_native_store(FILE *fp,
+                             const char *remote,
+                             const char *remote_nick,
+                             const char *refs,
+                             int verbose, int force)
+ {
+       char buffer[1024];
+       int err = 0;
+       sigchain_push_common(remove_keep_on_signal);
+       atexit(remove_keep);
+       while (fgets(buffer, sizeof(buffer), stdin)) {
+               int len;
+               char *cp;
+               char *local_name;
+               int single_force, not_for_merge;
+               for (cp = buffer; *cp && !isspace(*cp); cp++)
+                       ;
+               if (*cp)
+                       *cp++ = 0;
+               len = strlen(cp);
+               if (len && cp[len-1] == '\n')
+                       cp[--len] = 0;
+               if (!strcmp(buffer, "failed"))
+                       die("Fetch failure: %s", remote);
+               if (!strcmp(buffer, "pack"))
+                       continue;
+               if (!strcmp(buffer, "keep")) {
+                       char *od = get_object_directory();
+                       int len = strlen(od) + strlen(cp) + 50;
+                       keep = xmalloc(len);
+                       sprintf(keep, "%s/pack/pack-%s.keep", od, cp);
+                       continue;
+               }
+               local_name = find_local_name(cp, refs,
+                                            &single_force, &not_for_merge);
+               if (!local_name)
+                       continue;
+               err |= append_fetch_head(fp,
+                                        buffer, remote, cp, remote_nick,
+                                        local_name, not_for_merge,
+                                        verbose, force || single_force);
+       }
+       return err;
+ }
+ static int parse_reflist(const char *reflist)
+ {
+       const char *ref;
+       printf("refs='");
+       for (ref = reflist; ref; ) {
+               const char *next;
+               while (*ref && isspace(*ref))
+                       ref++;
+               if (!*ref)
+                       break;
+               for (next = ref; *next && !isspace(*next); next++)
+                       ;
+               printf("\n%.*s", (int)(next - ref), ref);
+               ref = next;
+       }
+       printf("'\n");
+       printf("rref='");
+       for (ref = reflist; ref; ) {
+               const char *next, *colon;
+               while (*ref && isspace(*ref))
+                       ref++;
+               if (!*ref)
+                       break;
+               for (next = ref; *next && !isspace(*next); next++)
+                       ;
+               if (*ref == '.')
+                       ref++;
+               if (*ref == '+')
+                       ref++;
+               colon = strchr(ref, ':');
+               putchar('\n');
+               printf("%.*s", (int)((colon ? colon : next) - ref), ref);
+               ref = next;
+       }
+       printf("'\n");
+       return 0;
+ }
+ static int expand_refs_wildcard(const char *ls_remote_result, int numrefs,
+                               const char **refs)
+ {
+       int i, matchlen, replacelen;
+       int found_one = 0;
+       const char *remote = *refs++;
+       numrefs--;
+       if (numrefs == 0) {
+               fprintf(stderr, "Nothing specified for fetching with remote.%s.fetch\n",
+                       remote);
+               printf("empty\n");
+       }
+       for (i = 0; i < numrefs; i++) {
+               const char *ref = refs[i];
+               const char *lref = ref;
+               const char *colon;
+               const char *tail;
+               const char *ls;
+               const char *next;
+               if (*lref == '+')
+                       lref++;
+               colon = strchr(lref, ':');
+               tail = lref + strlen(lref);
+               if (!(colon &&
+                     2 < colon - lref &&
+                     colon[-1] == '*' &&
+                     colon[-2] == '/' &&
+                     2 < tail - (colon + 1) &&
+                     tail[-1] == '*' &&
+                     tail[-2] == '/')) {
+                       /* not a glob */
+                       if (!found_one++)
+                               printf("explicit\n");
+                       printf("%s\n", ref);
+                       continue;
+               }
+               /* glob */
+               if (!found_one++)
+                       printf("glob\n");
+               /* lref to colon-2 is remote hierarchy name;
+                * colon+1 to tail-2 is local.
+                */
+               matchlen = (colon-1) - lref;
+               replacelen = (tail-1) - (colon+1);
+               for (ls = ls_remote_result; ls; ls = next) {
+                       const char *eol;
+                       unsigned char sha1[20];
+                       int namelen;
+                       while (*ls && isspace(*ls))
+                               ls++;
+                       next = strchr(ls, '\n');
+                       eol = !next ? (ls + strlen(ls)) : next;
+                       if (!memcmp("^{}", eol-3, 3))
+                               continue;
+                       if (eol - ls < 40)
+                               continue;
+                       if (get_sha1_hex(ls, sha1))
+                               continue;
+                       ls += 40;
+                       while (ls < eol && isspace(*ls))
+                               ls++;
+                       /* ls to next (or eol) is the name.
+                        * is it identical to lref to colon-2?
+                        */
+                       if ((eol - ls) <= matchlen ||
+                           strncmp(ls, lref, matchlen))
+                               continue;
+                       /* Yes, it is a match */
+                       namelen = eol - ls;
+                       if (lref != ref)
+                               putchar('+');
+                       printf("%.*s:%.*s%.*s\n",
+                              namelen, ls,
+                              replacelen, colon + 1,
+                              namelen - matchlen, ls + matchlen);
+               }
+       }
+       return 0;
+ }
+ static int pick_rref(int sha1_only, const char *rref, const char *ls_remote_result)
+ {
+       int err = 0;
+       int lrr_count = lrr_count, i, pass;
+       const char *cp;
+       struct lrr {
+               const char *line;
+               const char *name;
+               int namelen;
+               int shown;
+       } *lrr_list = lrr_list;
+       for (pass = 0; pass < 2; pass++) {
+               /* pass 0 counts and allocates, pass 1 fills... */
+               cp = ls_remote_result;
+               i = 0;
+               while (1) {
+                       const char *np;
+                       while (*cp && isspace(*cp))
+                               cp++;
+                       if (!*cp)
+                               break;
+                       np = strchrnul(cp, '\n');
+                       if (pass) {
+                               lrr_list[i].line = cp;
+                               lrr_list[i].name = cp + 41;
+                               lrr_list[i].namelen = np - (cp + 41);
+                       }
+                       i++;
+                       cp = np;
+               }
+               if (!pass) {
+                       lrr_count = i;
+                       lrr_list = xcalloc(lrr_count, sizeof(*lrr_list));
+               }
+       }
+       while (1) {
+               const char *next;
+               int rreflen;
+               int i;
+               while (*rref && isspace(*rref))
+                       rref++;
+               if (!*rref)
+                       break;
+               next = strchrnul(rref, '\n');
+               rreflen = next - rref;
+               for (i = 0; i < lrr_count; i++) {
+                       struct lrr *lrr = &(lrr_list[i]);
+                       if (rreflen == lrr->namelen &&
+                           !memcmp(lrr->name, rref, rreflen)) {
+                               if (!lrr->shown)
+                                       printf("%.*s\n",
+                                              sha1_only ? 40 : lrr->namelen + 41,
+                                              lrr->line);
+                               lrr->shown = 1;
+                               break;
+                       }
+               }
+               if (lrr_count <= i) {
+                       error("pick-rref: %.*s not found", rreflen, rref);
+                       err = 1;
+               }
+               rref = next;
+       }
+       free(lrr_list);
+       return err;
+ }
+ int cmd_fetch__tool(int argc, const char **argv, const char *prefix)
+ {
+       int verbose = 0;
+       int force = 0;
+       int sopt = 0;
+       while (1 < argc) {
+               const char *arg = argv[1];
+               if (!strcmp("-v", arg))
+                       verbose = 1;
+               else if (!strcmp("-f", arg))
+                       force = 1;
+               else if (!strcmp("-s", arg))
+                       sopt = 1;
+               else
+                       break;
+               argc--;
+               argv++;
+       }
+       if (argc <= 1)
+               return error("Missing subcommand");
+       if (!strcmp("append-fetch-head", argv[1])) {
+               int result;
+               FILE *fp;
+               char *filename;
+               if (argc != 8)
+                       return error("append-fetch-head takes 6 args");
+               filename = git_path("FETCH_HEAD");
+               fp = fopen(filename, "a");
+               if (!fp)
+                       return error("cannot open %s: %s\n", filename, strerror(errno));
+               result = append_fetch_head(fp, argv[2], argv[3],
+                                          argv[4], argv[5],
+                                          argv[6], !!argv[7][0],
+                                          verbose, force);
+               fclose(fp);
+               return result;
+       }
+       if (!strcmp("native-store", argv[1])) {
+               int result;
+               FILE *fp;
+               char *filename;
+               if (argc != 5)
+                       return error("fetch-native-store takes 3 args");
+               filename = git_path("FETCH_HEAD");
+               fp = fopen(filename, "a");
+               if (!fp)
+                       return error("cannot open %s: %s\n", filename, strerror(errno));
+               result = fetch_native_store(fp, argv[2], argv[3], argv[4],
+                                           verbose, force);
+               fclose(fp);
+               return result;
+       }
+       if (!strcmp("parse-reflist", argv[1])) {
+               const char *reflist;
+               if (argc != 3)
+                       return error("parse-reflist takes 1 arg");
+               reflist = argv[2];
+               if (!strcmp(reflist, "-"))
+                       reflist = get_stdin();
+               return parse_reflist(reflist);
+       }
+       if (!strcmp("pick-rref", argv[1])) {
+               const char *ls_remote_result;
+               if (argc != 4)
+                       return error("pick-rref takes 2 args");
+               ls_remote_result = argv[3];
+               if (!strcmp(ls_remote_result, "-"))
+                       ls_remote_result = get_stdin();
+               return pick_rref(sopt, argv[2], ls_remote_result);
+       }
+       if (!strcmp("expand-refs-wildcard", argv[1])) {
+               const char *reflist;
+               if (argc < 4)
+                       return error("expand-refs-wildcard takes at least 2 args");
+               reflist = argv[2];
+               if (!strcmp(reflist, "-"))
+                       reflist = get_stdin();
+               return expand_refs_wildcard(reflist, argc - 3, argv + 3);
+       }
+       return error("Unknown subcommand: %s", argv[1]);
+ }
diff --combined imap-send.c
index 6c9938a0757a3c3b440c227951462f6972923afe,04e537406e4113d5c1ead7b7e4a1a425c9b2bf09..834301cf409c311d47b22a0f7504917a32847647
@@@ -24,7 -24,6 +24,7 @@@
  
  #include "cache.h"
  #include "exec_cmd.h"
 +#include "run-command.h"
  #ifdef NO_OPENSSL
  typedef void *SSL;
  #endif
@@@ -94,7 -93,8 +94,9 @@@ struct msg_data 
        unsigned int crlf:1;
  };
  
+ static const char imap_send_usage[] = "git imap-send < <mbox>";
 +#undef DRV_OK
  #define DRV_OK          0
  #define DRV_MSG_BAD     -1
  #define DRV_BOX_BAD     -2
@@@ -125,6 -125,9 +127,6 @@@ static int nfvasprintf(char **strp, con
        return len;
  }
  
 -static void arc4_init(void);
 -static unsigned char arc4_getbyte(void);
 -
  struct imap_server_conf {
        char *name;
        char *tunnel;
@@@ -153,7 -156,7 +155,7 @@@ struct imap_list 
  };
  
  struct imap_socket {
 -      int fd;
 +      int fd[2];
        SSL *ssl;
  };
  
@@@ -271,12 -274,8 +273,12 @@@ static int ssl_socket_connect(struct im
  #ifdef NO_OPENSSL
        fprintf(stderr, "SSL requested but SSL support not compiled in\n");
        return -1;
 +#else
 +#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
 +      const SSL_METHOD *meth;
  #else
        SSL_METHOD *meth;
 +#endif
        SSL_CTX *ctx;
        int ret;
  
                ssl_socket_perror("SSL_new");
                return -1;
        }
 -      if (!SSL_set_fd(sock->ssl, sock->fd)) {
 -              ssl_socket_perror("SSL_set_fd");
 +      if (!SSL_set_rfd(sock->ssl, sock->fd[0])) {
 +              ssl_socket_perror("SSL_set_rfd");
 +              return -1;
 +      }
 +      if (!SSL_set_wfd(sock->ssl, sock->fd[1])) {
 +              ssl_socket_perror("SSL_set_wfd");
                return -1;
        }
  
@@@ -334,12 -329,11 +336,12 @@@ static int socket_read(struct imap_sock
                n = SSL_read(sock->ssl, buf, len);
        else
  #endif
 -              n = xread(sock->fd, buf, len);
 +              n = xread(sock->fd[0], buf, len);
        if (n <= 0) {
                socket_perror("read", sock, n);
 -              close(sock->fd);
 -              sock->fd = -1;
 +              close(sock->fd[0]);
 +              close(sock->fd[1]);
 +              sock->fd[0] = sock->fd[1] = -1;
        }
        return n;
  }
@@@ -352,12 -346,11 +354,12 @@@ static int socket_write(struct imap_soc
                n = SSL_write(sock->ssl, buf, len);
        else
  #endif
 -              n = write_in_full(sock->fd, buf, len);
 +              n = write_in_full(sock->fd[1], buf, len);
        if (n != len) {
                socket_perror("write", sock, n);
 -              close(sock->fd);
 -              sock->fd = -1;
 +              close(sock->fd[0]);
 +              close(sock->fd[1]);
 +              sock->fd[0] = sock->fd[1] = -1;
        }
        return n;
  }
@@@ -370,8 -363,7 +372,8 @@@ static void socket_shutdown(struct imap
                SSL_free(sock->ssl);
        }
  #endif
 -      close(sock->fd);
 +      close(sock->fd[0]);
 +      close(sock->fd[1]);
  }
  
  /* simple line buffering */
@@@ -499,6 -491,52 +501,6 @@@ static int nfsnprintf(char *buf, int bl
        return ret;
  }
  
 -static struct {
 -      unsigned char i, j, s[256];
 -} rs;
 -
 -static void arc4_init(void)
 -{
 -      int i, fd;
 -      unsigned char j, si, dat[128];
 -
 -      if ((fd = open("/dev/urandom", O_RDONLY)) < 0 && (fd = open("/dev/random", O_RDONLY)) < 0) {
 -              fprintf(stderr, "Fatal: no random number source available.\n");
 -              exit(3);
 -      }
 -      if (read_in_full(fd, dat, 128) != 128) {
 -              fprintf(stderr, "Fatal: cannot read random number source.\n");
 -              exit(3);
 -      }
 -      close(fd);
 -
 -      for (i = 0; i < 256; i++)
 -              rs.s[i] = i;
 -      for (i = j = 0; i < 256; i++) {
 -              si = rs.s[i];
 -              j += si + dat[i & 127];
 -              rs.s[i] = rs.s[j];
 -              rs.s[j] = si;
 -      }
 -      rs.i = rs.j = 0;
 -
 -      for (i = 0; i < 256; i++)
 -              arc4_getbyte();
 -}
 -
 -static unsigned char arc4_getbyte(void)
 -{
 -      unsigned char si, sj;
 -
 -      rs.i++;
 -      si = rs.s[rs.i];
 -      rs.j += si;
 -      sj = rs.s[rs.j];
 -      rs.s[rs.i] = sj;
 -      rs.s[rs.j] = si;
 -      return rs.s[(si + sj) & 0xff];
 -}
 -
  static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
                                         struct imap_cmd_cb *cb,
                                         const char *fmt, va_list ap)
@@@ -924,7 -962,7 +926,7 @@@ static void imap_close_server(struct im
  {
        struct imap *imap = ictx->imap;
  
 -      if (imap->buf.sock.fd != -1) {
 +      if (imap->buf.sock.fd[0] != -1) {
                imap_exec(ictx, NULL, "LOGOUT");
                socket_shutdown(&imap->buf.sock);
        }
@@@ -946,35 -984,40 +948,35 @@@ static struct store *imap_open_store(st
        struct imap_store *ctx;
        struct imap *imap;
        char *arg, *rsp;
 -      int s = -1, a[2], preauth;
 -      pid_t pid;
 +      int s = -1, preauth;
  
        ctx = xcalloc(sizeof(*ctx), 1);
  
        ctx->imap = imap = xcalloc(sizeof(*imap), 1);
 -      imap->buf.sock.fd = -1;
 +      imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
        imap->in_progress_append = &imap->in_progress;
  
        /* open connection to IMAP server */
  
        if (srvc->tunnel) {
 -              imap_info("Starting tunnel '%s'... ", srvc->tunnel);
 +              const char *argv[4];
 +              struct child_process tunnel = {0};
  
 -              if (socketpair(PF_UNIX, SOCK_STREAM, 0, a)) {
 -                      perror("socketpair");
 -                      exit(1);
 -              }
 +              imap_info("Starting tunnel '%s'... ", srvc->tunnel);
  
 -              pid = fork();
 -              if (pid < 0)
 -                      _exit(127);
 -              if (!pid) {
 -                      if (dup2(a[0], 0) == -1 || dup2(a[0], 1) == -1)
 -                              _exit(127);
 -                      close(a[0]);
 -                      close(a[1]);
 -                      execl("/bin/sh", "sh", "-c", srvc->tunnel, NULL);
 -                      _exit(127);
 -              }
 +              argv[0] = "sh";
 +              argv[1] = "-c";
 +              argv[2] = srvc->tunnel;
 +              argv[3] = NULL;
  
 -              close(a[0]);
 +              tunnel.argv = argv;
 +              tunnel.in = -1;
 +              tunnel.out = -1;
 +              if (start_command(&tunnel))
 +                      die("cannot start proxy %s", argv[0]);
  
 -              imap->buf.sock.fd = a[1];
 +              imap->buf.sock.fd[0] = tunnel.out;
 +              imap->buf.sock.fd[1] = tunnel.in;
  
                imap_info("ok\n");
        } else {
                        goto bail;
                }
  
 -              imap->buf.sock.fd = s;
 +              imap->buf.sock.fd[0] = s;
 +              imap->buf.sock.fd[1] = dup(s);
  
                if (srvc->use_ssl &&
                    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
@@@ -1158,20 -1200,88 +1160,20 @@@ static int imap_make_flags(int flags, c
        return d;
  }
  
 -#define TUIDL 8
 -
 -static int imap_store_msg(struct store *gctx, struct msg_data *data, int *uid)
 +static int imap_store_msg(struct store *gctx, struct msg_data *data)
  {
        struct imap_store *ctx = (struct imap_store *)gctx;
        struct imap *imap = ctx->imap;
        struct imap_cmd_cb cb;
 -      char *fmap, *buf;
        const char *prefix, *box;
 -      int ret, i, j, d, len, extra, nocr;
 -      int start, sbreak = 0, ebreak = 0;
 -      char flagstr[128], tuid[TUIDL * 2 + 1];
 +      int ret, d;
 +      char flagstr[128];
  
        memset(&cb, 0, sizeof(cb));
  
 -      fmap = data->data;
 -      len = data->len;
 -      nocr = !data->crlf;
 -      extra = 0, i = 0;
 -      if (!CAP(UIDPLUS) && uid) {
 -      nloop:
 -              start = i;
 -              while (i < len)
 -                      if (fmap[i++] == '\n') {
 -                              extra += nocr;
 -                              if (i - 2 + nocr == start) {
 -                                      sbreak = ebreak = i - 2 + nocr;
 -                                      goto mktid;
 -                              }
 -                              if (!memcmp(fmap + start, "X-TUID: ", 8)) {
 -                                      extra -= (ebreak = i) - (sbreak = start) + nocr;
 -                                      goto mktid;
 -                              }
 -                              goto nloop;
 -                      }
 -              /* invalid message */
 -              free(fmap);
 -              return DRV_MSG_BAD;
 -      mktid:
 -              for (j = 0; j < TUIDL; j++)
 -                      sprintf(tuid + j * 2, "%02x", arc4_getbyte());
 -              extra += 8 + TUIDL * 2 + 2;
 -      }
 -      if (nocr)
 -              for (; i < len; i++)
 -                      if (fmap[i] == '\n')
 -                              extra++;
 -
 -      cb.dlen = len + extra;
 -      buf = cb.data = xmalloc(cb.dlen);
 -      i = 0;
 -      if (!CAP(UIDPLUS) && uid) {
 -              if (nocr) {
 -                      for (; i < sbreak; i++)
 -                              if (fmap[i] == '\n') {
 -                                      *buf++ = '\r';
 -                                      *buf++ = '\n';
 -                              } else
 -                                      *buf++ = fmap[i];
 -              } else {
 -                      memcpy(buf, fmap, sbreak);
 -                      buf += sbreak;
 -              }
 -              memcpy(buf, "X-TUID: ", 8);
 -              buf += 8;
 -              memcpy(buf, tuid, TUIDL * 2);
 -              buf += TUIDL * 2;
 -              *buf++ = '\r';
 -              *buf++ = '\n';
 -              i = ebreak;
 -      }
 -      if (nocr) {
 -              for (; i < len; i++)
 -                      if (fmap[i] == '\n') {
 -                              *buf++ = '\r';
 -                              *buf++ = '\n';
 -                      } else
 -                              *buf++ = fmap[i];
 -      } else
 -              memcpy(buf, fmap + i, len - i);
 -
 -      free(fmap);
 +      cb.dlen = data->len;
 +      cb.data = xmalloc(cb.dlen);
 +      memcpy(cb.data, data->data, data->len);
  
        d = 0;
        if (data->flags) {
        }
        flagstr[d] = 0;
  
 -      if (!uid) {
 -              box = gctx->conf->trash;
 -              prefix = ctx->prefix;
 -              cb.create = 1;
 -              if (ctx->trashnc)
 -                      imap->caps = imap->rcaps & ~(1 << LITERALPLUS);
 -      } else {
 -              box = gctx->name;
 -              prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
 -              cb.create = 0;
 -      }
 -      cb.ctx = uid;
 +      box = gctx->name;
 +      prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
 +      cb.create = 0;
        ret = imap_exec_m(ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr);
        imap->caps = imap->rcaps;
        if (ret != DRV_OK)
                return ret;
 -      if (!uid)
 -              ctx->trashnc = 0;
 -      else
 -              gctx->count++;
 +      gctx->count++;
  
        return DRV_OK;
  }
@@@ -1363,6 -1485,7 +1365,6 @@@ int main(int argc, char **argv
  {
        struct msg_data all_msgs, msg;
        struct store *ctx = NULL;
 -      int uid = 0;
        int ofs = 0;
        int r;
        int total, n = 0;
  
        git_extract_argv0_path(argv[0]);
  
 -      /* init the random number generator */
 -      arc4_init();
 -
+       if (argc != 1)
+               usage(imap_send_usage);
        setup_git_directory_gently(&nongit_ok);
        git_config(git_imap_config, NULL);
  
                        break;
                if (server.use_html)
                        wrap_in_html(&msg);
 -              r = imap_store_msg(ctx, &msg, &uid);
 +              r = imap_store_msg(ctx, &msg);
                if (r != DRV_OK)
                        break;
                n++;