Merge branch 'jk/common-main'
authorJunio C Hamano <gitster@pobox.com>
Tue, 19 Jul 2016 20:22:19 +0000 (13:22 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 19 Jul 2016 20:22:19 +0000 (13:22 -0700)
There are certain house-keeping tasks that need to be performed at
the very beginning of any Git program, and programs that are not
built-in commands had to do them exactly the same way as "git"
potty does. It was easy to make mistakes in one-off standalone
programs (like test helpers). A common "main()" function that
calls cmd_main() of individual program has been introduced to
make it harder to make mistakes.

* jk/common-main:
mingw: declare main()'s argv as const
common-main: call git_setup_gettext()
common-main: call restore_sigpipe_to_default()
common-main: call sanitize_stdfds()
common-main: call git_extract_argv0_path()
add an extra level of indirection to main()

12 files changed:
1  2 
Makefile
compat/mingw.h
daemon.c
fast-import.c
git-compat-util.h
imap-send.c
t/helper/test-config.c
t/helper/test-date.c
t/helper/test-parse-options.c
t/helper/test-regex.c
t/helper/test-submodule-config.c
upload-pack.c
diff --combined Makefile
index 3cb1d601a3d273c3a5588ede35338cc5a7323479,717dc344ab131a5c8ee2d048bae60fa9336f69d0..c1e88dfcdd53f46ea742212f15863e63ef5e1d65
+++ b/Makefile
@@@ -939,7 -939,7 +939,7 @@@ BUILTIN_OBJS += builtin/verify-tag.
  BUILTIN_OBJS += builtin/worktree.o
  BUILTIN_OBJS += builtin/write-tree.o
  
- GITLIBS = $(LIB_FILE) $(XDIFF_LIB)
+ GITLIBS = common-main.o $(LIB_FILE) $(XDIFF_LIB)
  EXTLIBS =
  
  GIT_USER_AGENT = git/$(GIT_VERSION)
@@@ -1572,7 -1572,15 +1572,15 @@@ TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_
  DIFF_SQ = $(subst ','\'',$(DIFF))
  PERLLIB_EXTRA_SQ = $(subst ','\'',$(PERLLIB_EXTRA))
  
- LIBS = $(GITLIBS) $(EXTLIBS)
+ # We must filter out any object files from $(GITLIBS),
+ # as it is typically used like:
+ #
+ #   foo: foo.o $(GITLIBS)
+ #     $(CC) $(filter %.o,$^) $(LIBS)
+ #
+ # where we use it as a dependency. Since we also pull object files
+ # from the dependency list, that would make each entry appear twice.
+ LIBS = $(filter-out %.o, $(GITLIBS)) $(EXTLIBS)
  
  BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \
        $(COMPAT_CFLAGS)
@@@ -1708,8 -1716,8 +1716,8 @@@ git.sp git.s git.o: EXTRA_CPPFLAGS = 
        '-DGIT_INFO_PATH="$(infodir_relative_SQ)"'
  
  git$X: git.o GIT-LDFLAGS $(BUILTIN_OBJS) $(GITLIBS)
-       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) git.o \
-               $(BUILTIN_OBJS) $(LIBS)
+       $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) \
+               $(filter %.o,$^) $(LIBS)
  
  help.sp help.s help.o: common-cmds.h
  
@@@ -1902,6 -1910,7 +1910,7 @@@ TEST_OBJS := $(patsubst %$X,%.o,$(TEST_
  OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
        $(XDIFF_OBJS) \
        $(VCSSVN_OBJS) \
+       common-main.o \
        git.o
  ifndef NO_CURL
        OBJECTS += http.o http-walker.o remote-curl.o
@@@ -2063,10 -2072,7 +2072,10 @@@ XGETTEXT_FLAGS_SH = $(XGETTEXT_FLAGS) -
        --keyword=gettextln --keyword=eval_gettextln
  XGETTEXT_FLAGS_PERL = $(XGETTEXT_FLAGS) --keyword=__ --language=Perl
  LOCALIZED_C = $(C_OBJ:o=c) $(LIB_H) $(GENERATED_H)
 -LOCALIZED_SH = $(SCRIPT_SH) git-parse-remote.sh
 +LOCALIZED_SH = $(SCRIPT_SH)
 +LOCALIZED_SH += git-parse-remote.sh
 +LOCALIZED_SH += git-rebase--interactive.sh
 +LOCALIZED_SH += git-sh-setup.sh
  LOCALIZED_PERL = $(SCRIPT_PERL)
  
  ifdef XGETTEXT_INCLUDE_TESTS
diff --combined compat/mingw.h
index 9a8803b876a1ed38aca34e83c3859e607bdd17e7,1ac9086a8275341a1a9a66338cc196d6782e7371..233933ee86b2d069c7fca5b0dc39ea6c6badb20b
@@@ -532,10 -532,10 +532,10 @@@ extern CRITICAL_SECTION pinfo_cs
   * A replacement of main() that adds win32 specific initialization.
   */
  
 -void mingw_startup();
 -#define main(c,v) dummy_decl_mingw_main(); \
 +void mingw_startup(void);
 +#define main(c,v) dummy_decl_mingw_main(void); \
  static int mingw_main(c,v); \
- int main(int argc, char **argv) \
+ int main(int argc, const char **argv) \
  { \
        mingw_startup(); \
        return mingw_main(__argc, (void *)__argv); \
diff --combined daemon.c
index 46dddaca5a05a4d50da21c828085168e3ebe8b86,569997c98fcb3a4e2dea9eae698a8cbb839d89b2..e647254c196f8270691cd369b51ac5132add1259
+++ b/daemon.c
@@@ -1,6 -1,5 +1,5 @@@
  #include "cache.h"
  #include "pkt-line.h"
- #include "exec_cmd.h"
  #include "run-command.h"
  #include "strbuf.h"
  #include "string-list.h"
@@@ -32,7 -31,7 +31,7 @@@ static const char daemon_usage[] 
  "           [<directory>...]";
  
  /* List of acceptable pathname prefixes */
- static char **ok_paths;
+ static const char **ok_paths;
  static int strict_paths;
  
  /* If this is set, git-daemon-export-ok is not required */
@@@ -240,7 -239,7 +239,7 @@@ static const char *path_ok(const char *
        }
  
        if ( ok_paths && *ok_paths ) {
-               char **pp;
+               const char **pp;
                int pathlen = strlen(path);
  
                /* The validation is done on the paths after enter_repo
@@@ -669,15 -668,6 +668,15 @@@ static void hostinfo_clear(struct hosti
        strbuf_release(&hi->tcp_port);
  }
  
 +static void set_keep_alive(int sockfd)
 +{
 +      int ka = 1;
 +
 +      if (setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &ka, sizeof(ka)) < 0)
 +              logerror("unable to set SO_KEEPALIVE on socket: %s",
 +                      strerror(errno));
 +}
 +
  static int execute(void)
  {
        char *line = packet_buffer;
        if (addr)
                loginfo("Connection from %s:%s", addr, port);
  
 +      set_keep_alive(0);
        alarm(init_timeout ? init_timeout : timeout);
        pktlen = packet_read(0, NULL, NULL, packet_buffer, sizeof(packet_buffer), 0);
        alarm(0);
@@@ -961,8 -950,6 +960,8 @@@ static int setup_named_sock(char *liste
                        continue;
                }
  
 +              set_keep_alive(sockfd);
 +
                if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
                        logerror("Could not bind to %s: %s",
                                 ip2str(ai->ai_family, ai->ai_addr, ai->ai_addrlen),
@@@ -1022,8 -1009,6 +1021,8 @@@ static int setup_named_sock(char *liste
                return 0;
        }
  
 +      set_keep_alive(sockfd);
 +
        if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
                logerror("Could not bind to %s: %s",
                         ip2str(AF_INET, (struct sockaddr *)&sin, sizeof(sin)),
@@@ -1192,7 -1177,7 +1191,7 @@@ static int serve(struct string_list *li
        return service_loop(&socklist);
  }
  
- int main(int argc, char **argv)
+ int cmd_main(int argc, const char **argv)
  {
        int listen_port = 0;
        struct string_list listen_addr = STRING_LIST_INIT_NODUP;
        struct credentials *cred = NULL;
        int i;
  
-       git_setup_gettext();
-       git_extract_argv0_path(argv[0]);
        for (i = 1; i < argc; i++) {
-               char *arg = argv[i];
+               const char *arg = argv[i];
                const char *v;
  
                if (skip_prefix(arg, "--listen=", &v)) {
        if (detach) {
                if (daemonize())
                        die("--detach not supported on this platform");
-       } else
-               sanitize_stdfds();
+       }
  
        if (pid_file)
                write_file(pid_file, "%"PRIuMAX, (uintmax_t) getpid());
diff --combined fast-import.c
index 59630cee1488bda274bd4f4bd8bf2748d9ab081a,84a13756cc6387d7546ebda8d3c92a6c49a53cf8..bf53ac95da04327aa2b83ff2943d51e6d0c731db
@@@ -164,9 -164,7 +164,8 @@@ Format of STDIN stream
  #include "refs.h"
  #include "csum-file.h"
  #include "quote.h"
- #include "exec_cmd.h"
  #include "dir.h"
 +#include "run-command.h"
  
  #define PACK_ID_BITS 16
  #define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
@@@ -283,7 -281,6 +282,7 @@@ struct recent_command 
  /* Configured limits on output */
  static unsigned long max_depth = 10;
  static off_t max_packsize;
 +static int unpack_limit = 100;
  static int force_update;
  static int pack_compression_level = Z_DEFAULT_COMPRESSION;
  static int pack_compression_seen;
@@@ -302,7 -299,7 +301,7 @@@ static int failure
  static FILE *pack_edges;
  static unsigned int show_stats = 1;
  static int global_argc;
- static char **global_argv;
+ static const char **global_argv;
  
  /* Memory pools */
  static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool);
@@@ -598,33 -595,6 +597,33 @@@ static struct object_entry *insert_obje
        return e;
  }
  
 +static void invalidate_pack_id(unsigned int id)
 +{
 +      unsigned int h;
 +      unsigned long lu;
 +      struct tag *t;
 +
 +      for (h = 0; h < ARRAY_SIZE(object_table); h++) {
 +              struct object_entry *e;
 +
 +              for (e = object_table[h]; e; e = e->next)
 +                      if (e->pack_id == id)
 +                              e->pack_id = MAX_PACK_ID;
 +      }
 +
 +      for (lu = 0; lu < branch_table_sz; lu++) {
 +              struct branch *b;
 +
 +              for (b = branch_table[lu]; b; b = b->table_next_branch)
 +                      if (b->pack_id == id)
 +                              b->pack_id = MAX_PACK_ID;
 +      }
 +
 +      for (t = first_tag; t; t = t->next_tag)
 +              if (t->pack_id == id)
 +                      t->pack_id = MAX_PACK_ID;
 +}
 +
  static unsigned int hc_str(const char *s, size_t len)
  {
        unsigned int r = 0;
@@@ -980,23 -950,6 +979,23 @@@ static void unkeep_all_packs(void
        }
  }
  
 +static int loosen_small_pack(const struct packed_git *p)
 +{
 +      struct child_process unpack = CHILD_PROCESS_INIT;
 +
 +      if (lseek(p->pack_fd, 0, SEEK_SET) < 0)
 +              die_errno("Failed seeking to start of '%s'", p->pack_name);
 +
 +      unpack.in = p->pack_fd;
 +      unpack.git_cmd = 1;
 +      unpack.stdout_to_stderr = 1;
 +      argv_array_push(&unpack.args, "unpack-objects");
 +      if (!show_stats)
 +              argv_array_push(&unpack.args, "-q");
 +
 +      return run_command(&unpack);
 +}
 +
  static void end_packfile(void)
  {
        static int running;
                fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
                                    pack_data->pack_name, object_count,
                                    cur_pack_sha1, pack_size);
 +
 +              if (object_count <= unpack_limit) {
 +                      if (!loosen_small_pack(pack_data)) {
 +                              invalidate_pack_id(pack_id);
 +                              goto discard_pack;
 +                      }
 +              }
 +
                close(pack_data->pack_fd);
                idx_name = keep_pack(create_index());
  
                pack_id++;
        }
        else {
 +discard_pack:
                close(pack_data->pack_fd);
                unlink_or_warn(pack_data->pack_name);
        }
@@@ -3375,7 -3319,6 +3374,7 @@@ static void parse_option(const char *op
  static void git_pack_config(void)
  {
        int indexversion_value;
 +      int limit;
        unsigned long packsizelimit_value;
  
        if (!git_config_get_ulong("pack.depth", &max_depth)) {
        if (!git_config_get_ulong("pack.packsizelimit", &packsizelimit_value))
                max_packsize = packsizelimit_value;
  
 +      if (!git_config_get_int("fastimport.unpacklimit", &limit))
 +              unpack_limit = limit;
 +      else if (!git_config_get_int("transfer.unpacklimit", &limit))
 +              unpack_limit = limit;
 +
        git_config(git_default_config, NULL);
  }
  
@@@ -3445,14 -3383,10 +3444,10 @@@ static void parse_argv(void
                read_marks();
  }
  
- int main(int argc, char **argv)
+ int cmd_main(int argc, const char **argv)
  {
        unsigned int i;
  
-       git_extract_argv0_path(argv[0]);
-       git_setup_gettext();
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage(fast_import_usage);
  
diff --combined git-compat-util.h
index c99cddc54b1ea14955e019dc84088b6c9fd74f88,1930444ef092f2b5834d12b73b4d2d0c050f3551..590bfddf73238d37f7b2d831a6e46ed608c005ce
@@@ -473,23 -473,6 +473,23 @@@ static inline int skip_prefix(const cha
        return 0;
  }
  
 +/*
 + * Like skip_prefix, but promises never to read past "len" bytes of the input
 + * buffer, and returns the remaining number of bytes in "out" via "outlen".
 + */
 +static inline int skip_prefix_mem(const char *buf, size_t len,
 +                                const char *prefix,
 +                                const char **out, size_t *outlen)
 +{
 +      size_t prefix_len = strlen(prefix);
 +      if (prefix_len <= len && !memcmp(buf, prefix, prefix_len)) {
 +              *out = buf + prefix_len;
 +              *outlen = len - prefix_len;
 +              return 1;
 +      }
 +      return 0;
 +}
 +
  /*
   * If buf ends with suffix, return 1 and subtract the length of the suffix
   * from *len. Otherwise, return 0 and leave *len untouched.
@@@ -1062,3 -1045,5 +1062,5 @@@ struct tm *git_gmtime_r(const time_t *
  #endif
  
  #endif
+ extern int cmd_main(int, const char **);
diff --combined imap-send.c
index 50377c5b88d59a3563b49e669b93f17009bcdd19,9cbe27fcd44d81465ac20bf99cc6efadb36f4097..db0fafee995874824f91cb8378399fcf720d705c
@@@ -1443,7 -1443,6 +1443,7 @@@ static CURL *setup_curl(struct imap_ser
  
        if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
                curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
 +      setup_curl_trace(curl);
  
        return curl;
  }
@@@ -1495,16 -1494,12 +1495,12 @@@ static int curl_append_msgs_to_imap(str
  }
  #endif
  
- int main(int argc, char **argv)
+ int cmd_main(int argc, const char **argv)
  {
        struct strbuf all_msgs = STRBUF_INIT;
        int total;
        int nongit_ok;
  
-       git_extract_argv0_path(argv[0]);
-       git_setup_gettext();
        setup_git_directory_gently(&nongit_ok);
        git_imap_config();
  
diff --combined t/helper/test-config.c
index 509aeef400e6495ce7ca428d7df5cd63f3aa1247,d143cd72223dec9c947fbf84d0f72945745a09c6..3c6d08cd095152cab9abeb038c1b4f82440c55cd
@@@ -25,9 -25,6 +25,9 @@@
   *                            ascending order of priority from a config_set
   *                            constructed from files entered as arguments.
   *
 + * iterate -> iterate over all values using git_config(), and print some
 + *            data for each
 + *
   * Examples:
   *
   * To print the value with highest priority for key "foo.bAr Baz.rock":
   *
   */
  
 +static const char *scope_name(enum config_scope scope)
 +{
 +      switch (scope) {
 +      case CONFIG_SCOPE_SYSTEM:
 +              return "system";
 +      case CONFIG_SCOPE_GLOBAL:
 +              return "global";
 +      case CONFIG_SCOPE_REPO:
 +              return "repo";
 +      case CONFIG_SCOPE_CMDLINE:
 +              return "cmdline";
 +      default:
 +              return "unknown";
 +      }
 +}
 +static int iterate_cb(const char *var, const char *value, void *data)
 +{
 +      static int nr;
 +
 +      if (nr++)
 +              putchar('\n');
 +
 +      printf("key=%s\n", var);
 +      printf("value=%s\n", value ? value : "(null)");
 +      printf("origin=%s\n", current_config_origin_type());
 +      printf("name=%s\n", current_config_name());
 +      printf("scope=%s\n", scope_name(current_config_scope()));
 +
 +      return 0;
 +}
  
- int main(int argc, char **argv)
+ int cmd_main(int argc, const char **argv)
  {
        int i, val;
        const char *v;
                        printf("Value not found for \"%s\"\n", argv[2]);
                        goto exit1;
                }
 +      } else if (!strcmp(argv[1], "iterate")) {
 +              git_config(iterate_cb, NULL);
 +              goto exit0;
        }
  
        die("%s: Please check the syntax and the function name", argv[0]);
diff --combined t/helper/test-date.c
index d9ab3609094df80a1e3afc3d24d8fd158c96e496,0f3cfb1721b641b25267458edce93c9de2f923e2..506054bcd5dfbd76c8aec85382f35794514b9db9
@@@ -1,12 -1,11 +1,12 @@@
  #include "cache.h"
  
  static const char *usage_msg = "\n"
 -"  test-date show [time_t]...\n"
 +"  test-date relative [time_t]...\n"
 +"  test-date show:<format> [time_t]...\n"
  "  test-date parse [date]...\n"
  "  test-date approxidate [date]...\n";
  
- static void show_relative_dates(char **argv, struct timeval *now)
 -static void show_dates(const char **argv, struct timeval *now)
++static void show_relative_dates(const char **argv, struct timeval *now)
  {
        struct strbuf buf = STRBUF_INIT;
  
        strbuf_release(&buf);
  }
  
- static void show_dates(char **argv, const char *format)
++static void show_dates(const char **argv, const char *format)
 +{
 +      struct date_mode mode;
 +
 +      parse_date_format(format, &mode);
 +      for (; *argv; argv++) {
-               char *arg = *argv;
++              char *arg;
 +              time_t t;
 +              int tz;
 +
 +              /*
 +               * Do not use our normal timestamp parsing here, as the point
 +               * is to test the formatting code in isolation.
 +               */
-               t = strtol(arg, &arg, 10);
++              t = strtol(*argv, &arg, 10);
 +              while (*arg == ' ')
 +                      arg++;
 +              tz = atoi(arg);
 +
 +              printf("%s -> %s\n", *argv, show_date(t, tz, &mode));
 +      }
 +}
 +
- static void parse_dates(char **argv, struct timeval *now)
+ static void parse_dates(const char **argv, struct timeval *now)
  {
        struct strbuf result = STRBUF_INIT;
  
@@@ -60,7 -36,7 +60,7 @@@
        strbuf_release(&result);
  }
  
- static void parse_approxidate(char **argv, struct timeval *now)
+ static void parse_approxidate(const char **argv, struct timeval *now)
  {
        for (; *argv; argv++) {
                time_t t;
@@@ -69,7 -45,7 +69,7 @@@
        }
  }
  
- int main(int argc, char **argv)
+ int cmd_main(int argc, const char **argv)
  {
        struct timeval now;
        const char *x;
        argv++;
        if (!*argv)
                usage(usage_msg);
 -      if (!strcmp(*argv, "show"))
 -              show_dates(argv+1, &now);
 +      if (!strcmp(*argv, "relative"))
 +              show_relative_dates(argv+1, &now);
 +      else if (skip_prefix(*argv, "show:", &x))
 +              show_dates(argv+1, x);
        else if (!strcmp(*argv, "parse"))
                parse_dates(argv+1, &now);
        else if (!strcmp(*argv, "approxidate"))
index 2c63298fab10563bcd8c98c3d0b6d563164e2283,d51d29251eb2cf5adeba1d4f94ff2ce100421560..a01430c24bdb8c0e35147b71d6f9e898405b1c85
@@@ -12,7 -12,7 +12,7 @@@ static int dry_run = 0, quiet = 0
  static char *string = NULL;
  static char *file = NULL;
  static int ambiguous;
 -static struct string_list list;
 +static struct string_list list = STRING_LIST_INIT_NODUP;
  
  static struct {
        int called;
@@@ -94,7 -94,7 +94,7 @@@ static void show(struct string_list *ex
        strbuf_release(&buf);
  }
  
- int main(int argc, char **argv)
+ int cmd_main(int argc, const char **argv)
  {
        const char *prefix = "prefix/";
        const char *usage[] = {
diff --combined t/helper/test-regex.c
index eff26f534fc21e3d77bc3d6cf76fce2b5a74e981,37b7f06e552ef51c852f54e70d66c434ca50b6b2..b5ea8a97c54e1737d91dec894c1cc02e1baf64e5
@@@ -1,23 -1,6 +1,23 @@@
  #include "git-compat-util.h"
 +#include "gettext.h"
  
 -int cmd_main(int argc, const char **argv)
 +struct reg_flag {
 +      const char *name;
 +      int flag;
 +};
 +
 +static struct reg_flag reg_flags[] = {
 +      { "EXTENDED",    REG_EXTENDED   },
 +      { "NEWLINE",     REG_NEWLINE    },
 +      { "ICASE",       REG_ICASE      },
 +      { "NOTBOL",      REG_NOTBOL     },
 +#ifdef REG_STARTEND
 +      { "STARTEND",    REG_STARTEND   },
 +#endif
 +      { NULL, 0 }
 +};
 +
 +static int test_regex_bug(void)
  {
        char *pat = "[^={} \t]+";
        char *str = "={}\nfred";
        if (m[0].rm_so == 3) /* matches '\n' when it should not */
                die("regex bug confirmed: re-build git with NO_REGEX=1");
  
 -      exit(0);
 +      return 0;
 +}
 +
- int main(int argc, char **argv)
++int cmd_main(int argc, const char **argv)
 +{
 +      const char *pat;
 +      const char *str;
 +      int flags = 0;
 +      regex_t r;
 +      regmatch_t m[1];
 +
 +      if (argc == 2 && !strcmp(argv[1], "--bug"))
 +              return test_regex_bug();
 +      else if (argc < 3)
 +              usage("test-regex --bug\n"
 +                    "test-regex <pattern> <string> [<options>]");
 +
 +      argv++;
 +      pat = *argv++;
 +      str = *argv++;
 +      while (*argv) {
 +              struct reg_flag *rf;
 +              for (rf = reg_flags; rf->name; rf++)
 +                      if (!strcmp(*argv, rf->name)) {
 +                              flags |= rf->flag;
 +                              break;
 +                      }
 +              if (!rf->name)
 +                      die("do not recognize %s", *argv);
 +              argv++;
 +      }
 +      git_setup_gettext();
 +
 +      if (regcomp(&r, pat, flags))
 +              die("failed regcomp() for pattern '%s'", pat);
 +      if (regexec(&r, str, 1, m, 0))
 +              return 1;
 +
 +      return 0;
  }
index 7922ffba1de169f1b2acd2cf9d75c4449275b990,2cffded116f77d7c62ee718fa08558b52574a205..61049b87a07c638a9211b11484c503ffc56a1f9e
@@@ -2,7 -2,7 +2,7 @@@
  #include "submodule-config.h"
  #include "submodule.h"
  
- static void die_usage(int argc, char **argv, const char *msg)
+ static void die_usage(int argc, const char **argv, const char *msg)
  {
        fprintf(stderr, "%s\n", msg);
        fprintf(stderr, "Usage: %s [<commit> <submodulepath>] ...\n", argv[0]);
@@@ -14,9 -14,9 +14,9 @@@ static int git_test_config(const char *
        return parse_submodule_config_option(var, value);
  }
  
- int main(int argc, char **argv)
+ int cmd_main(int argc, const char **argv)
  {
-       char **arg = argv;
+       const char **arg = argv;
        int my_argc = argc;
        int output_url = 0;
        int lookup_name = 0;
@@@ -49,7 -49,7 +49,7 @@@
                path_or_name = arg[1];
  
                if (commit[0] == '\0')
 -                      hashcpy(commit_sha1, null_sha1);
 +                      hashclr(commit_sha1);
                else if (get_sha1(commit, commit_sha1) < 0)
                        die_usage(argc, argv, "Commit not found.");
  
diff --combined upload-pack.c
index f93787003a21b465c33ab8302347d80e46c10428,b97a7659d1f2ffeb8e63fa46a17ca1501db983ab..d4cc414bc218d193225d7b7891638b3798173254
  #include "sigchain.h"
  #include "version.h"
  #include "string-list.h"
 +#include "parse-options.h"
  
 -static const char upload_pack_usage[] = "git upload-pack [--strict] [--timeout=<n>] <dir>";
 +static const char * const upload_pack_usage[] = {
 +      N_("git upload-pack [<options>] <dir>"),
 +      NULL
 +};
  
  /* Remember to update object flag allocation in object.h */
  #define THEY_HAVE     (1u << 11)
@@@ -56,28 -52,26 +56,28 @@@ static int keepalive = 5
  static int use_sideband;
  static int advertise_refs;
  static int stateless_rpc;
 +static const char *pack_objects_hook;
  
  static void reset_timeout(void)
  {
        alarm(timeout);
  }
  
 -static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
 +static void send_client_data(int fd, const char *data, ssize_t sz)
  {
 -      if (use_sideband)
 -              return send_sideband(1, fd, data, sz, use_sideband);
 +      if (use_sideband) {
 +              send_sideband(1, fd, data, sz, use_sideband);
 +              return;
 +      }
        if (fd == 3)
                /* emergency quit */
                fd = 2;
        if (fd == 2) {
                /* XXX: are we happy to lose stuff here? */
                xwrite(fd, data, sz);
 -              return sz;
 +              return;
        }
        write_or_die(fd, data, sz);
 -      return sz;
  }
  
  static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
@@@ -99,14 -93,6 +99,14 @@@ static void create_pack_file(void
        int i;
        FILE *pipe_fd;
  
 +      if (!pack_objects_hook)
 +              pack_objects.git_cmd = 1;
 +      else {
 +              argv_array_push(&pack_objects.args, pack_objects_hook);
 +              argv_array_push(&pack_objects.args, "git");
 +              pack_objects.use_shell = 1;
 +      }
 +
        if (shallow_nr) {
                argv_array_push(&pack_objects.args, "--shallow-file");
                argv_array_push(&pack_objects.args, "");
        pack_objects.in = -1;
        pack_objects.out = -1;
        pack_objects.err = -1;
 -      pack_objects.git_cmd = 1;
  
        if (start_command(&pack_objects))
                die("git upload-pack: unable to fork git-pack-objects");
                        }
                        else
                                buffered = -1;
 -                      sz = send_client_data(1, data, sz);
 -                      if (sz < 0)
 -                              goto fail;
 +                      send_client_data(1, data, sz);
                }
  
                /*
        /* flush the data */
        if (0 <= buffered) {
                data[0] = buffered;
 -              sz = send_client_data(1, data, 1);
 -              if (sz < 0)
 -                      goto fail;
 +              send_client_data(1, data, 1);
                fprintf(stderr, "flushed.\n");
        }
        if (use_sideband)
@@@ -821,46 -812,53 +821,43 @@@ static int upload_pack_config(const cha
                keepalive = git_config_int(var, value);
                if (!keepalive)
                        keepalive = -1;
 +      } else if (current_config_scope() != CONFIG_SCOPE_REPO) {
 +              if (!strcmp("uploadpack.packobjectshook", var))
 +                      return git_config_string(&pack_objects_hook, var, value);
        }
        return parse_hide_refs_config(var, value, "uploadpack");
  }
  
- int main(int argc, const char **argv)
+ int cmd_main(int argc, const char **argv)
  {
        const char *dir;
 -      int i;
        int strict = 0;
 +      struct option options[] = {
 +              OPT_BOOL(0, "stateless-rpc", &stateless_rpc,
 +                       N_("quit after a single request/response exchange")),
 +              OPT_BOOL(0, "advertise-refs", &advertise_refs,
 +                       N_("exit immediately after intial ref advertisement")),
 +              OPT_BOOL(0, "strict", &strict,
 +                       N_("do not try <directory>/.git/ if <directory> is no Git directory")),
 +              OPT_INTEGER(0, "timeout", &timeout,
 +                          N_("interrupt transfer after <n> seconds of inactivity")),
 +              OPT_END()
 +      };
  
-       git_setup_gettext();
        packet_trace_identity("upload-pack");
-       git_extract_argv0_path(argv[0]);
        check_replace_refs = 0;
  
 -      for (i = 1; i < argc; i++) {
 -              const char *arg = argv[i];
 +      argc = parse_options(argc, argv, NULL, options, upload_pack_usage, 0);
  
 -              if (arg[0] != '-')
 -                      break;
 -              if (!strcmp(arg, "--advertise-refs")) {
 -                      advertise_refs = 1;
 -                      continue;
 -              }
 -              if (!strcmp(arg, "--stateless-rpc")) {
 -                      stateless_rpc = 1;
 -                      continue;
 -              }
 -              if (!strcmp(arg, "--strict")) {
 -                      strict = 1;
 -                      continue;
 -              }
 -              if (starts_with(arg, "--timeout=")) {
 -                      timeout = atoi(arg+10);
 -                      daemon_mode = 1;
 -                      continue;
 -              }
 -              if (!strcmp(arg, "--")) {
 -                      i++;
 -                      break;
 -              }
 -      }
 +      if (argc != 1)
 +              usage_with_options(upload_pack_usage, options);
  
 -      if (i != argc-1)
 -              usage(upload_pack_usage);
 +      if (timeout)
 +              daemon_mode = 1;
  
        setup_path();
  
 -      dir = argv[i];
 +      dir = argv[0];
  
        if (!enter_repo(dir, strict))
                die("'%s' does not appear to be a git repository", dir);