Merge branch 'maint'
authorJunio C Hamano <gitster@pobox.com>
Fri, 9 Nov 2007 08:21:44 +0000 (00:21 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 9 Nov 2007 08:21:44 +0000 (00:21 -0800)
* maint:
Start preparing for 1.5.3.6
git-send-email: Change the prompt for the subject of the initial message.
SubmittingPatches: improve the 'Patch:' section of the checklist
instaweb: Minor cleanups and fixes for potential problems
stop t1400 hiding errors in tests
Makefile: add missing dependency on wt-status.h
refresh_index_quietly(): express "optional" nature of index writing better
Fix sed string regex escaping in module_name.
Avoid a few unportable, needlessly nested "...`...".
git-mailsplit: with maildirs not only process cur/, but also new/

Signed-off-by: Junio C Hamano <gitster@pobox.com>
1  2 
Makefile
builtin-mailsplit.c
git-instaweb.sh
git-rebase--interactive.sh
git-send-email.perl
git-submodule.sh
diff --combined Makefile
index 0d5590f6cfdd3d6c567eff9d38322071fbba4b9e,2331e45adfb09c7327d5868f536bc5d413d8af95..621270f6235306dcdd9c55ac371c90cb5b5effc3
+++ b/Makefile
@@@ -28,8 -28,6 +28,8 @@@ all:
  #
  # Define NO_STRCASESTR if you don't have strcasestr.
  #
 +# Define NO_MEMMEM if you don't have memmem.
 +#
  # Define NO_STRLCPY if you don't have strlcpy.
  #
  # Define NO_STRTOUMAX if you don't have strtoumax in the C library.
@@@ -38,8 -36,6 +38,8 @@@
  #
  # Define NO_SETENV if you don't have setenv in the C library.
  #
 +# Define NO_MKDTEMP if you don't have mkdtemp in the C library.
 +#
  # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
  # Enable it on Windows.  By default, symrefs are still used.
  #
@@@ -98,8 -94,6 +98,8 @@@
  # Define OLD_ICONV if your library has an old iconv(), where the second
  # (input buffer pointer) parameter is declared with type (const char **).
  #
 +# Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.
 +#
  # Define NO_R_TO_GCC_LINKER if your gcc does not like "-R/path/lib"
  # that tells runtime paths to dynamic libraries;
  # "-Wl,-rpath=/path/lib" is used instead.
  # If not set it defaults to the bare 'wish'. If it is set to the empty
  # string then NO_TCLTK will be forced (this is used by configure script).
  #
 +# Define THREADED_DELTA_SEARCH if you have pthreads and wish to exploit
 +# parallel delta searching when packing objects.
 +#
  
  GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@@ -169,7 -160,6 +169,7 @@@ GITWEB_CONFIG = gitweb_config.per
  GITWEB_HOME_LINK_STR = projects
  GITWEB_SITENAME =
  GITWEB_PROJECTROOT = /pub/git
 +GITWEB_PROJECT_MAXDEPTH = 2007
  GITWEB_EXPORT_OK =
  GITWEB_STRICT_EXPORT =
  GITWEB_BASE_URL =
@@@ -212,10 -202,11 +212,10 @@@ BASIC_LDFLAGS 
  SCRIPT_SH = \
        git-bisect.sh git-checkout.sh \
        git-clean.sh git-clone.sh git-commit.sh \
 -      git-fetch.sh \
        git-ls-remote.sh \
        git-merge-one-file.sh git-mergetool.sh git-parse-remote.sh \
        git-pull.sh git-rebase.sh git-rebase--interactive.sh \
 -      git-repack.sh git-request-pull.sh git-reset.sh \
 +      git-repack.sh git-request-pull.sh \
        git-sh-setup.sh \
        git-am.sh \
        git-merge.sh git-merge-stupid.sh git-merge-octopus.sh \
  SCRIPT_PERL = \
        git-add--interactive.perl \
        git-archimport.perl git-cvsimport.perl git-relink.perl \
 -      git-cvsserver.perl git-remote.perl \
 -      git-svnimport.perl git-cvsexportcommit.perl \
 +      git-cvsserver.perl git-remote.perl git-cvsexportcommit.perl \
        git-send-email.perl git-svn.perl
  
  SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
  
  # ... and all the rest that could be moved out of bindir to gitexecdir
  PROGRAMS = \
 -      git-convert-objects$X git-fetch-pack$X \
 -      git-hash-object$X git-index-pack$X git-local-fetch$X \
 +      git-fetch-pack$X \
 +      git-hash-object$X git-index-pack$X \
        git-fast-import$X \
        git-daemon$X \
        git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
        git-peek-remote$X git-receive-pack$X \
        git-send-pack$X git-shell$X \
 -      git-show-index$X git-ssh-fetch$X \
 -      git-ssh-upload$X git-unpack-file$X \
 +      git-show-index$X \
 +      git-unpack-file$X \
        git-update-server-info$X \
        git-upload-pack$X \
        git-pack-redundant$X git-var$X \
@@@ -272,6 -264,9 +272,6 @@@ ifndef NO_TCLT
  OTHER_PROGRAMS += gitk-wish
  endif
  
 -# Backward compatibility -- to be removed after 1.0
 -PROGRAMS += git-ssh-pull$X git-ssh-push$X
 -
  # Set paths to tools early so that they can be used for version tests.
  ifndef SHELL_PATH
        SHELL_PATH = /bin/sh
@@@ -291,7 -286,7 +291,7 @@@ LIB_H = 
        run-command.h strbuf.h tag.h tree.h git-compat-util.h revision.h \
        tree-walk.h log-tree.h dir.h path-list.h unpack-trees.h builtin.h \
        utf8.h reflog-walk.h patch-ids.h attr.h decorate.h progress.h \
 -      mailmap.h remote.h
 +      mailmap.h remote.h parse-options.h transport.h diffcore.h hash.h
  
  DIFF_OBJS = \
        diff.o diff-lib.o diffcore-break.o diffcore-order.o \
  LIB_OBJS = \
        blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
        date.o diff-delta.o entry.o exec_cmd.o ident.o \
 -      interpolate.o \
 +      pretty.o interpolate.o hash.o \
        lockfile.o \
        patch-ids.o \
        object.o pack-check.o pack-write.o patch-delta.o path.o pkt-line.o \
        write_or_die.o trace.o list-objects.o grep.o match-trees.o \
        alloc.o merge-file.o path-list.o help.o unpack-trees.o $(DIFF_OBJS) \
        color.o wt-status.o archive-zip.o archive-tar.o shallow.o utf8.o \
 -      convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o
 +      convert.o attr.o decorate.o progress.o mailmap.o symlinks.o remote.o \
 +      transport.o bundle.o walker.o parse-options.o
  
  BUILTIN_OBJS = \
        builtin-add.o \
        builtin-diff-files.o \
        builtin-diff-index.o \
        builtin-diff-tree.o \
 +      builtin-fetch.o \
 +      builtin-fetch-pack.o \
        builtin-fetch--tool.o \
        builtin-fmt-merge-msg.o \
        builtin-for-each-ref.o \
        builtin-reflog.o \
        builtin-config.o \
        builtin-rerere.o \
 +      builtin-reset.o \
        builtin-rev-list.o \
        builtin-rev-parse.o \
        builtin-revert.o \
@@@ -405,27 -396,23 +405,27 @@@ ifeq ($(uname_S),Darwin
        NEEDS_LIBICONV = YesPlease
        OLD_ICONV = UnfortunatelyYes
        NO_STRLCPY = YesPlease
 +      NO_MEMMEM = YesPlease
  endif
  ifeq ($(uname_S),SunOS)
        NEEDS_SOCKET = YesPlease
        NEEDS_NSL = YesPlease
        SHELL_PATH = /bin/bash
        NO_STRCASESTR = YesPlease
 +      NO_MEMMEM = YesPlease
        NO_HSTRERROR = YesPlease
        ifeq ($(uname_R),5.8)
                NEEDS_LIBICONV = YesPlease
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
 +              NO_MKDTEMP = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
        endif
        ifeq ($(uname_R),5.9)
                NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
 +              NO_MKDTEMP = YesPlease
                NO_C99_FORMAT = YesPlease
                NO_STRTOUMAX = YesPlease
        endif
@@@ -437,7 -424,6 +437,7 @@@ ifeq ($(uname_O),Cygwin
        NO_D_TYPE_IN_DIRENT = YesPlease
        NO_D_INO_IN_DIRENT = YesPlease
        NO_STRCASESTR = YesPlease
 +      NO_MEMMEM = YesPlease
        NO_SYMLINK_HEAD = YesPlease
        NEEDS_LIBICONV = YesPlease
        NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
  endif
  ifeq ($(uname_S),FreeBSD)
        NEEDS_LIBICONV = YesPlease
 +      NO_MEMMEM = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
  endif
  ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
 +      NO_MEMMEM = YesPlease
        NEEDS_LIBICONV = YesPlease
        BASIC_CFLAGS += -I/usr/local/include
        BASIC_LDFLAGS += -L/usr/local/lib
@@@ -472,7 -456,6 +472,7 @@@ ifeq ($(uname_S),NetBSD
  endif
  ifeq ($(uname_S),AIX)
        NO_STRCASESTR=YesPlease
 +      NO_MEMMEM = YesPlease
        NO_STRLCPY = YesPlease
        NEEDS_LIBICONV=YesPlease
  endif
@@@ -484,7 -467,6 +484,7 @@@ ifeq ($(uname_S),IRIX64
        NO_IPV6=YesPlease
        NO_SETENV=YesPlease
        NO_STRCASESTR=YesPlease
 +      NO_MEMMEM = YesPlease
        NO_STRLCPY = YesPlease
        NO_SOCKADDR_STORAGE=YesPlease
        SHELL_PATH=/usr/gnu/bin/bash
@@@ -522,9 -504,7 +522,9 @@@ els
        CC_LD_DYNPATH = -R
  endif
  
 -ifndef NO_CURL
 +ifdef NO_CURL
 +      BASIC_CFLAGS += -DNO_CURL
 +else
        ifdef CURLDIR
                # Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
                BASIC_CFLAGS += -I$(CURLDIR)/include
        else
                CURL_LIBCURL = -lcurl
        endif
 -      PROGRAMS += git-http-fetch$X
 +      BUILTIN_OBJS += builtin-http-fetch.o
 +      EXTLIBS += $(CURL_LIBCURL)
 +      LIB_OBJS += http.o http-walker.o
        curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
        ifeq "$(curl_check)" "070908"
                ifndef NO_EXPAT
@@@ -616,10 -594,6 +616,10 @@@ ifdef NO_SETEN
        COMPAT_CFLAGS += -DNO_SETENV
        COMPAT_OBJS += compat/setenv.o
  endif
 +ifdef NO_MKDTEMP
 +      COMPAT_CFLAGS += -DNO_MKDTEMP
 +      COMPAT_OBJS += compat/mkdtemp.o
 +endif
  ifdef NO_UNSETENV
        COMPAT_CFLAGS += -DNO_UNSETENV
        COMPAT_OBJS += compat/unsetenv.o
@@@ -663,10 -637,6 +663,10 @@@ ifdef OLD_ICON
        BASIC_CFLAGS += -DOLD_ICONV
  endif
  
 +ifdef NO_DEFLATE_BOUND
 +      BASIC_CFLAGS += -DNO_DEFLATE_BOUND
 +endif
 +
  ifdef PPC_SHA1
        SHA1_HEADER = "ppc/sha1.h"
        LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
@@@ -691,15 -661,6 +691,15 @@@ ifdef NO_HSTRERRO
        COMPAT_CFLAGS += -DNO_HSTRERROR
        COMPAT_OBJS += compat/hstrerror.o
  endif
 +ifdef NO_MEMMEM
 +      COMPAT_CFLAGS += -DNO_MEMMEM
 +      COMPAT_OBJS += compat/memmem.o
 +endif
 +
 +ifdef THREADED_DELTA_SEARCH
 +      BASIC_CFLAGS += -DTHREADED_DELTA_SEARCH
 +      EXTLIBS += -lpthread
 +endif
  
  ifeq ($(TCLTK_PATH),)
  NO_TCLTK=NoThanks
@@@ -848,7 -809,6 +848,7 @@@ gitweb/gitweb.cgi: gitweb/gitweb.per
            -e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
            -e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
            -e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \
 +          -e 's|"++GITWEB_PROJECT_MAXDEPTH++"|$(GITWEB_PROJECT_MAXDEPTH)|g' \
            -e 's|++GITWEB_EXPORT_OK++|$(GITWEB_EXPORT_OK)|g' \
            -e 's|++GITWEB_STRICT_EXPORT++|$(GITWEB_STRICT_EXPORT)|g' \
            -e 's|++GITWEB_BASE_URL++|$(GITWEB_BASE_URL)|g' \
@@@ -905,23 -865,36 +905,24 @@@ http.o: http.c GIT-CFLAG
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $<
  
  ifdef NO_EXPAT
 -http-fetch.o: http-fetch.c http.h GIT-CFLAGS
 +http-walker.o: http-walker.c http.h GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $<
  endif
  
  git-%$X: %.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
  
 -ssh-pull.o: ssh-fetch.c
 -ssh-push.o: ssh-upload.c
 -git-local-fetch$X: fetch.o
 -git-ssh-fetch$X: rsh.o fetch.o
 -git-ssh-upload$X: rsh.o
 -git-ssh-pull$X: rsh.o fetch.o
 -git-ssh-push$X: rsh.o
 -
  git-imap-send$X: imap-send.o $(LIB_FILE)
  
 -http.o http-fetch.o http-push.o: http.h
 -git-http-fetch$X: fetch.o http.o http-fetch.o $(GITLIBS)
 -      $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
 -              $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 +http.o http-walker.o http-push.o: http.h
  
  git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
  
 -$(LIB_OBJS) $(BUILTIN_OBJS) fetch.o: $(LIB_H)
 +$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)
  $(patsubst git-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)
 -$(DIFF_OBJS): diffcore.h
+ builtin-revert.o builtin-runstatus.o wt-status.o: wt-status.h
  
  $(LIB_FILE): $(LIB_OBJS)
        $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
@@@ -949,10 -922,6 +950,10 @@@ tags
        $(RM) tags
        $(FIND) . -name '*.[hcS]' -print | xargs ctags -a
  
 +cscope:
 +      $(RM) cscope*
 +      $(FIND) . -name '*.[hcS]' -print | xargs cscope -b
 +
  ### Detect prefix changes
  TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
               $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
@@@ -980,7 -949,7 +981,7 @@@ endi
  
  ### Testing rules
  
 -TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X
 +TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-absolute-path$X test-parse-options$X
  
  all:: $(TEST_PROGRAMS)
  
@@@ -1102,7 -1071,7 +1103,7 @@@ clean
                $(LIB_FILE) $(XDIFF_LIB)
        $(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS)
 -      $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
 +      $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope*
        $(RM) -r autom4te.cache
        $(RM) config.log config.mak.autogen config.mak.append config.status config.cache
        $(RM) -r $(GIT_TARNAME) .doc-tmp-dir
@@@ -1120,7 -1089,7 +1121,7 @@@ endi
        $(RM) GIT-VERSION-FILE GIT-CFLAGS GIT-GUI-VARS
  
  .PHONY: all install clean strip
 -.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags .FORCE-GIT-CFLAGS
 +.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags cscope .FORCE-GIT-CFLAGS
  
  ### Check documentation
  #
@@@ -1131,7 -1100,8 +1132,7 @@@ check-docs:
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
                git-merge-resolve | git-merge-stupid | \
                git-add--interactive | git-fsck-objects | git-init-db | \
 -              git-repo-config | git-fetch--tool | \
 -              git-ssh-pull | git-ssh-push ) continue ;; \
 +              git-repo-config | git-fetch--tool ) continue ;; \
                esac ; \
                test -f "Documentation/$$v.txt" || \
                echo "no doc: $$v"; \
diff --combined builtin-mailsplit.c
index 74b04706f36128711719fff3e1423b3fb454af4c,10fa1773401d6474d8e6c88065e3a992690ae0f8..46b27cdaea71cba92974480da74ec5922fcf3a7a
@@@ -101,20 -101,29 +101,29 @@@ static int populate_maildir_list(struc
  {
        DIR *dir;
        struct dirent *dent;
+       char name[PATH_MAX];
+       char *subs[] = { "cur", "new", NULL };
+       char **sub;
+       for (sub = subs; *sub; ++sub) {
+               snprintf(name, sizeof(name), "%s/%s", path, *sub);
+               if ((dir = opendir(name)) == NULL) {
+                       if (errno == ENOENT)
+                               continue;
+                       error("cannot opendir %s (%s)", name, strerror(errno));
+                       return -1;
+               }
  
-       if ((dir = opendir(path)) == NULL) {
-               error("cannot opendir %s (%s)", path, strerror(errno));
-               return -1;
-       }
+               while ((dent = readdir(dir)) != NULL) {
+                       if (dent->d_name[0] == '.')
+                               continue;
+                       snprintf(name, sizeof(name), "%s/%s", *sub, dent->d_name);
+                       path_list_insert(name, list);
+               }
  
-       while ((dent = readdir(dir)) != NULL) {
-               if (dent->d_name[0] == '.')
-                       continue;
-               path_list_insert(dent->d_name, list);
+               closedir(dir);
        }
  
-       closedir(dir);
        return 0;
  }
  
@@@ -122,19 -131,17 +131,17 @@@ static int split_maildir(const char *ma
        int nr_prec, int skip)
  {
        char file[PATH_MAX];
-       char curdir[PATH_MAX];
        char name[PATH_MAX];
        int ret = -1;
        int i;
        struct path_list list = {NULL, 0, 0, 1};
  
-       snprintf(curdir, sizeof(curdir), "%s/cur", maildir);
-       if (populate_maildir_list(&list, curdir) < 0)
+       if (populate_maildir_list(&list, maildir) < 0)
                goto out;
  
        for (i = 0; i < list.nr; i++) {
                FILE *f;
-               snprintf(file, sizeof(file), "%s/%s", curdir, list.items[i].path);
+               snprintf(file, sizeof(file), "%s/%s", maildir, list.items[i].path);
                f = fopen(file, "r");
                if (!f) {
                        error("cannot open mail %s (%s)", file, strerror(errno));
                fclose(f);
        }
  
-       path_list_clear(&list, 1);
        ret = skip;
  out:
+       path_list_clear(&list, 1);
        return ret;
  }
  
@@@ -164,7 -170,6 +170,7 @@@ static int split_mbox(const char *file
  {
        char name[PATH_MAX];
        int ret = -1;
 +      int peek;
  
        FILE *f = !strcmp(file, "-") ? stdin : fopen(file, "r");
        int file_done = 0;
                goto out;
        }
  
 +      do {
 +              peek = fgetc(f);
 +      } while (isspace(peek));
 +      ungetc(peek, f);
 +
        if (fgets(buf, sizeof(buf), f) == NULL) {
                /* empty stdin is OK */
                if (f != stdin) {
diff --combined git-instaweb.sh
index 95c3e5aa1f9bd5f0cec215ee2f81b2d4c36ea929,2ca487d7d5e5e35bc8f3ceaa04389e5bbad9a098..ada1180528b08607858efbcce9b2afc6d1b0e061
@@@ -15,7 -15,7 +15,7 @@@ browser="`git config --get instaweb.bro
  port=`git config --get instaweb.port`
  module_path="`git config --get instaweb.modulepath`"
  
- conf=$GIT_DIR/gitweb/httpd.conf
+ conf="$GIT_DIR/gitweb/httpd.conf"
  
  # Defaults:
  
@@@ -32,13 -32,11 +32,13 @@@ start_httpd () 
        httpd_only="`echo $httpd | cut -f1 -d' '`"
        if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null;; esac
        then
-               $httpd $fqgitdir/gitweb/httpd.conf
+               $httpd "$fqgitdir/gitweb/httpd.conf"
        else
                # many httpds are installed in /usr/sbin or /usr/local/sbin
                # these days and those are not in most users $PATHs
 -              for i in /usr/local/sbin /usr/sbin
 +              # in addition, we may have generated a server script
 +              # in $fqgitdir/gitweb.
 +              for i in /usr/local/sbin /usr/sbin "$fqgitdir/gitweb"
                do
                        if test -x "$i/$httpd_only"
                        then
@@@ -138,43 -136,6 +138,43 @@@ GIT_DIR="$fqgitdir
  export GIT_EXEC_PATH GIT_DIR
  
  
 +webrick_conf () {
 +      # generate a standalone server script in $fqgitdir/gitweb.
 +      cat >"$fqgitdir/gitweb/$httpd.rb" <<EOF
 +require 'webrick'
 +require 'yaml'
 +options = YAML::load_file(ARGV[0])
 +options[:StartCallback] = proc do
 +  File.open(options[:PidFile],"w") do |f|
 +    f.puts Process.pid
 +  end
 +end
 +options[:ServerType] = WEBrick::Daemon
 +server = WEBrick::HTTPServer.new(options)
 +['INT', 'TERM'].each do |signal|
 +  trap(signal) {server.shutdown}
 +end
 +server.start
 +EOF
 +      # generate a shell script to invoke the above ruby script,
 +      # which assumes _ruby_ is in the user's $PATH. that's _one_
 +      # portable way to run ruby, which could be installed anywhere,
 +      # really.
 +      cat >"$fqgitdir/gitweb/$httpd" <<EOF
 +#!/bin/sh
 +exec ruby "$fqgitdir/gitweb/$httpd.rb" \$*
 +EOF
 +      chmod +x "$fqgitdir/gitweb/$httpd"
 +
 +      cat >"$conf" <<EOF
 +:Port: $port
 +:DocumentRoot: "$fqgitdir/gitweb"
 +:DirectoryIndex: ["gitweb.cgi"]
 +:PidFile: "$fqgitdir/pid"
 +EOF
 +      test "$local" = true && echo ':BindAddress: "127.0.0.1"' >> "$conf"
 +}
 +
  lighttpd_conf () {
        cat > "$conf" <<EOF
  server.document-root = "$fqgitdir/gitweb"
@@@ -185,14 -146,14 +185,14 @@@ server.pid-file = "$fqgitdir/pid
  cgi.assign = ( ".cgi" => "" )
  mimetype.assign = ( ".css" => "text/css" )
  EOF
-       test "$local" = true && echo 'server.bind = "127.0.0.1"' >> "$conf"
+       test x"$local" = xtrue && echo 'server.bind = "127.0.0.1"' >> "$conf"
  }
  
  apache2_conf () {
        test -z "$module_path" && module_path=/usr/lib/apache2/modules
        mkdir -p "$GIT_DIR/gitweb/logs"
        bind=
-       test "$local" = true && bind='127.0.0.1:'
+       test x"$local" = xtrue && bind='127.0.0.1:'
        echo 'text/css css' > $fqgitdir/mime.types
        cat > "$conf" <<EOF
  ServerName "git-instaweb"
@@@ -245,7 -206,7 +245,7 @@@ EO
  }
  
  script='
- s#^\(my\|our\) $projectroot =.*#\1 $projectroot = "'`dirname $fqgitdir`'";#
+ s#^\(my\|our\) $projectroot =.*#\1 $projectroot = "'$(dirname "$fqgitdir")'";#
  s#\(my\|our\) $gitbin =.*#\1 $gitbin = "'$GIT_EXEC_PATH'";#
  s#\(my\|our\) $projects_list =.*#\1 $projects_list = $projectroot;#
  s#\(my\|our\) $git_temp =.*#\1 $git_temp = "'$fqgitdir/gitweb/tmp'";#'
@@@ -265,8 -226,8 +265,8 @@@ gitweb_css () 
  EOFGITWEB
  }
  
- gitweb_cgi $GIT_DIR/gitweb/gitweb.cgi
- gitweb_css $GIT_DIR/gitweb/gitweb.css
+ gitweb_cgi "$GIT_DIR/gitweb/gitweb.cgi"
+ gitweb_css "$GIT_DIR/gitweb/gitweb.css"
  
  case "$httpd" in
  *lighttpd*)
  *apache2*)
        apache2_conf
        ;;
 +webrick)
 +      webrick_conf
 +      ;;
  *)
        echo "Unknown httpd specified: $httpd"
        exit 1
  esac
  
  start_httpd
- test -z "$browser" && browser=echo
  url=http://127.0.0.1:$port
$browser $url || echo $url
"$browser" $url || echo $url
index 76dc679e62cf32a5ba04902558812e3244baf936,d65df2cb8037f715bfaf016ee323bd810b041460..51063776d2540ed4ad6537e3578916b40d46c81a
@@@ -36,14 -36,14 +36,14 @@@ warn () 
  output () {
        case "$VERBOSE" in
        '')
 -              "$@" > "$DOTEST"/output 2>&1
 +              output=$("$@" 2>&1 )
                status=$?
 -              test $status != 0 &&
 -                      cat "$DOTEST"/output
 +              test $status != 0 && printf "%s\n" "$output"
                return $status
 -      ;;
 +              ;;
        *)
                "$@"
 +              ;;
        esac
  }
  
@@@ -63,7 -63,6 +63,7 @@@ comment_for_reflog () 
        ''|rebase*)
                GIT_REFLOG_ACTION="rebase -i ($1)"
                export GIT_REFLOG_ACTION
 +              ;;
        esac
  }
  
@@@ -71,23 -70,22 +71,23 @@@ mark_action_done () 
        sed -e 1q < "$TODO" >> "$DONE"
        sed -e 1d < "$TODO" >> "$TODO".new
        mv -f "$TODO".new "$TODO"
 -      count=$(($(wc -l < "$DONE")))
 -      total=$(($count+$(wc -l < "$TODO")))
 +      count=$(($(grep -ve '^$' -e '^#' < "$DONE" | wc -l)))
 +      total=$(($count+$(grep -ve '^$' -e '^#' < "$TODO" | wc -l)))
        printf "Rebasing (%d/%d)\r" $count $total
        test -z "$VERBOSE" || echo
  }
  
  make_patch () {
 -      parent_sha1=$(git rev-parse --verify "$1"^ 2> /dev/null)
 +      parent_sha1=$(git rev-parse --verify "$1"^) ||
 +              die "Cannot get patch for $1^"
        git diff-tree -p "$parent_sha1".."$1" > "$DOTEST"/patch
 +      test -f "$DOTEST"/message ||
 +              git cat-file commit "$1" | sed "1,/^$/d" > "$DOTEST"/message
 +      test -f "$DOTEST"/author-script ||
 +              get_author_ident_from_commit "$1" > "$DOTEST"/author-script
  }
  
  die_with_patch () {
 -      test -f "$DOTEST"/message ||
 -              git cat-file commit $sha1 | sed "1,/^$/d" > "$DOTEST"/message
 -      test -f "$DOTEST"/author-script ||
 -              get_author_ident_from_commit $sha1 > "$DOTEST"/author-script
        make_patch "$1"
        die "$2"
  }
@@@ -97,20 -95,15 +97,20 @@@ die_abort () 
        die "$1"
  }
  
 +has_action () {
 +      grep -vqe '^$' -e '^#' "$1"
 +}
 +
  pick_one () {
        no_ff=
        case "$1" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac
        output git rev-parse --verify $sha1 || die "Invalid commit name: $sha1"
        test -d "$REWRITTEN" &&
                pick_one_preserving_merges "$@" && return
 -      parent_sha1=$(git rev-parse --verify $sha1^ 2>/dev/null)
 +      parent_sha1=$(git rev-parse --verify $sha1^) ||
 +              die "Could not get the parent of $sha1"
        current_sha1=$(git rev-parse --verify HEAD)
 -      if test $no_ff$current_sha1 = $parent_sha1; then
 +      if test "$no_ff$current_sha1" = "$parent_sha1"; then
                output git reset --hard $sha1
                test "a$1" = a-n && output git reset --soft $current_sha1
                sha1=$(git rev-parse --short $sha1)
@@@ -136,7 -129,7 +136,7 @@@ pick_one_preserving_merges () 
        fast_forward=t
        preserve=t
        new_parents=
 -      for p in $(git rev-list --parents -1 $sha1 | cut -d -f2-)
 +      for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)
        do
                if test -f "$REWRITTEN"/$p
                then
                                ;; # do nothing; that parent is already there
                        *)
                                new_parents="$new_parents $new_p"
 +                              ;;
                        esac
                fi
        done
        case $fast_forward in
        t)
                output warn "Fast forward to $sha1"
 -              test $preserve=f && echo $sha1 > "$REWRITTEN"/$sha1
 +              test $preserve = f || echo $sha1 > "$REWRITTEN"/$sha1
                ;;
        f)
                test "a$1" = a-n && die "Refusing to squash a merge: $sha1"
  
 -              first_parent=$(expr "$new_parents" : " \([^ ]*\)")
 +              first_parent=$(expr "$new_parents" : ' \([^ ]*\)')
                # detach HEAD to current parent
                output git checkout $first_parent 2> /dev/null ||
                        die "Cannot move HEAD to $first_parent"
  
                echo $sha1 > "$DOTEST"/current-commit
                case "$new_parents" in
 -              \ *\ *)
 +              ' '*' '*)
                        # redo merge
                        author_script=$(get_author_ident_from_commit $sha1)
                        eval "$author_script"
 -                      msg="$(git cat-file commit $sha1 | \
 -                              sed -e '1,/^$/d' -e "s/[\"\\]/\\\\&/g")"
 +                      msg="$(git cat-file commit $sha1 | sed -e '1,/^$/d')"
                        # No point in merging the first parent, that's HEAD
                        new_parents=${new_parents# $first_parent}
                        # NEEDSWORK: give rerere a chance
 -                      if ! output git merge $STRATEGY -m "$msg" $new_parents
 +                      if ! GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
 +                              GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
 +                              GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
 +                              output git merge $STRATEGY -m "$msg" \
 +                                      $new_parents
                        then
 -                              echo "$msg" > "$GIT_DIR"/MERGE_MSG
 +                              printf "%s\n" "$msg" > "$GIT_DIR"/MERGE_MSG
                                die Error redoing merge $sha1
                        fi
                        ;;
                *)
                        output git cherry-pick "$@" ||
                                die_with_patch $sha1 "Could not pick $sha1"
 +                      ;;
                esac
 +              ;;
        esac
  }
  
@@@ -227,28 -214,27 +227,28 @@@ peek_next_command () 
  }
  
  do_next () {
 -      test -f "$DOTEST"/message && rm "$DOTEST"/message
 -      test -f "$DOTEST"/author-script && rm "$DOTEST"/author-script
 +      rm -f "$DOTEST"/message "$DOTEST"/author-script \
 +              "$DOTEST"/amend || exit
        read command sha1 rest < "$TODO"
        case "$command" in
 -      \#|'')
 +      '#'*|'')
                mark_action_done
                ;;
 -      pick)
 +      pick|p)
                comment_for_reflog pick
  
                mark_action_done
                pick_one $sha1 ||
                        die_with_patch $sha1 "Could not apply $sha1... $rest"
                ;;
 -      edit)
 +      edit|e)
                comment_for_reflog edit
  
                mark_action_done
                pick_one $sha1 ||
                        die_with_patch $sha1 "Could not apply $sha1... $rest"
                make_patch $sha1
 +              : > "$DOTEST"/amend
                warn
                warn "You can amend the commit now, with"
                warn
                warn
                exit 0
                ;;
 -      squash)
 +      squash|s)
                comment_for_reflog squash
  
 -              test -z "$(grep -ve '^$' -e '^#' < $DONE)" &&
 +              has_action "$DONE" ||
                        die "Cannot 'squash' without a previous commit"
  
                mark_action_done
                make_squash_message $sha1 > "$MSG"
                case "$(peek_next_command)" in
 -              squash)
 +              squash|s)
                        EDIT_COMMIT=
                        USE_OUTPUT=output
                        cp "$MSG" "$SQUASH_MSG"
 -              ;;
 +                      ;;
                *)
                        EDIT_COMMIT=-e
                        USE_OUTPUT=
 -                      test -f "$SQUASH_MSG" && rm "$SQUASH_MSG"
 +                      rm -f "$SQUASH_MSG" || exit
 +                      ;;
                esac
  
                failed=f
                f)
                        # This is like --amend, but with a different message
                        eval "$author_script"
 -                      export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
 +                      GIT_AUTHOR_NAME="$GIT_AUTHOR_NAME" \
 +                      GIT_AUTHOR_EMAIL="$GIT_AUTHOR_EMAIL" \
 +                      GIT_AUTHOR_DATE="$GIT_AUTHOR_DATE" \
                        $USE_OUTPUT git commit -F "$MSG" $EDIT_COMMIT
                        ;;
                t)
                        warn
                        warn "Could not apply $sha1... $rest"
                        die_with_patch $sha1 ""
 +                      ;;
                esac
                ;;
        *)
                warn "Unknown command: $command $sha1 $rest"
                die_with_patch $sha1 "Please fix this in the file $TODO."
 +              ;;
        esac
        test -s "$TODO" && return
  
        else
                NEWHEAD=$(git rev-parse HEAD)
        fi &&
 -      message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" &&
 -      git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD &&
 -      git symbolic-ref HEAD $HEADNAME && {
 +      case $HEADNAME in
 +      refs/*)
 +              message="$GIT_REFLOG_ACTION: $HEADNAME onto $SHORTONTO)" &&
 +              git update-ref -m "$message" $HEADNAME $NEWHEAD $OLDHEAD &&
 +              git symbolic-ref HEAD $HEADNAME
 +              ;;
 +      esac && {
                test ! -f "$DOTEST"/verbose ||
                        git diff-tree --stat $(cat "$DOTEST"/head)..HEAD
        } &&
        rm -rf "$DOTEST" &&
 +      git gc --auto &&
        warn "Successfully rebased and updated $HEADNAME."
  
        exit
@@@ -356,9 -332,7 +356,9 @@@ d
                git update-index --refresh &&
                git diff-files --quiet &&
                ! git diff-index --cached --quiet HEAD &&
 -              . "$DOTEST"/author-script &&
 +              . "$DOTEST"/author-script && {
 +                      test ! -f "$DOTEST"/amend || git reset --soft HEAD^
 +              } &&
                export GIT_AUTHOR_NAME GIT_AUTHOR_NAME GIT_AUTHOR_DATE &&
                git commit -F "$DOTEST"/message -e
  
  
                HEADNAME=$(cat "$DOTEST"/head-name)
                HEAD=$(cat "$DOTEST"/head)
 -              git symbolic-ref HEAD $HEADNAME &&
 +              case $HEADNAME in
 +              refs/*)
 +                      git symbolic-ref HEAD $HEADNAME
 +                      ;;
 +              esac &&
                output git reset --hard $HEAD &&
                rm -rf "$DOTEST"
                exit
        -s|--strategy)
                case "$#,$1" in
                *,*=*)
-                       STRATEGY="-s `expr "z$1" : 'z-[^=]*=\(.*\)'`" ;;
+                       STRATEGY="-s "$(expr "z$1" : 'z-[^=]*=\(.*\)') ;;
                1,*)
                        usage ;;
                *)
  
                require_clean_work_tree
  
 -              mkdir "$DOTEST" || die "Could not create temporary $DOTEST"
                if test ! -z "$2"
                then
                        output git show-ref --verify --quiet "refs/heads/$2" ||
                HEAD=$(git rev-parse --verify HEAD) || die "No HEAD?"
                UPSTREAM=$(git rev-parse --verify "$1") || die "Invalid base"
  
 +              mkdir "$DOTEST" || die "Could not create temporary $DOTEST"
 +
                test -z "$ONTO" && ONTO=$UPSTREAM
  
                : > "$DOTEST"/interactive || die "Could not mark as interactive"
 -              git symbolic-ref HEAD > "$DOTEST"/head-name ||
 -                      die "Could not get HEAD"
 +              git symbolic-ref HEAD > "$DOTEST"/head-name 2> /dev/null ||
 +                      echo "detached HEAD" > "$DOTEST"/head-name
  
                echo $HEAD > "$DOTEST"/head
                echo $UPSTREAM > "$DOTEST"/upstream
@@@ -500,18 -469,17 +500,18 @@@ EO
                        $UPSTREAM...$HEAD | \
                        sed -n "s/^>/pick /p" >> "$TODO"
  
 -              test -z "$(grep -ve '^$' -e '^#' < $TODO)" &&
 +              has_action "$TODO" ||
                        die_abort "Nothing to do"
  
                cp "$TODO" "$TODO".backup
                git_editor "$TODO" ||
                        die "Could not execute editor"
  
 -              test -z "$(grep -ve '^$' -e '^#' < $TODO)" &&
 +              has_action "$TODO" ||
                        die_abort "Nothing to do"
  
                output git checkout $ONTO && do_rest
 +              ;;
        esac
        shift
  done
diff --combined git-send-email.perl
index f4b8f96511b17e084163e2289c620f8793c10d87,8760cf88a5fe52723297eda1b4db54d6e97d50bb..f9bd2e5a9176ccf7dd8e2ce15233d2d93f9222ec
@@@ -73,22 -73,11 +73,22 @@@ Options
     --signed-off-cc Automatically add email addresses that appear in
                   Signed-off-by: or Cc: lines to the cc: list. Defaults to on.
  
 +   --identity     The configuration identity, a subsection to prioritise over
 +                  the default section.
 +
     --smtp-server  If set, specifies the outgoing SMTP server to use.
 -                  Defaults to localhost.
 +                  Defaults to localhost.  Port number can be specified here with
 +                  hostname:port format or by using --smtp-server-port option.
 +
 +   --smtp-server-port Specify a port on the outgoing SMTP server to connect to.
 +
 +   --smtp-user    The username for SMTP-AUTH.
  
 -   --suppress-from Suppress sending emails to yourself if your address
 -                  appears in a From: line. Defaults to off.
 +   --smtp-pass    The password for SMTP-AUTH.
 +
 +   --smtp-ssl     If set, connects to the SMTP server using SSL.
 +
 +   --suppress-from Suppress sending emails to yourself. Defaults to off.
  
     --thread       Specify that the "In-Reply-To:" header should be set on all
                    emails. Defaults to on.
@@@ -156,6 -145,7 +156,6 @@@ my $compose_filename = ".msg.$$"
  my (@to,@cc,@initial_cc,@bcclist,@xh,
        $initial_reply_to,$initial_subject,@files,$author,$sender,$compose,$time);
  
 -my $smtp_server;
  my $envelope_sender;
  
  # Example reply to:
@@@ -174,28 -164,24 +174,28 @@@ my ($quiet, $dry_run) = (0, 0)
  
  # Variables with corresponding config settings
  my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd);
 +my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_authpass, $smtp_ssl);
 +my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts);
  
 -my %config_settings = (
 +my %config_bool_settings = (
      "thread" => [\$thread, 1],
      "chainreplyto" => [\$chain_reply_to, 1],
      "suppressfrom" => [\$suppress_from, 0],
      "signedoffcc" => [\$signed_off_cc, 1],
 -    "cccmd" => [\$cc_cmd, ""],
 +    "smtpssl" => [\$smtp_ssl, 0],
  );
  
 -foreach my $setting (keys %config_settings) {
 -    my $config = $repo->config_bool("sendemail.$setting");
 -    ${$config_settings{$setting}->[0]} = (defined $config) ? $config : $config_settings{$setting}->[1];
 -}
 -
 -@bcclist = $repo->config('sendemail.bcc');
 -if (!@bcclist or !$bcclist[0]) {
 -    @bcclist = ();
 -}
 +my %config_settings = (
 +    "smtpserver" => \$smtp_server,
 +    "smtpserverport" => \$smtp_server_port,
 +    "smtpuser" => \$smtp_authuser,
 +    "smtppass" => \$smtp_authpass,
 +    "to" => \@to,
 +    "cccmd" => \$cc_cmd,
 +    "aliasfiletype" => \$aliasfiletype,
 +    "bcc" => \@bcclist,
 +    "aliasesfile" => \@alias_files,
 +);
  
  # Begin by accumulating all the variables (defined above), that we will end up
  # needing, first, from the command line:
@@@ -208,11 -194,6 +208,11 @@@ my $rc = GetOptions("sender|from=s" => 
                    "bcc=s" => \@bcclist,
                    "chain-reply-to!" => \$chain_reply_to,
                    "smtp-server=s" => \$smtp_server,
 +                  "smtp-server-port=s" => \$smtp_server_port,
 +                  "smtp-user=s" => \$smtp_authuser,
 +                  "smtp-pass=s" => \$smtp_authpass,
 +                  "smtp-ssl!" => \$smtp_ssl,
 +                  "identity=s" => \$identity,
                    "compose" => \$compose,
                    "quiet" => \$quiet,
                    "cc-cmd=s" => \$cc_cmd,
@@@ -227,43 -208,6 +227,43 @@@ unless ($rc) 
      usage();
  }
  
 +# Now, let's fill any that aren't set in with defaults:
 +
 +sub read_config {
 +      my ($prefix) = @_;
 +
 +      foreach my $setting (keys %config_bool_settings) {
 +              my $target = $config_bool_settings{$setting}->[0];
 +              $$target = $repo->config_bool("$prefix.$setting") unless (defined $$target);
 +      }
 +
 +      foreach my $setting (keys %config_settings) {
 +              my $target = $config_settings{$setting};
 +              if (ref($target) eq "ARRAY") {
 +                      unless (@$target) {
 +                              my @values = $repo->config("$prefix.$setting");
 +                              @$target = @values if (@values && defined $values[0]);
 +                      }
 +              }
 +              else {
 +                      $$target = $repo->config("$prefix.$setting") unless (defined $$target);
 +              }
 +      }
 +}
 +
 +# read configuration from [sendemail "$identity"], fall back on [sendemail]
 +$identity = $repo->config("sendemail.identity") unless (defined $identity);
 +read_config("sendemail.$identity") if (defined $identity);
 +read_config("sendemail");
 +
 +# fall back on builtin bool defaults
 +foreach my $setting (values %config_bool_settings) {
 +      ${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]}));
 +}
 +
 +my ($repoauthor) = $repo->ident_person('author');
 +my ($repocommitter) = $repo->ident_person('committer');
 +
  # Verify the user input
  
  foreach my $entry (@to) {
@@@ -278,7 -222,14 +278,7 @@@ foreach my $entry (@bcclist) 
        die "Comma in --bcclist entry: $entry'\n" unless $entry !~ m/,/;
  }
  
 -# Now, let's fill any that aren't set in with defaults:
 -
 -my ($repoauthor) = $repo->ident_person('author');
 -my ($repocommitter) = $repo->ident_person('committer');
 -
  my %aliases;
 -my @alias_files = $repo->config('sendemail.aliasesfile');
 -my $aliasfiletype = $repo->config('sendemail.aliasfiletype');
  my %parse_alias = (
        # multiline formats can be supported in the future
        mutt => sub { my $fh = shift; while (<$fh>) {
@@@ -352,7 -303,7 +352,7 @@@ sub expand_aliases 
  
  if (!defined $initial_subject && $compose) {
        do {
-               $_ = $term->readline("What subject should the emails start with? ",
+               $_ = $term->readline("What subject should the initial email start with? ",
                        $initial_subject);
        } while (!defined $_);
        $initial_subject = $_;
@@@ -370,7 -321,10 +370,7 @@@ if ($thread && !defined $initial_reply_
        $initial_reply_to =~ s/>?\s+$/>/;
  }
  
 -if (!$smtp_server) {
 -      $smtp_server = $repo->config('sendemail.smtpserver');
 -}
 -if (!$smtp_server) {
 +if (!defined $smtp_server) {
        foreach (qw( /usr/sbin/sendmail /usr/lib/sendmail )) {
                if (-x $_) {
                        $smtp_server = $_;
@@@ -607,30 -561,8 +607,30 @@@ X-Mailer: git-send-email $gitversio
                print $sm "$header\n$message";
                close $sm or die $?;
        } else {
 -              require Net::SMTP;
 -              $smtp ||= Net::SMTP->new( $smtp_server );
 +
 +              if (!defined $smtp_server) {
 +                      die "The required SMTP server is not properly defined."
 +              }
 +
 +              if ($smtp_ssl) {
 +                      $smtp_server_port ||= 465; # ssmtp
 +                      require Net::SMTP::SSL;
 +                      $smtp ||= Net::SMTP::SSL->new($smtp_server, Port => $smtp_server_port);
 +              }
 +              else {
 +                      require Net::SMTP;
 +                      $smtp ||= Net::SMTP->new((defined $smtp_server_port)
 +                                               ? "$smtp_server:$smtp_server_port"
 +                                               : $smtp_server);
 +              }
 +
 +              if (!$smtp) {
 +                      die "Unable to initialize SMTP properly.  Is there something wrong with your config?";
 +              }
 +
 +              if ((defined $smtp_authuser) && (defined $smtp_authpass)) {
 +                      $smtp->auth( $smtp_authuser, $smtp_authpass ) or die $smtp->message;
 +              }
                $smtp->mail( $raw_from ) or die $smtp->message;
                $smtp->to( @recipients ) or die $smtp->message;
                $smtp->data or die $smtp->message;
@@@ -729,7 -661,6 +729,7 @@@ foreach my $t (@files) 
                        if (/^(Signed-off-by|Cc): (.*)$/i && $signed_off_cc) {
                                my $c = $2;
                                chomp $c;
 +                              next if ($c eq $sender and $suppress_from);
                                push @cc, $c;
                                printf("(sob) Adding cc: %s from line '%s'\n",
                                        $c, $_) unless $quiet;
        }
        close F;
  
 -      if ($cc_cmd ne "") {
 +      if (defined $cc_cmd) {
                open(F, "$cc_cmd $t |")
                        or die "(cc-cmd) Could not execute '$cc_cmd'";
                while(<F>) {
                        my $c = $_;
                        $c =~ s/^\s*//g;
                        $c =~ s/\n$//g;
 +                      next if ($c eq $sender and $suppress_from);
                        push @cc, $c;
                        printf("(cc-cmd) Adding cc: %s from: '%s'\n",
                                $c, $cc_cmd) unless $quiet;
diff --combined git-submodule.sh
index 4aaaaab0d8450094efb8d2f8e82cc63383b91b48,b91d62632c2eecf701a473dfaf90874a19f90f98..5af28ecd58de8c46f194280887ef9c27c784e606
@@@ -39,32 -39,6 +39,32 @@@ get_repo_base() 
        ) 2>/dev/null
  }
  
 +# Resolve relative url by appending to parent's url
 +resolve_relative_url ()
 +{
 +      branch="$(git symbolic-ref HEAD 2>/dev/null)"
 +      remote="$(git config branch.${branch#refs/heads/}.remote)"
 +      remote="${remote:-origin}"
 +      remoteurl="$(git config remote.$remote.url)" ||
 +              die "remote ($remote) does not have a url in .git/config"
 +      url="$1"
 +      while test -n "$url"
 +      do
 +              case "$url" in
 +              ../*)
 +                      url="${url#../}"
 +                      remoteurl="${remoteurl%/*}"
 +                      ;;
 +              ./*)
 +                      url="${url#./}"
 +                      ;;
 +              *)
 +                      break;;
 +              esac
 +      done
 +      echo "$remoteurl/$url"
 +}
 +
  #
  # Map submodule path to submodule name
  #
@@@ -73,7 -47,7 +73,7 @@@
  module_name()
  {
        # Do we have "submodule.<something>.path = $1" defined in .gitmodules file?
-       re=$(printf '%s' "$1" | sed -e 's/\([^a-zA-Z0-9_]\)/\\\1/g')
+       re=$(printf '%s' "$1" | sed -e 's/[].[^$\\*]/\\&/g')
        name=$( GIT_CONFIG=.gitmodules \
                git config --get-regexp '^submodule\..*\.path$' |
                sed -n -e 's|^submodule\.\(.*\)\.path '"$re"'$|\1|p' )
@@@ -129,19 -103,11 +129,19 @@@ module_add(
                usage
        fi
  
 -      # Turn the source into an absolute path if
 -      # it is local
 -      if base=$(get_repo_base "$repo"); then
 -              repo="$base"
 -      fi
 +      case "$repo" in
 +      ./*|../*)
 +              # dereference source url relative to parent's url
 +              realrepo="$(resolve_relative_url $repo)" ;;
 +      *)
 +              # Turn the source into an absolute path if
 +              # it is local
 +              if base=$(get_repo_base "$repo"); then
 +                      repo="$base"
 +              fi
 +              realrepo=$repo
 +              ;;
 +      esac
  
        # Guess path from repo if not specified or strip trailing slashes
        if test -z "$path"; then
        git ls-files --error-unmatch "$path" > /dev/null 2>&1 &&
        die "'$path' already exists in the index"
  
 -      module_clone "$path" "$repo" || exit
 +      module_clone "$path" "$realrepo" || exit
        (unset GIT_DIR && cd "$path" && git checkout -q ${branch:+-b "$branch" "origin/$branch"}) ||
        die "Unable to checkout submodule '$path'"
        git add "$path" ||
@@@ -187,13 -153,6 +187,13 @@@ modules_init(
                test -z "$url" &&
                die "No url found for submodule path '$path' in .gitmodules"
  
 +              # Possibly a url relative to parent
 +              case "$url" in
 +              ./*|../*)
 +                      url="$(resolve_relative_url "$url")"
 +                      ;;
 +              esac
 +
                git config submodule."$name".url "$url" ||
                die "Failed to register url for submodule path '$path'"