Merge branch 'maint' to sync with GIT 1.6.0.6
authorJunio C Hamano <gitster@pobox.com>
Sat, 20 Dec 2008 03:32:29 +0000 (19:32 -0800)
committerJunio C Hamano <gitster@pobox.com>
Sat, 20 Dec 2008 03:35:55 +0000 (19:35 -0800)
Signed-off-by: Junio C Hamano <gitster@pobox.com>
13 files changed:
1  2 
Documentation/SubmittingPatches
Documentation/git-commit.txt
Documentation/git-diff-tree.txt
Documentation/git-fast-export.txt
Documentation/git-submodule.txt
Documentation/git.txt
Documentation/gitcore-tutorial.txt
Documentation/gitk.txt
Documentation/i18n.txt
fast-import.c
git-send-email.perl
t/t9300-fast-import.sh
t/t9301-fast-export.sh
index f0295c60f5aceb975575903327228776d6b2bb9e,34fdc83ad48f764ebc7908a947cd057350c3674d..ba07c8c57107a8438d5b3ce1a49761dba7182d25
@@@ -71,7 -71,7 +71,7 @@@ run git diff --check on your changes be
  
  (1a) Try to be nice to older C compilers
  
 -We try to support wide range of C compilers to compile
 +We try to support wide range of C compilers to compile
  git with. That means that you should not use C99 initializers, even
  if a lot of compilers grok it.
  
@@@ -222,6 -222,9 +222,9 @@@ D-C-O.  Indeed you are encouraged to d
  place an in-body "From: " line at the beginning to properly attribute
  the change to its true author (see (2) above).
  
+ Also notice that a real name is used in the Signed-off-by: line. Please
+ don't hide your real name.
  Some people also put extra tags at the end.
  
  "Acked-by:" says that the patch was reviewed by the person who
index 6203461f41865b2205520d86d9f3bb85629709cc,5cce3a379167845a7c47e37d42e6b43ca399fa6c..b5d81be7ecd60daa1a1d476441ca58e6b732d9ef
@@@ -29,8 -29,7 +29,8 @@@ The content to be added can be specifie
  
  3. by listing files as arguments to the 'commit' command, in which
     case the commit will ignore changes staged in the index, and instead
 -   record the current content of the listed files;
 +   record the current content of the listed files (which must already
 +   be known to git);
  
  4. by using the -a switch with the 'commit' command to automatically
     "add" changes from all known files (i.e. all files that are already
@@@ -76,10 -75,8 +76,10 @@@ OPTION
        read the message from the standard input.
  
  --author=<author>::
 -      Override the author name used in the commit.  Use
 -      `A U Thor <author@example.com>` format.
 +      Override the author name used in the commit.  You can use the
 +      standard `A U Thor <author@example.com>` format.  Otherwise,
 +      an existing commit that matches the given string and its author
 +      name is used.
  
  -m <msg>::
  --message=<msg>::
@@@ -146,10 -143,6 +146,10 @@@ It is a rough equivalent for
  ------
  but can be used to amend a merge commit.
  --
 ++
 +You should understand the implications of rewriting history if you
 +amend a commit that has already been published.  (See the "RECOVERING
 +FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
  
  -i::
  --include::
        'git-commit' if any paths are given on the command line,
        in which case this option can be omitted.
        If this option is specified together with '--amend', then
-       no paths need be specified, which can be used to amend
+       no paths need to be specified, which can be used to amend
        the last commit without committing changes that have
        already been staged.
  
index 5d48664e624f59e0df16121ed67d84939f6d2a31,4e83067c4a0dc12c2607439c07d972c67b4407cb..fdf8e7b27755e7767550e1ccc33e6f80265244a3
@@@ -43,28 -43,19 +43,28 @@@ include::diff-options.txt[
        show tree entry itself as well as subtrees.  Implies -r.
  
  --root::
-       When '--root' is specified the initial commit will be showed as a big
+       When '--root' is specified the initial commit will be shown as a big
        creation event. This is equivalent to a diff against the NULL tree.
  
  --stdin::
        When '--stdin' is specified, the command does not take
        <tree-ish> arguments from the command line.  Instead, it
 -      reads either one <commit> or a list of <commit>
 -      separated with a single space from its standard input.
 +      reads lines containing either two <tree>, one <commit>, or a
 +      list of <commit> from its standard input.  (Use a single space
 +      as separator.)
  +
 -When a single commit is given on one line of such input, it compares
 -the commit with its parents.  The following flags further affects its
 -behavior.  The remaining commits, when given, are used as if they are
 +When two trees are given, it compares the first tree with the second.
 +When a single commit is given, it compares the commit with its
 +parents.  The remaining commits, when given, are used as if they are
  parents of the first commit.
 ++
 +When comparing two trees, the ID of both trees (separated by a space
 +and terminated by a newline) is printed before the difference.  When
 +comparing commits, the ID of the first (or only) commit, followed by a
 +newline, is printed.
 ++
 +The following flags further affects the behavior when comparing
 +commits (but not trees).
  
  -m::
        By default, 'git-diff-tree --stdin' does not show
index 99a1c3158dc34d642f611524f1bedbc306daf96a,539decbeb2c442633a9c40cbd03ce641bcf115b8..0c9eb567cb3e3af89a2e2613d1e074941fa30d13
@@@ -15,7 -15,7 +15,7 @@@ DESCRIPTIO
  This program dumps the given revisions in a form suitable to be piped
  into 'git-fast-import'.
  
 -You can use it as a human readable bundle replacement (see
 +You can use it as a human-readable bundle replacement (see
  linkgit:git-bundle[1]), or as a kind of an interactive
  'git-filter-branch'.
  
@@@ -65,6 -65,12 +65,12 @@@ If the backend uses a similar \--import
  incremental bidirectional exporting of the repository by keeping the
  marks the same across runs.
  
+ --fake-missing-tagger::
+       Some old repositories have tags without a tagger.  The
+       fast-import protocol was pretty strict about that, and did not
+       allow that.  So fake a tagger to be able to fast-import the
+       output.
  
  EXAMPLES
  --------
index babaa9bc46a404c4610abc4e4c4281b6c5c5ea6c,e6652a7de1b44679cf97c35220018f1ff3fbada2..2f207fbbda04c176cedd5a88c88f74e2ac97cf2e
@@@ -14,8 -14,6 +14,8 @@@ SYNOPSI
  'git submodule' [--quiet] init [--] [<path>...]
  'git submodule' [--quiet] update [--init] [--] [<path>...]
  'git submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...]
 +'git submodule' [--quiet] foreach <command>
 +'git submodule' [--quiet] sync [--] [<path>...]
  
  
  DESCRIPTION
@@@ -87,7 -85,7 +87,7 @@@ use by subsequent users cloning the sup
  given relative to the superproject's repository, the presumption
  is the superproject and submodule repositories will be kept
  together in the same relative location, and only the
- superproject's URL need be provided: git-submodule will correctly
+ superproject's URL needs to be provided: git-submodule will correctly
  locate the submodule using the relative URL in .gitmodules.
  
  status::
@@@ -125,30 -123,6 +125,30 @@@ summary:
        in the submodule between the given super project commit and the
        index or working tree (switched by --cached) are shown.
  
 +foreach::
 +      Evaluates an arbitrary shell command in each checked out submodule.
 +      The command has access to the variables $path and $sha1:
 +      $path is the name of the submodule directory relative to the
 +      superproject, and $sha1 is the commit as recorded in the superproject.
 +      Any submodules defined in the superproject but not checked out are
 +      ignored by this command. Unless given --quiet, foreach prints the name
 +      of each submodule before evaluating the command.
 +      A non-zero return from the command in any submodule causes
 +      the processing to terminate. This can be overridden by adding '|| :'
 +      to the end of the command.
 ++
 +As an example, "git submodule foreach 'echo $path `git rev-parse HEAD`' will
 +show the path and currently checked out commit for each submodule.
 +
 +sync::
 +      Synchronizes submodules' remote URL configuration setting
 +      to the value specified in .gitmodules.  This is useful when
 +      submodule URLs change upstream and you need to update your local
 +      repositories accordingly.
 ++
 +"git submodule sync" synchronizes all submodules while
 +"git submodule sync -- A" synchronizes submodule "A" only.
 +
  OPTIONS
  -------
  -q::
diff --combined Documentation/git.txt
index 585af7a0d066c212a69260e0eace3060e539c205,df420aeb331592192ab27d76af780c8c97a95e87..2c0c23003f74eb72927cc6ecff98247c0894a15c
@@@ -43,19 -43,16 +43,21 @@@ unreleased) version of git, that is ava
  branch of the `git.git` repository.
  Documentation for older releases are available here:
  
- * link:v1.6.0.5/git.html[documentation for release 1.6.0.5]
 -* link:v1.6.0.2/git.html[documentation for release 1.6.0.2]
++* link:v1.6.0.6/git.html[documentation for release 1.6.0.6]
  
  * release notes for
++  link:RelNotes-1.6.0.6.txt[1.6.0.6],
 +  link:RelNotes-1.6.0.5.txt[1.6.0.5],
 +  link:RelNotes-1.6.0.4.txt[1.6.0.4],
 +  link:RelNotes-1.6.0.3.txt[1.6.0.3],
    link:RelNotes-1.6.0.2.txt[1.6.0.2],
    link:RelNotes-1.6.0.1.txt[1.6.0.1],
    link:RelNotes-1.6.0.txt[1.6.0].
  
--* link:v1.5.6.5/git.html[documentation for release 1.5.6.5]
++* link:v1.5.6.6/git.html[documentation for release 1.5.6.6]
  
  * release notes for
++  link:RelNotes-1.5.6.6.txt[1.5.6.6],
    link:RelNotes-1.5.6.5.txt[1.5.6.5],
    link:RelNotes-1.5.6.4.txt[1.5.6.4],
    link:RelNotes-1.5.6.3.txt[1.5.6.3],
    link:RelNotes-1.5.6.1.txt[1.5.6.1],
    link:RelNotes-1.5.6.txt[1.5.6].
  
--* link:v1.5.5.4/git.html[documentation for release 1.5.5.4]
++* link:v1.5.5.6/git.html[documentation for release 1.5.5.6]
  
  * release notes for
++  link:RelNotes-1.5.5.6.txt[1.5.5.6],
++  link:RelNotes-1.5.5.5.txt[1.5.5.5],
    link:RelNotes-1.5.5.4.txt[1.5.5.4],
    link:RelNotes-1.5.5.3.txt[1.5.5.3],
    link:RelNotes-1.5.5.2.txt[1.5.5.2],
    link:RelNotes-1.5.5.1.txt[1.5.5.1],
    link:RelNotes-1.5.5.txt[1.5.5].
  
--* link:v1.5.4.5/git.html[documentation for release 1.5.4.5]
++* link:v1.5.4.7/git.html[documentation for release 1.5.4.7]
  
  * release notes for
++  link:RelNotes-1.5.4.7.txt[1.5.4.7],
++  link:RelNotes-1.5.4.6.txt[1.5.4.6],
    link:RelNotes-1.5.4.5.txt[1.5.4.5],
    link:RelNotes-1.5.4.4.txt[1.5.4.4],
    link:RelNotes-1.5.4.3.txt[1.5.4.3],
index df48045ef61fa1565bb1af1eb1b165b2f2dad8f8,896cbdf6864fdd6a83552921d8a182e76b439ca6..e4dd5518c81ac98ef3da9cbb1cea4a9e1fe6e62c
@@@ -899,7 -899,7 +899,7 @@@ file, which had no differences in the `
  ----------------
        Auto-merging hello
        CONFLICT (content): Merge conflict in hello
 -      Automatic merge failed; fix up by hand
 +      Automatic merge failed; fix conflicts and then commit the result.
  ----------------
  
  It tells you that it did an "Automatic merge", which
@@@ -993,14 -993,14 +993,14 @@@ would be different
  
  ----------------
  Updating from ae3a2da... to a80b4aa....
 -Fast forward
 +Fast forward (no commit created; -m option ignored)
   example |    1 +
   hello   |    1 +
   2 files changed, 2 insertions(+), 0 deletions(-)
  ----------------
  
- Because your branch did not contain anything more than what are
- already merged into the `master` branch, the merge operation did
+ Because your branch did not contain anything more than what had
+ already been merged into the `master` branch, the merge operation did
  not actually do a merge. Instead, it just updated the top of
  the tree of your branch to that of the `master` branch. This is
  often called 'fast forward' merge.
@@@ -1265,8 -1265,9 +1265,8 @@@ file, using 3-way merge.  This is done 
  
  ------------
  $ git merge-index git-merge-one-file hello
 -Auto-merging hello.
 -merge: warning: conflicts during merge
 -ERROR: Merge conflict in hello.
 +Auto-merging hello
 +ERROR: Merge conflict in hello
  fatal: merge program failed
  ------------
  
@@@ -1352,7 -1353,7 +1352,7 @@@ $ GIT_DIR=my-git.git git ini
  ------------
  
  Make sure this directory is available for others you want your
- changes to be pulled by via the transport of your choice. Also
+ changes to be pulled via the transport of your choice. Also
  you need to make sure that you have the 'git-receive-pack'
  program on the `$PATH`.
  
@@@ -1446,7 -1447,7 +1446,7 @@@ public repository you might want to rep
  never.
  
  If you run `git repack` again at this point, it will say
 -"Nothing to pack". Once you continue your development and
 +"Nothing new to pack.". Once you continue your development and
  accumulate the changes, running `git repack` again will create a
  new pack, that contains objects created since you packed your
  repository the last time. We recommend that you pack your project
@@@ -1511,7 -1512,7 +1511,7 @@@ You can repack this private repository 
  6. Push your changes to the public repository, and announce it
     to the public.
  
- 7. Every once in a while, "git-repack" the public repository.
+ 7. Every once in a while, 'git-repack' the public repository.
     Go back to step 5. and continue working.
  
  
@@@ -1692,7 -1693,6 +1692,7 @@@ SEE ALS
  linkgit:gittutorial[7],
  linkgit:gittutorial-2[7],
  linkgit:gitcvs-migration[7],
 +linkgit:git-help[1],
  link:everyday.html[Everyday git],
  link:user-manual.html[The Git User's Manual]
  
diff --combined Documentation/gitk.txt
index 317f6317c254f0ec51c42c5485123aeb91ccd7c6,5ef3687e3941adaeb652b161d7488aaed942d9d3..4673a75a98da1f778c1323d8e5a4f7faff388589
@@@ -21,7 -21,7 +21,7 @@@ git repository
  
  OPTIONS
  -------
- To control which revisions to shown, the command takes options applicable to
+ To control which revisions to show, the command takes options applicable to
  the 'git-rev-list' command (see linkgit:git-rev-list[1]).
  This manual page describes only the most
  frequently used options.
        Use this instead of explicitly specifying <revs> if the set of
        commits to show may vary between refreshes.
  
 +--select-commit=<ref>::
 +
 +      Automatically select the specified commit after loading the graph.
 +      Default behavior is equivalent to specifying '--select-commit=HEAD'.
 +
  <revs>::
  
        Limit the revisions to show. This can be either a single revision
@@@ -80,7 -75,7 +80,7 @@@ Example
  --------
  gitk v2.6.12.. include/scsi drivers/scsi::
  
-       Show as the changes since version 'v2.6.12' that changed any
+       Show the changes since version 'v2.6.12' that changed any
        file in the include/scsi or drivers/scsi subdirectories
  
  gitk --since="2 weeks ago" \-- gitk::
diff --combined Documentation/i18n.txt
index 2cdacd94cd1b53536d4726e05a9f9f8620e85ec6,c6739663313753db6bb751b2ef7310ca7f8ecd81..708da6ca31c1bc0a714df9f437d6813b016dacb7
@@@ -7,11 -7,11 +7,11 @@@ At the core level, git is character enc
     to be what lstat(2) and creat(2) accepts.  There is no such
     thing as pathname encoding translation.
  
-  - The contents of the blob objects are uninterpreted sequence
+  - The contents of the blob objects are uninterpreted sequences
     of bytes.  There is no encoding translation at the core
     level.
  
-  - The commit log messages are uninterpreted sequence of non-NUL
+  - The commit log messages are uninterpreted sequences of non-NUL
     bytes.
  
  Although we encourage that the commit log messages are encoded
@@@ -37,9 -37,9 +37,9 @@@ of `i18n.commitencoding` in its `encodi
  help other people who look at them later.  Lack of this header
  implies that the commit log message is encoded in UTF-8.
  
 -. 'git-log', 'git-show' and friends looks at the `encoding`
 -  header of a commit object, and tries to re-code the log
 -  message into UTF-8 unless otherwise specified.  You can
 +. 'git-log', 'git-show', 'git-blame' and friends look at the
 +  `encoding` header of a commit object, and try to re-code the
 +  log message into UTF-8 unless otherwise specified.  You can
    specify the desired output encoding with
    `i18n.logoutputencoding` in `.git/config` file, like this:
  +
diff --combined fast-import.c
index 201d4ffa13696f857c3a7cf84ba41fae21c04a7b,d58cca58e5fdbe0838e3622148d7b502affeaebf..171d1783399c970136d55cf86fdb78b5c4edce65
@@@ -43,7 -43,7 +43,7 @@@ Format of STDIN stream
  
    new_tag ::= 'tag' sp tag_str lf
      'from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf
-     'tagger' sp name '<' email '>' when lf
+     ('tagger' sp name '<' email '>' when lf)?
      tag_msg;
    tag_msg ::= data;
  
@@@ -376,7 -376,7 +376,7 @@@ static void dump_marks_helper(FILE *, u
  
  static void write_crash_report(const char *err)
  {
 -      char *loc = git_path("fast_import_crash_%d", getpid());
 +      char *loc = git_path("fast_import_crash_%"PRIuMAX, (uintmax_t) getpid());
        FILE *rpt = fopen(loc, "w");
        struct branch *b;
        unsigned long lu;
        fprintf(stderr, "fast-import: dumping crash report to %s\n", loc);
  
        fprintf(rpt, "fast-import crash report:\n");
 -      fprintf(rpt, "    fast-import process: %d\n", getpid());
 -      fprintf(rpt, "    parent process     : %d\n", getppid());
 +      fprintf(rpt, "    fast-import process: %"PRIuMAX"\n", (uintmax_t) getpid());
 +      fprintf(rpt, "    parent process     : %"PRIuMAX"\n", (uintmax_t) getppid());
        fprintf(rpt, "    at %s\n", show_date(time(NULL), 0, DATE_LOCAL));
        fputc('\n', rpt);
  
@@@ -554,10 -554,6 +554,10 @@@ static void *pool_alloc(size_t len
        struct mem_pool *p;
        void *r;
  
 +      /* round up to a 'uintmax_t' alignment */
 +      if (len & (sizeof(uintmax_t) - 1))
 +              len += sizeof(uintmax_t) - (len & (sizeof(uintmax_t) - 1));
 +
        for (p = mem_pool; p; p = p->next_pool)
                if ((p->end - p->next_free >= len))
                        break;
        }
  
        r = p->next_free;
 -      /* round out to a 'uintmax_t' alignment */
 -      if (len & (sizeof(uintmax_t) - 1))
 -              len += sizeof(uintmax_t) - (len & (sizeof(uintmax_t) - 1));
        p->next_free += len;
        return r;
  }
@@@ -846,7 -845,7 +846,7 @@@ static int oecmp (const void *a_, cons
  static char *create_index(void)
  {
        static char tmpfile[PATH_MAX];
 -      SHA_CTX ctx;
 +      git_SHA_CTX ctx;
        struct sha1file *f;
        struct object_entry **idx, **c, **last, *e;
        struct object_entry_pool *o;
        idx_fd = xmkstemp(tmpfile);
        f = sha1fd(idx_fd, tmpfile);
        sha1write(f, array, 256 * sizeof(int));
 -      SHA1_Init(&ctx);
 +      git_SHA1_Init(&ctx);
        for (c = idx; c != last; c++) {
                uint32_t offset = htonl((*c)->offset);
                sha1write(f, &offset, 4);
                sha1write(f, (*c)->sha1, sizeof((*c)->sha1));
 -              SHA1_Update(&ctx, (*c)->sha1, 20);
 +              git_SHA1_Update(&ctx, (*c)->sha1, 20);
        }
        sha1write(f, pack_data->sha1, sizeof(pack_data->sha1));
        sha1close(f, NULL, CSUM_FSYNC);
        free(idx);
 -      SHA1_Final(pack_data->sha1, &ctx);
 +      git_SHA1_Final(pack_data->sha1, &ctx);
        return tmpfile;
  }
  
@@@ -1036,15 -1035,15 +1036,15 @@@ static int store_object
        unsigned char hdr[96];
        unsigned char sha1[20];
        unsigned long hdrlen, deltalen;
 -      SHA_CTX c;
 +      git_SHA_CTX c;
        z_stream s;
  
        hdrlen = sprintf((char*)hdr,"%s %lu", typename(type),
                (unsigned long)dat->len) + 1;
 -      SHA1_Init(&c);
 -      SHA1_Update(&c, hdr, hdrlen);
 -      SHA1_Update(&c, dat->buf, dat->len);
 -      SHA1_Final(sha1, &c);
 +      git_SHA1_Init(&c);
 +      git_SHA1_Update(&c, hdr, hdrlen);
 +      git_SHA1_Update(&c, dat->buf, dat->len);
 +      git_SHA1_Final(sha1, &c);
        if (sha1out)
                hashcpy(sha1out, sha1);
  
@@@ -2265,23 -2264,27 +2265,27 @@@ static void parse_new_tag(void
        read_next_command();
  
        /* tagger ... */
-       if (prefixcmp(command_buf.buf, "tagger "))
-               die("Expected tagger command, got %s", command_buf.buf);
-       tagger = parse_ident(command_buf.buf + 7);
+       if (!prefixcmp(command_buf.buf, "tagger ")) {
+               tagger = parse_ident(command_buf.buf + 7);
+               read_next_command();
+       } else
+               tagger = NULL;
  
        /* tag payload/message */
-       read_next_command();
        parse_data(&msg);
  
        /* build the tag object */
        strbuf_reset(&new_data);
        strbuf_addf(&new_data,
-               "object %s\n"
-               "type %s\n"
-               "tag %s\n"
-               "tagger %s\n"
-               "\n",
-               sha1_to_hex(sha1), commit_type, t->name, tagger);
+                   "object %s\n"
+                   "type %s\n"
+                   "tag %s\n",
+                   sha1_to_hex(sha1), commit_type, t->name);
+       if (tagger)
+               strbuf_addf(&new_data,
+                           "tagger %s\n", tagger);
+       strbuf_addch(&new_data, '\n');
        strbuf_addbuf(&new_data, &msg);
        free(tagger);
  
diff --combined git-send-email.perl
index 3112f769cd4b86cbb87fbbb14b5a9d8c4b5fc6bd,449d938ba97cf1db47049a2f514bea1ed220044b..61144011d0e4ab2c56ee0607eb7b80fbe3634e10
@@@ -20,14 -20,11 +20,15 @@@ use strict
  use warnings;
  use Term::ReadLine;
  use Getopt::Long;
+ use Text::ParseWords;
  use Data::Dumper;
  use Term::ANSIColor;
 +use File::Temp qw/ tempdir /;
 +use Error qw(:try);
  use Git;
  
 +Getopt::Long::Configure qw/ pass_through /;
 +
  package FakeTerm;
  sub new {
        my ($class, $reason) = @_;
@@@ -42,44 -39,76 +43,44 @@@ package main
  
  sub usage {
        print <<EOT;
 -git send-email [options] <file | directory>...
 -Options:
 -   --from         Specify the "From:" line of the email to be sent.
 -
 -   --to           Specify the primary "To:" line of the email.
 -
 -   --cc           Specify an initial "Cc:" list for the entire series
 -                  of emails.
 -
 -   --cc-cmd       Specify a command to execute per file which adds
 -                  per file specific cc address entries
 -
 -   --bcc          Specify a list of email addresses that should be Bcc:
 -                on all the emails.
 -
 -   --compose      Use \$GIT_EDITOR, core.editor, \$EDITOR, or \$VISUAL to edit
 -                an introductory message for the patch series.
 -
 -   --subject      Specify the initial "Subject:" line.
 -                  Only necessary if --compose is also set.  If --compose
 -                is not set, this will be prompted for.
 -
 -   --in-reply-to  Specify the first "In-Reply-To:" header line.
 -                  Only used if --compose is also set.  If --compose is not
 -                set, this will be prompted for.
 -
 -   --chain-reply-to If set, the replies will all be to the previous
 -                  email sent, rather than to the first email sent.
 -                  Defaults to on.
 -
 -   --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.  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.
 -
 -   --smtp-pass    The password for SMTP-AUTH.
 -
 -   --smtp-encryption Specify 'tls' for STARTTLS encryption, or 'ssl' for SSL.
 -                  Any other value disables the feature.
 -
 -   --smtp-ssl     Synonym for '--smtp-encryption=ssl'.  Deprecated.
 -
 -   --suppress-cc  Suppress the specified category of auto-CC.  The category
 -                can be one of 'author' for the patch author, 'self' to
 -                avoid copying yourself, 'sob' for Signed-off-by lines,
 -                'cccmd' for the output of the cccmd, or 'all' to suppress
 -                all of these.
 -
 -   --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.
 -
 -   --quiet      Make git-send-email less verbose.  One line per email
 -                  should be all that is output.
 -
 -   --dry-run    Do everything except actually send the emails.
 -
 -   --envelope-sender  Specify the envelope sender used to send the emails.
 -
 -   --no-validate      Don't perform any sanity checks on patches.
 +git send-email [options] <file | directory | rev-list options >
 +
 +  Composing:
 +    --from                  <str>  * Email From:
 +    --to                    <str>  * Email To:
 +    --cc                    <str>  * Email Cc:
 +    --bcc                   <str>  * Email Bcc:
 +    --subject               <str>  * Email "Subject:"
 +    --in-reply-to           <str>  * Email "In-Reply-To:"
 +    --annotate                     * Review each patch that will be sent in an editor.
 +    --compose                      * Open an editor for introduction.
 +
 +  Sending:
 +    --envelope-sender       <str>  * Email envelope sender.
 +    --smtp-server       <str:int>  * Outgoing SMTP server to use. The port
 +                                     is optional. Default 'localhost'.
 +    --smtp-server-port      <int>  * Outgoing SMTP server port.
 +    --smtp-user             <str>  * Username for SMTP-AUTH.
 +    --smtp-pass             <str>  * Password for SMTP-AUTH; not necessary.
 +    --smtp-encryption       <str>  * tls or ssl; anything else disables.
 +    --smtp-ssl                     * Deprecated. Use '--smtp-encryption ssl'.
 +
 +  Automating:
 +    --identity              <str>  * Use the sendemail.<id> options.
 +    --cc-cmd                <str>  * Email Cc: via `<str> \$patch_path`
 +    --suppress-cc           <str>  * author, self, sob, cccmd, all.
 +    --[no-]signed-off-by-cc        * Send to Cc: and Signed-off-by:
 +                                     addresses. Default on.
 +    --[no-]suppress-from           * Send to self. Default off.
 +    --[no-]chain-reply-to          * Chain In-Reply-To: fields. Default on.
 +    --[no-]thread                  * Use In-Reply-To: field. Default on.
 +
 +  Administering:
 +    --quiet                        * Output one line of info per email.
 +    --dry-run                      * Don't actually send the emails.
 +    --[no-]validate                * Perform patch sanity checks. Default on.
 +    --[no-]format-patch            * understand any non optional arguments as
 +                                     `git format-patch` ones.
  
  EOT
        exit(1);
@@@ -131,10 -160,12 +132,10 @@@ my $auth
  sub unique_email_list(@);
  sub cleanup_compose_files();
  
 -# Constants (essentially)
 -my $compose_filename = ".msg.$$";
 -
  # Variables we fill in automatically, or via prompting:
  my (@to,@cc,@initial_cc,@bcclist,@xh,
 -      $initial_reply_to,$initial_subject,@files,$author,$sender,$smtp_authpass,$compose,$time);
 +      $initial_reply_to,$initial_subject,@files,
 +      $author,$sender,$smtp_authpass,$annotate,$compose,$time);
  
  my $envelope_sender;
  
@@@ -154,42 -185,19 +155,42 @@@ if ($@) 
  
  # Behavior modification variables
  my ($quiet, $dry_run) = (0, 0);
 +my $format_patch;
 +my $compose_filename = $repo->repo_path() . "/.gitsendemail.msg.$$";
 +
 +# Handle interactive edition of files.
 +my $multiedit;
 +my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
 +sub do_edit {
 +      if (defined($multiedit) && !$multiedit) {
 +              map {
 +                      system('sh', '-c', $editor.' "$@"', $editor, $_);
 +                      if (($? & 127) || ($? >> 8)) {
 +                              die("the editor exited uncleanly, aborting everything");
 +                      }
 +              } @_;
 +      } else {
 +              system('sh', '-c', $editor.' "$@"', $editor, @_);
 +              if (($? & 127) || ($? >> 8)) {
 +                      die("the editor exited uncleanly, aborting everything");
 +              }
 +      }
 +}
  
  # Variables with corresponding config settings
 -my ($thread, $chain_reply_to, $suppress_from, $signed_off_cc, $cc_cmd);
 +my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd);
  my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption);
  my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts);
 -my ($no_validate);
 +my ($validate);
  my (@suppress_cc);
  
  my %config_bool_settings = (
      "thread" => [\$thread, 1],
      "chainreplyto" => [\$chain_reply_to, 1],
      "suppressfrom" => [\$suppress_from, undef],
 -    "signedoffcc" => [\$signed_off_cc, undef],
 +    "signedoffbycc" => [\$signed_off_by_cc, undef],
 +    "signedoffcc" => [\$signed_off_by_cc, undef],      # Deprecated
 +    "validate" => [\$validate, 1],
  );
  
  my %config_settings = (
      "aliasesfile" => \@alias_files,
      "suppresscc" => \@suppress_cc,
      "envelopesender" => \$envelope_sender,
 +    "multiedit" => \$multiedit,
  );
  
  # Handle Uncouth Termination
@@@ -248,18 -255,16 +249,18 @@@ my $rc = GetOptions("sender|from=s" => 
                    "smtp-ssl" => sub { $smtp_encryption = 'ssl' },
                    "smtp-encryption=s" => \$smtp_encryption,
                    "identity=s" => \$identity,
 +                  "annotate" => \$annotate,
                    "compose" => \$compose,
                    "quiet" => \$quiet,
                    "cc-cmd=s" => \$cc_cmd,
                    "suppress-from!" => \$suppress_from,
                    "suppress-cc=s" => \@suppress_cc,
 -                  "signed-off-cc|signed-off-by-cc!" => \$signed_off_cc,
 +                  "signed-off-cc|signed-off-by-cc!" => \$signed_off_by_cc,
                    "dry-run" => \$dry_run,
                    "envelope-sender=s" => \$envelope_sender,
                    "thread!" => \$thread,
 -                  "no-validate" => \$no_validate,
 +                  "validate!" => \$validate,
 +                  "format-patch!" => \$format_patch,
         );
  
  unless ($rc) {
@@@ -331,7 -336,7 +332,7 @@@ if ($suppress_cc{'all'}) 
  
  # If explicit old-style ones are specified, they trump --suppress-cc.
  $suppress_cc{'self'} = $suppress_from if defined $suppress_from;
 -$suppress_cc{'sob'} = !$signed_off_cc if defined $signed_off_cc;
 +$suppress_cc{'sob'} = !$signed_off_by_cc if defined $signed_off_by_cc;
  
  # Debugging, print out the suppressions.
  if (0) {
@@@ -359,6 -364,10 +360,10 @@@ foreach my $entry (@bcclist) 
        die "Comma in --bcclist entry: $entry'\n" unless $entry !~ m/,/;
  }
  
+ sub split_addrs {
+       return parse_line('\s*,\s*', 1, @_);
+ }
  my %aliases;
  my %parse_alias = (
        # multiline formats can be supported in the future
                        my ($alias, $addr) = ($1, $2);
                        $addr =~ s/#.*$//; # mutt allows # comments
                         # commas delimit multiple addresses
-                       $aliases{$alias} = [ split(/\s*,\s*/, $addr) ];
+                       $aliases{$alias} = [ split_addrs($addr) ];
                }}},
        mailrc => sub { my $fh = shift; while (<$fh>) {
                if (/^alias\s+(\S+)\s+(.*)$/) {
                        # spaces delimit multiple addresses
                        $aliases{$1} = [ split(/\s+/, $2) ];
                }}},
 -      pine => sub { my $fh = shift; while (<$fh>) {
 -              if (/^(\S+)\t.*\t(.*)$/) {
 +      pine => sub { my $fh = shift; my $f='\t[^\t]*';
 +              for (my $x = ''; defined($x); $x = $_) {
 +                      chomp $x;
 +                      $x .= $1 while(defined($_ = <$fh>) && /^ +(.*)$/);
 +                      $x =~ /^(\S+)$f\t\(?([^\t]+?)\)?(:?$f){0,2}$/ or next;
-                       $aliases{$1} = [ split(/\s*,\s*/, $2) ];
+                       $aliases{$1} = [ split_addrs($2) ];
 -              }}},
 +              }},
        gnus => sub { my $fh = shift; while (<$fh>) {
                if (/\(define-mail-alias\s+"(\S+?)"\s+"(\S+?)"\)/) {
                        $aliases{$1} = [ $2 ];
@@@ -397,53 -403,24 +402,53 @@@ if (@alias_files and $aliasfiletype an
  
  ($sender) = expand_aliases($sender) if defined $sender;
  
 +# returns 1 if the conflict must be solved using it as a format-patch argument
 +sub check_file_rev_conflict($) {
 +      my $f = shift;
 +      try {
 +              $repo->command('rev-parse', '--verify', '--quiet', $f);
 +              if (defined($format_patch)) {
 +                      print "foo\n";
 +                      return $format_patch;
 +              }
 +              die(<<EOF);
 +File '$f' exists but it could also be the range of commits
 +to produce patches for.  Please disambiguate by...
 +
 +    * Saying "./$f" if you mean a file; or
 +    * Giving --format-patch option if you mean a range.
 +EOF
 +      } catch Git::Error::Command with {
 +              return 0;
 +      }
 +}
 +
  # Now that all the defaults are set, process the rest of the command line
  # arguments and collect up the files that need to be processed.
 -for my $f (@ARGV) {
 -      if (-d $f) {
 +my @rev_list_opts;
 +while (defined(my $f = shift @ARGV)) {
 +      if ($f eq "--") {
 +              push @rev_list_opts, "--", @ARGV;
 +              @ARGV = ();
 +      } elsif (-d $f and !check_file_rev_conflict($f)) {
                opendir(DH,$f)
                        or die "Failed to opendir $f: $!";
  
                push @files, grep { -f $_ } map { +$f . "/" . $_ }
                                sort readdir(DH);
                closedir(DH);
 -      } elsif (-f $f or -p $f) {
 +      } elsif ((-f $f or -p $f) and !check_file_rev_conflict($f)) {
                push @files, $f;
        } else {
 -              print STDERR "Skipping $f - not found.\n";
 +              push @rev_list_opts, $f;
        }
  }
  
 -if (!$no_validate) {
 +if (@rev_list_opts) {
 +      push @files, $repo->command('format-patch', '-o', tempdir(CLEANUP => 1), @rev_list_opts);
 +}
 +
 +if ($validate) {
        foreach my $f (@files) {
                unless (-p $f) {
                        my $error = validate_patch($f);
@@@ -461,108 -438,6 +466,108 @@@ if (@files) 
        usage();
  }
  
 +sub get_patch_subject($) {
 +      my $fn = shift;
 +      open (my $fh, '<', $fn);
 +      while (my $line = <$fh>) {
 +              next unless ($line =~ /^Subject: (.*)$/);
 +              close $fh;
 +              return "GIT: $1\n";
 +      }
 +      close $fh;
 +      die "No subject line in $fn ?";
 +}
 +
 +if ($compose) {
 +      # Note that this does not need to be secure, but we will make a small
 +      # effort to have it be unique
 +      open(C,">",$compose_filename)
 +              or die "Failed to open for writing $compose_filename: $!";
 +
 +
 +      my $tpl_sender = $sender || $repoauthor || $repocommitter || '';
 +      my $tpl_subject = $initial_subject || '';
 +      my $tpl_reply_to = $initial_reply_to || '';
 +
 +      print C <<EOT;
 +From $tpl_sender # This line is ignored.
 +GIT: Lines beginning in "GIT: " will be removed.
 +GIT: Consider including an overall diffstat or table of contents
 +GIT: for the patch you are writing.
 +GIT:
 +GIT: Clear the body content if you don't wish to send a summary.
 +From: $tpl_sender
 +Subject: $tpl_subject
 +In-Reply-To: $tpl_reply_to
 +
 +EOT
 +      for my $f (@files) {
 +              print C get_patch_subject($f);
 +      }
 +      close(C);
 +
 +      my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
 +
 +      if ($annotate) {
 +              do_edit($compose_filename, @files);
 +      } else {
 +              do_edit($compose_filename);
 +      }
 +
 +      open(C2,">",$compose_filename . ".final")
 +              or die "Failed to open $compose_filename.final : " . $!;
 +
 +      open(C,"<",$compose_filename)
 +              or die "Failed to open $compose_filename : " . $!;
 +
 +      my $need_8bit_cte = file_has_nonascii($compose_filename);
 +      my $in_body = 0;
 +      my $summary_empty = 1;
 +      while(<C>) {
 +              next if m/^GIT: /;
 +              if ($in_body) {
 +                      $summary_empty = 0 unless (/^\n$/);
 +              } elsif (/^\n$/) {
 +                      $in_body = 1;
 +                      if ($need_8bit_cte) {
 +                              print C2 "MIME-Version: 1.0\n",
 +                                       "Content-Type: text/plain; ",
 +                                         "charset=utf-8\n",
 +                                       "Content-Transfer-Encoding: 8bit\n";
 +                      }
 +              } elsif (/^MIME-Version:/i) {
 +                      $need_8bit_cte = 0;
 +              } elsif (/^Subject:\s*(.+)\s*$/i) {
 +                      $initial_subject = $1;
 +                      my $subject = $initial_subject;
 +                      $_ = "Subject: " .
 +                              ($subject =~ /[^[:ascii:]]/ ?
 +                               quote_rfc2047($subject) :
 +                               $subject) .
 +                              "\n";
 +              } elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
 +                      $initial_reply_to = $1;
 +                      next;
 +              } elsif (/^From:\s*(.+)\s*$/i) {
 +                      $sender = $1;
 +                      next;
 +              } elsif (/^(?:To|Cc|Bcc):/i) {
 +                      print "To/Cc/Bcc fields are not interpreted yet, they have been ignored\n";
 +                      next;
 +              }
 +              print C2 $_;
 +      }
 +      close(C);
 +      close(C2);
 +
 +      if ($summary_empty) {
 +              print "Summary email is empty, skipping it\n";
 +              $compose = -1;
 +      }
 +} elsif ($annotate) {
 +      do_edit(@files);
 +}
 +
  my $prompting = 0;
  if (!defined $sender) {
        $sender = $repoauthor || $repocommitter || '';
@@@ -588,7 -463,7 +593,7 @@@ if (!@to) 
        }
  
        my $to = $_;
-       push @to, split /,\s*/, $to;
+       push @to, split_addrs($to);
        $prompting++;
  }
  
@@@ -607,6 -482,17 +612,6 @@@ sub expand_aliases 
  @initial_cc = expand_aliases(@initial_cc);
  @bcclist = expand_aliases(@bcclist);
  
 -if (!defined $initial_subject && $compose) {
 -      while (1) {
 -              $_ = $term->readline("What subject should the initial email start with? ", $initial_subject);
 -              last if defined $_;
 -              print "\n";
 -      }
 -
 -      $initial_subject = $_;
 -      $prompting++;
 -}
 -
  if ($thread && !defined $initial_reply_to && $prompting) {
        while (1) {
                $_= $term->readline("Message-ID to be used as In-Reply-To for the first email? ", $initial_reply_to);
@@@ -633,6 -519,59 +638,6 @@@ if (!defined $smtp_server) 
  }
  
  if ($compose) {
 -      # Note that this does not need to be secure, but we will make a small
 -      # effort to have it be unique
 -      open(C,">",$compose_filename)
 -              or die "Failed to open for writing $compose_filename: $!";
 -      print C "From $sender # This line is ignored.\n";
 -      printf C "Subject: %s\n\n", $initial_subject;
 -      printf C <<EOT;
 -GIT: Please enter your email below.
 -GIT: Lines beginning in "GIT: " will be removed.
 -GIT: Consider including an overall diffstat or table of contents
 -GIT: for the patch you are writing.
 -
 -EOT
 -      close(C);
 -
 -      my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
 -      system('sh', '-c', $editor.' "$@"', $editor, $compose_filename);
 -
 -      open(C2,">",$compose_filename . ".final")
 -              or die "Failed to open $compose_filename.final : " . $!;
 -
 -      open(C,"<",$compose_filename)
 -              or die "Failed to open $compose_filename : " . $!;
 -
 -      my $need_8bit_cte = file_has_nonascii($compose_filename);
 -      my $in_body = 0;
 -      while(<C>) {
 -              next if m/^GIT: /;
 -              if (!$in_body && /^\n$/) {
 -                      $in_body = 1;
 -                      if ($need_8bit_cte) {
 -                              print C2 "MIME-Version: 1.0\n",
 -                                       "Content-Type: text/plain; ",
 -                                         "charset=utf-8\n",
 -                                       "Content-Transfer-Encoding: 8bit\n";
 -                      }
 -              }
 -              if (!$in_body && /^MIME-Version:/i) {
 -                      $need_8bit_cte = 0;
 -              }
 -              if (!$in_body && /^Subject: ?(.*)/i) {
 -                      my $subject = $1;
 -                      $_ = "Subject: " .
 -                              ($subject =~ /[^[:ascii:]]/ ?
 -                               quote_rfc2047($subject) :
 -                               $subject) .
 -                              "\n";
 -              }
 -              print C2 $_;
 -      }
 -      close(C);
 -      close(C2);
 -
        while (1) {
                $_ = $term->readline("Send this email? (y|n) ");
                last if defined $_;
                exit(0);
        }
  
 -      @files = ($compose_filename . ".final", @files);
 +      if ($compose > 0) {
 +              @files = ($compose_filename . ".final", @files);
 +      }
  }
  
  # Variables we set as part of the loop over files
diff --combined t/t9300-fast-import.sh
index 91b5aced1bcea74a66c3365e4aceff355d577f6a,8b79de5b6345680bfc0a50f02a0334ddc67186c2..821be7ce8d92f8ead1bcaa946260e8d715784612
@@@ -3,9 -3,9 +3,9 @@@
  # Copyright (c) 2007 Shawn Pearce
  #
  
 -test_description='test git-fast-import utility'
 +test_description='test git fast-import utility'
  . ./test-lib.sh
 -. ../diff-lib.sh ;# test-lib chdir's into trash
 +. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
  
  file2_data='file2
  second line of EOF'
@@@ -56,10 -56,16 +56,16 @@@ M 644 :2 file
  M 644 :3 file3
  M 755 :4 file4
  
+ tag series-A
+ from :5
+ data <<EOF
+ An annotated tag without a tagger
+ EOF
  INPUT_END
  test_expect_success \
      'A: create pack from stdin' \
 -    'git-fast-import --export-marks=marks.out <input &&
 +    'git fast-import --export-marks=marks.out <input &&
         git whatchanged master'
  test_expect_success \
        'A: verify pack' \
@@@ -101,6 -107,18 +107,18 @@@ test_expect_success 
        'A: verify file4' \
        'git cat-file blob master:file4 >actual && test_cmp expect actual'
  
+ cat >expect <<EOF
+ object $(git rev-parse refs/heads/master)
+ type commit
+ tag series-A
+ An annotated tag without a tagger
+ EOF
+ test_expect_success 'A: verify tag/series-A' '
+       git cat-file tag tags/series-A >actual &&
+       test_cmp expect actual
+ '
  cat >expect <<EOF
  :2 `git rev-parse --verify master:file2`
  :3 `git rev-parse --verify master:file3`
@@@ -113,7 -131,7 +131,7 @@@ test_expect_success 
  
  test_expect_success \
        'A: verify marks import' \
 -      'git-fast-import \
 +      'git fast-import \
                --import-marks=marks.out \
                --export-marks=marks.new \
                </dev/null &&
@@@ -133,7 -151,7 +151,7 @@@ M 755 :2 copy-of-file
  INPUT_END
  test_expect_success \
        'A: verify marks import does not crash' \
 -      'git-fast-import --import-marks=marks.out <input &&
 +      'git fast-import --import-marks=marks.out <input &&
         git whatchanged verify--import-marks'
  test_expect_success \
        'A: verify pack' \
@@@ -166,7 -184,7 +184,7 @@@ M 755 000000000000000000000000000000000
  
  INPUT_END
  test_expect_success 'B: fail on invalid blob sha1' '
 -    test_must_fail git-fast-import <input
 +    test_must_fail git fast-import <input
  '
  rm -f .git/objects/pack_* .git/objects/index_*
  
@@@ -181,7 -199,7 +199,7 @@@ from refs/heads/maste
  
  INPUT_END
  test_expect_success 'B: fail on invalid branch name ".badbranchname"' '
 -    test_must_fail git-fast-import <input
 +    test_must_fail git fast-import <input
  '
  rm -f .git/objects/pack_* .git/objects/index_*
  
@@@ -196,7 -214,7 +214,7 @@@ from refs/heads/maste
  
  INPUT_END
  test_expect_success 'B: fail on invalid branch name "bad[branch]name"' '
 -    test_must_fail git-fast-import <input
 +    test_must_fail git fast-import <input
  '
  rm -f .git/objects/pack_* .git/objects/index_*
  
@@@ -212,7 -230,7 +230,7 @@@ from refs/heads/maste
  INPUT_END
  test_expect_success \
      'B: accept branch name "TEMP_TAG"' \
 -    'git-fast-import <input &&
 +    'git fast-import <input &&
         test -f .git/TEMP_TAG &&
         test `git rev-parse master` = `git rev-parse TEMP_TAG^`'
  rm -f .git/TEMP_TAG
  ### series C
  ###
  
 -newf=`echo hi newf | git-hash-object -w --stdin`
 +newf=`echo hi newf | git hash-object -w --stdin`
  oldf=`git rev-parse --verify master:file2`
  test_tick
  cat >input <<INPUT_END
@@@ -239,7 -257,7 +257,7 @@@ D file
  INPUT_END
  test_expect_success \
      'C: incremental import create pack from stdin' \
 -    'git-fast-import <input &&
 +    'git fast-import <input &&
         git whatchanged branch'
  test_expect_success \
        'C: verify pack' \
@@@ -297,7 -315,7 +315,7 @@@ EO
  INPUT_END
  test_expect_success \
      'D: inline data in commit' \
 -    'git-fast-import <input &&
 +    'git fast-import <input &&
         git whatchanged branch'
  test_expect_success \
        'D: verify pack' \
@@@ -340,11 -358,11 +358,11 @@@ from refs/heads/branch^
  
  INPUT_END
  test_expect_success 'E: rfc2822 date, --date-format=raw' '
 -    test_must_fail git-fast-import --date-format=raw <input
 +    test_must_fail git fast-import --date-format=raw <input
  '
  test_expect_success \
      'E: rfc2822 date, --date-format=rfc2822' \
 -    'git-fast-import --date-format=rfc2822 <input'
 +    'git fast-import --date-format=rfc2822 <input'
  test_expect_success \
        'E: verify pack' \
        'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
@@@ -381,7 -399,7 +399,7 @@@ from refs/heads/branc
  INPUT_END
  test_expect_success \
      'F: non-fast-forward update skips' \
 -    'if git-fast-import <input
 +    'if git fast-import <input
         then
                echo BAD gfi did not fail
                return 1
@@@ -431,7 -449,7 +449,7 @@@ from refs/heads/branch~
  INPUT_END
  test_expect_success \
      'G: non-fast-forward update forced' \
 -    'git-fast-import --force <input'
 +    'git fast-import --force <input'
  test_expect_success \
        'G: verify pack' \
        'for p in .git/objects/pack/*.pack;do git verify-pack $p||exit;done'
@@@ -467,7 -485,7 +485,7 @@@ EO
  INPUT_END
  test_expect_success \
      'H: deletall, add 1' \
 -    'git-fast-import <input &&
 +    'git fast-import <input &&
         git whatchanged H'
  test_expect_success \
        'H: verify pack' \
@@@ -507,7 -525,7 +525,7 @@@ from refs/heads/branc
  INPUT_END
  test_expect_success \
      'I: export-pack-edges' \
 -    'git-fast-import --export-pack-edges=edges.list <input'
 +    'git fast-import --export-pack-edges=edges.list <input'
  
  cat >expect <<EOF
  .git/objects/pack/pack-.pack: `git rev-parse --verify export-boundary`
@@@ -541,7 -559,7 +559,7 @@@ COMMI
  INPUT_END
  test_expect_success \
      'J: reset existing branch creates empty commit' \
 -    'git-fast-import <input'
 +    'git fast-import <input'
  test_expect_success \
        'J: branch has 1 commit, empty tree' \
        'test 1 = `git rev-list J | wc -l` &&
@@@ -571,7 -589,7 +589,7 @@@ from refs/heads/branch^
  INPUT_END
  test_expect_success \
      'K: reinit branch with from' \
 -    'git-fast-import <input'
 +    'git fast-import <input'
  test_expect_success \
      'K: verify K^1 = branch^1' \
      'test `git rev-parse --verify branch^1` \
@@@ -623,7 -641,7 +641,7 @@@ EXPECT_EN
  
  test_expect_success \
      'L: verify internal tree sorting' \
 -      'git-fast-import <input &&
 +      'git fast-import <input &&
         git diff-tree --abbrev --raw L^ L >output &&
         test_cmp expect output'
  
@@@ -649,7 -667,7 +667,7 @@@ cat >expect <<EO
  EOF
  test_expect_success \
        'M: rename file in same subdirectory' \
 -      'git-fast-import <input &&
 +      'git fast-import <input &&
         git diff-tree -M -r M1^ M1 >actual &&
         compare_diff_raw expect actual'
  
@@@ -670,7 -688,7 +688,7 @@@ cat >expect <<EO
  EOF
  test_expect_success \
        'M: rename file to new subdirectory' \
 -      'git-fast-import <input &&
 +      'git fast-import <input &&
         git diff-tree -M -r M2^ M2 >actual &&
         compare_diff_raw expect actual'
  
@@@ -691,7 -709,7 +709,7 @@@ cat >expect <<EO
  EOF
  test_expect_success \
        'M: rename subdirectory to new subdirectory' \
 -      'git-fast-import <input &&
 +      'git fast-import <input &&
         git diff-tree -M -r M3^ M3 >actual &&
         compare_diff_raw expect actual'
  
@@@ -717,7 -735,7 +735,7 @@@ cat >expect <<EO
  EOF
  test_expect_success \
        'N: copy file in same subdirectory' \
 -      'git-fast-import <input &&
 +      'git fast-import <input &&
         git diff-tree -C --find-copies-harder -r N1^ N1 >actual &&
         compare_diff_raw expect actual'
  
@@@ -751,7 -769,7 +769,7 @@@ cat >expect <<EO
  EOF
  test_expect_success \
        'N: copy then modify subdirectory' \
 -      'git-fast-import <input &&
 +      'git fast-import <input &&
         git diff-tree -C --find-copies-harder -r N2^^ N2 >actual &&
         compare_diff_raw expect actual'
  
@@@ -775,8 -793,8 +793,8 @@@ INPUT_EN
  
  test_expect_success \
        'N: copy dirty subdirectory' \
 -      'git-fast-import <input &&
 -       test `git-rev-parse N2^{tree}` = `git-rev-parse N3^{tree}`'
 +      'git fast-import <input &&
 +       test `git rev-parse N2^{tree}` = `git rev-parse N3^{tree}`'
  
  ###
  ### series O
@@@ -815,8 -833,8 +833,8 @@@ INPUT_EN
  
  test_expect_success \
        'O: comments are all skipped' \
 -      'git-fast-import <input &&
 -       test `git-rev-parse N3` = `git-rev-parse O1`'
 +      'git fast-import <input &&
 +       test `git rev-parse N3` = `git rev-parse O1`'
  
  cat >input <<INPUT_END
  commit refs/heads/O2
@@@ -836,8 -854,8 +854,8 @@@ INPUT_EN
  
  test_expect_success \
        'O: blank lines not necessary after data commands' \
 -      'git-fast-import <input &&
 -       test `git-rev-parse N3` = `git-rev-parse O2`'
 +      'git fast-import <input &&
 +       test `git rev-parse N3` = `git rev-parse O2`'
  
  test_expect_success \
        'O: repack before next test' \
@@@ -881,7 -899,7 +899,7 @@@ commit
  INPUT_END
  test_expect_success \
        'O: blank lines not necessary after other commands' \
 -      'git-fast-import <input &&
 +      'git fast-import <input &&
         test 8 = `find .git/objects/pack -type f | wc -l` &&
         test `git rev-parse refs/tags/O3-2nd` = `git rev-parse O3^` &&
         git log --reverse --pretty=oneline O3 | sed s/^.*z// >actual &&
@@@ -914,7 -932,7 +932,7 @@@ progress I'm done
  INPUT_END
  test_expect_success \
        'O: progress outputs as requested by input' \
 -      'git-fast-import <input >actual &&
 +      'git fast-import <input >actual &&
         grep "progress " <input >expect &&
         test_cmp expect actual'
  
@@@ -979,7 -997,7 +997,7 @@@ INPUT_EN
  
  test_expect_success \
        'P: supermodule & submodule mix' \
 -      'git-fast-import <input &&
 +      'git fast-import <input &&
         git checkout subuse1 &&
         rm -rf sub && mkdir sub && cd sub &&
         git init &&
         git submodule init &&
         git submodule update'
  
 -SUBLAST=$(git-rev-parse --verify sub)
 -SUBPREV=$(git-rev-parse --verify sub^)
 +SUBLAST=$(git rev-parse --verify sub)
 +SUBPREV=$(git rev-parse --verify sub^)
  
  cat >input <<INPUT_END
  blob
@@@ -1024,8 -1042,8 +1042,8 @@@ test_expect_success 
        'P: verbatim SHA gitlinks' \
        'git branch -D sub &&
         git gc && git prune &&
 -       git-fast-import <input &&
 -       test $(git-rev-parse --verify subuse2) = $(git-rev-parse --verify subuse1)'
 +       git fast-import <input &&
 +       test $(git rev-parse --verify subuse2) = $(git rev-parse --verify subuse1)'
  
  test_tick
  cat >input <<INPUT_END
@@@ -1045,7 -1063,7 +1063,7 @@@ DAT
  INPUT_END
  
  test_expect_success 'P: fail on inline gitlink' '
 -    test_must_fail git-fast-import <input'
 +    test_must_fail git fast-import <input'
  
  test_tick
  cat >input <<INPUT_END
@@@ -1068,6 -1086,6 +1086,6 @@@ M 160000 :1 su
  INPUT_END
  
  test_expect_success 'P: fail on blob mark in gitlink' '
 -    test_must_fail git-fast-import <input'
 +    test_must_fail git fast-import <input'
  
  test_done
diff --combined t/t9301-fast-export.sh
index 20574354627f1e46b36a8eba0731138462482386,3a6509a1c87671ab92598fc971891100159c42d4..99857210555288cf5ca8f4fa4d1e94bfe557fbd2
@@@ -3,7 -3,7 +3,7 @@@
  # Copyright (c) 2007 Johannes E. Schindelin
  #
  
 -test_description='git-fast-export'
 +test_description='git fast-export'
  . ./test-lib.sh
  
  test_expect_success 'setup' '
@@@ -67,7 -67,7 +67,7 @@@ test_expect_success 'iso-8859-1' 
  
        git config i18n.commitencoding ISO-8859-1 &&
        # use author and committer name in ISO-8859-1 to match it.
 -      . ../t3901-8859-1.txt &&
 +      . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
        test_tick &&
        echo rosten >file &&
        git commit -s -m den file &&
@@@ -239,4 -239,24 +239,24 @@@ test_expect_success 'fast-export | fast
  
  '
  
+ cat > tag-content << EOF
+ object $(git rev-parse HEAD)
+ type commit
+ tag rosten
+ EOF
+ test_expect_success 'cope with tagger-less tags' '
+       TAG=$(git hash-object -t tag -w tag-content) &&
+       git update-ref refs/tags/sonnenschein $TAG &&
+       git fast-export -C -C --signed-tags=strip --all > output &&
+       test $(grep -c "^tag " output) = 4 &&
+       ! grep "Unspecified Tagger" output &&
+       git fast-export -C -C --signed-tags=strip --all \
+               --fake-missing-tagger > output &&
+       test $(grep -c "^tag " output) = 4 &&
+       grep "Unspecified Tagger" output
+ '
  test_done