Merge branch 'master' into js/fmt-patch
authorJunio C Hamano <junkio@cox.net>
Sun, 21 May 2006 08:34:54 +0000 (01:34 -0700)
committerJunio C Hamano <junkio@cox.net>
Sun, 21 May 2006 08:34:54 +0000 (01:34 -0700)
* master: (119 commits)
diff family: add --check option
Document that "git add" only adds non-ignored files.
Add a conversion tool to migrate remote information into the config
fetch, pull: ask config for remote information
Fix build procedure for builtin-init-db
read-tree -m -u: do not overwrite or remove untracked working tree files.
apply --cached: do not check newly added file in the working tree
Implement a --dry-run option to git-quiltimport
Implement git-quiltimport
Revert "builtin-grep: workaround for non GNU grep."
builtin-grep: workaround for non GNU grep.
builtin-grep: workaround for non GNU grep.
git-am: use apply --cached
apply --cached: apply a patch without using working tree.
apply --numstat: show new name, not old name.
Documentation/Makefile: create tarballs for the man pages and html files
Allow pickaxe and diff-filter options to be used by git log.
Libify the index refresh logic
Builtin git-init-db
Remove unnecessary local in get_ref_sha1.
...

1  2 
builtin-diff.c
builtin-log.c
builtin-rev-list.c
builtin.h
cache.h
commit.c
git.c
diff --combined builtin-diff.c
index 20873162f98478b60b069e97305ab34425c5dde4,de81b05e32f884053060448bc59c83ff70961c24..27451d56134e08364c33c26d5074693dd4437d31
@@@ -84,8 -84,7 +84,7 @@@ static void stuff_change(struct diff_op
  
        if (opt->reverse_diff) {
                unsigned tmp;
-               const
-                       const unsigned char *tmp_u;
+               const unsigned char *tmp_u;
                const char *tmp_c;
                tmp = old_mode; old_mode = new_mode; new_mode = tmp;
                tmp_u = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_u;
@@@ -123,7 -122,7 +122,7 @@@ static int builtin_diff_b_f(struct rev_
        stuff_change(&revs->diffopt,
                     canon_mode(st.st_mode), canon_mode(st.st_mode),
                     blob[0].sha1, null_sha1,
-                    blob[0].name, path);
+                    path, path);
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
        return 0;
@@@ -133,7 -132,9 +132,9 @@@ static int builtin_diff_blobs(struct re
                              int argc, const char **argv,
                              struct blobinfo *blob)
  {
-       /* Blobs */
+       /* Blobs: the arguments are reversed when setup_revisions()
+        * picked them up.
+        */
        unsigned mode = canon_mode(S_IFREG | 0644);
  
        while (1 < argc) {
        }
        stuff_change(&revs->diffopt,
                     mode, mode,
-                    blob[0].sha1, blob[1].sha1,
-                    blob[1].name, blob[1].name);
+                    blob[1].sha1, blob[0].sha1,
+                    blob[0].name, blob[0].name);
        diffcore_std(&revs->diffopt);
        diff_flush(&revs->diffopt);
        return 0;
@@@ -232,7 -233,7 +233,7 @@@ static int builtin_diff_combined(struc
        return 0;
  }
  
 -static void add_head(struct rev_info *revs)
 +void add_head(struct rev_info *revs)
  {
        unsigned char sha1[20];
        struct object *obj;
diff --combined builtin-log.c
index d5bbc1cc06931974de46078b5cac17e315177e40,c4ceee0f9801dcfbfb6aafe386dad3cc48b31560..12a6d19203f054d892c04873fef0f92a5c2ef0c9
@@@ -9,10 -9,6 +9,10 @@@
  #include "diff.h"
  #include "revision.h"
  #include "log-tree.h"
 +#include "builtin.h"
 +
 +/* this is in builtin-diff.c */
 +void add_head(struct rev_info *revs);
  
  static int cmd_log_wc(int argc, const char **argv, char **envp,
                      struct rev_info *rev)
        rev->commit_format = CMIT_FMT_DEFAULT;
        rev->verbose_header = 1;
        argc = setup_revisions(argc, argv, rev, "HEAD");
+       if (rev->always_show_header) {
+               if (rev->diffopt.pickaxe || rev->diffopt.filter) {
+                       rev->always_show_header = 0;
+                       if (rev->diffopt.output_format == DIFF_FORMAT_RAW)
+                               rev->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;
+               }
+       }
  
        if (argc > 1)
                die("unrecognized argument: %s", argv[1]);
@@@ -71,160 -74,3 +78,160 @@@ int cmd_log(int argc, const char **argv
        rev.diffopt.recursive = 1;
        return cmd_log_wc(argc, argv, envp, &rev);
  }
 +
 +static int istitlechar(char c)
 +{
 +      return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
 +              (c >= '0' && c <= '9') || c == '.' || c == '_';
 +}
 +
 +static FILE *realstdout = NULL;
 +static char *output_directory = NULL;
 +
 +static void reopen_stdout(struct commit *commit, int nr, int keep_subject)
 +{
 +      char filename[1024];
 +      char *sol;
 +      int len = 0;
 +
 +      if (output_directory) {
 +              strncpy(filename, output_directory, 1010);
 +              len = strlen(filename);
 +              if (filename[len - 1] != '/')
 +                      filename[len++] = '/';
 +      }
 +
 +      sprintf(filename + len, "%04d", nr);
 +      len = strlen(filename);
 +
 +      sol = strstr(commit->buffer, "\n\n");
 +      if (sol) {
 +              int j, space = 1;
 +
 +              sol += 2;
 +              /* strip [PATCH] or [PATCH blabla] */
 +              if (!keep_subject && !strncmp(sol, "[PATCH", 6)) {
 +                      char *eos = strchr(sol + 6, ']');
 +                      if (eos) {
 +                              while (isspace(*eos))
 +                                      eos++;
 +                              sol = eos;
 +                      }
 +              }
 +
 +              for (j = 0; len < 1024 - 6 && sol[j] && sol[j] != '\n'; j++) {
 +                      if (istitlechar(sol[j])) {
 +                              if (space) {
 +                                      filename[len++] = '-';
 +                                      space = 0;
 +                              }
 +                              filename[len++] = sol[j];
 +                              if (sol[j] == '.')
 +                                      while (sol[j + 1] == '.')
 +                                              j++;
 +                      } else
 +                              space = 1;
 +              }
 +              while (filename[len - 1] == '.' || filename[len - 1] == '-')
 +                      len--;
 +      }
 +      strcpy(filename + len, ".txt");
 +      fprintf(realstdout, "%s\n", filename);
 +      freopen(filename, "w", stdout);
 +}
 +
 +int cmd_format_patch(int argc, const char **argv, char **envp)
 +{
 +      struct commit *commit;
 +      struct commit **list = NULL;
 +      struct rev_info rev;
 +      int nr = 0, total, i, j;
 +      int use_stdout = 0;
 +      int numbered = 0;
 +      int keep_subject = 0;
 +
 +      init_revisions(&rev);
 +      rev.commit_format = CMIT_FMT_EMAIL;
 +      rev.verbose_header = 1;
 +      rev.diff = 1;
 +      rev.diffopt.with_raw = 0;
 +      rev.diffopt.with_stat = 1;
 +      rev.combine_merges = 0;
 +      rev.ignore_merges = 1;
 +      rev.diffopt.output_format = DIFF_FORMAT_PATCH;
 +
 +      /*
 +       * Parse the arguments before setup_revisions(), or something
 +       * like "git fmt-patch -o a123 HEAD^.." may fail; a123 is
 +       * possibly a valid SHA1.
 +       */
 +      for (i = 1, j = 1; i < argc; i++) {
 +              if (!strcmp(argv[i], "--stdout"))
 +                      use_stdout = 1;
 +              else if (!strcmp(argv[i], "-n") ||
 +                              !strcmp(argv[i], "--numbered"))
 +                      numbered = 1;
 +              else if (!strcmp(argv[i], "-k") ||
 +                              !strcmp(argv[i], "--keep-subject")) {
 +                      keep_subject = 1;
 +                      rev.total = -1;
 +              } else if (!strcmp(argv[i], "-o")) {
 +                      if (argc < 3)
 +                              die ("Which directory?");
 +                      if (mkdir(argv[i + 1], 0777) < 0 && errno != EEXIST)
 +                              die("Could not create directory %s",
 +                                              argv[i + 1]);
 +                      output_directory = strdup(argv[i + 1]);
 +                      i++;
 +              } else
 +                      argv[j++] = argv[i];
 +      }
 +      argc = j;
 +
 +      if (numbered && keep_subject < 0)
 +              die ("-n and -k are mutually exclusive.");
 +
 +      argc = setup_revisions(argc, argv, &rev, "HEAD");
 +      if (argc > 1)
 +              die ("unrecognized argument: %s", argv[1]);
 +
 +      if (rev.pending_objects && rev.pending_objects->next == NULL) {
 +              rev.pending_objects->item->flags |= UNINTERESTING;
 +              add_head(&rev);
 +      }
 +
 +      if (!use_stdout)
 +              realstdout = fdopen(dup(1), "w");
 +
 +      prepare_revision_walk(&rev);
 +      while ((commit = get_revision(&rev)) != NULL) {
 +              /* ignore merges */
 +              if (commit->parents && commit->parents->next)
 +                      continue;
 +              nr++;
 +              list = realloc(list, nr * sizeof(list[0]));
 +              list[nr - 1] = commit;
 +      }
 +      total = nr;
 +      if (numbered)
 +              rev.total = total;
 +      while (0 <= --nr) {
 +              int shown;
 +              commit = list[nr];
 +              rev.nr = total - nr;
 +              if (!use_stdout)
 +                      reopen_stdout(commit, rev.nr, keep_subject);
 +              shown = log_tree_commit(&rev, commit);
 +              free(commit->buffer);
 +              commit->buffer = NULL;
 +              if (shown)
 +                      printf("-- \n%s\n\n", git_version_string);
 +              if (!use_stdout)
 +                      fclose(stdout);
 +      }
 +      if (output_directory)
 +              free(output_directory);
 +      free(list);
 +      return 0;
 +}
 +
diff --combined builtin-rev-list.c
index 0000000000000000000000000000000000000000,446802d37758c0aa5b2ff3ec917b49316d303312..7942297d137aa3ccd6bf5f6b4bca1afa35ae5450
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,358 +1,358 @@@
 -                                  revs.abbrev);
+ #include "cache.h"
+ #include "refs.h"
+ #include "tag.h"
+ #include "commit.h"
+ #include "tree.h"
+ #include "blob.h"
+ #include "tree-walk.h"
+ #include "diff.h"
+ #include "revision.h"
+ #include "builtin.h"
+ /* bits #0-15 in revision.h */
+ #define COUNTED               (1u<<16)
+ static const char rev_list_usage[] =
+ "git-rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
+ "  limiting output:\n"
+ "    --max-count=nr\n"
+ "    --max-age=epoch\n"
+ "    --min-age=epoch\n"
+ "    --sparse\n"
+ "    --no-merges\n"
+ "    --remove-empty\n"
+ "    --all\n"
+ "  ordering output:\n"
+ "    --topo-order\n"
+ "    --date-order\n"
+ "  formatting output:\n"
+ "    --parents\n"
+ "    --objects | --objects-edge\n"
+ "    --unpacked\n"
+ "    --header | --pretty\n"
+ "    --abbrev=nr | --no-abbrev\n"
+ "    --abbrev-commit\n"
+ "  special purpose:\n"
+ "    --bisect"
+ ;
+ static struct rev_info revs;
+ static int bisect_list = 0;
+ static int show_timestamp = 0;
+ static int hdr_termination = 0;
+ static const char *header_prefix;
+ static void show_commit(struct commit *commit)
+ {
+       if (show_timestamp)
+               printf("%lu ", commit->date);
+       if (header_prefix)
+               fputs(header_prefix, stdout);
+       if (commit->object.flags & BOUNDARY)
+               putchar('-');
+       if (revs.abbrev_commit && revs.abbrev)
+               fputs(find_unique_abbrev(commit->object.sha1, revs.abbrev),
+                     stdout);
+       else
+               fputs(sha1_to_hex(commit->object.sha1), stdout);
+       if (revs.parents) {
+               struct commit_list *parents = commit->parents;
+               while (parents) {
+                       struct object *o = &(parents->item->object);
+                       parents = parents->next;
+                       if (o->flags & TMP_MARK)
+                               continue;
+                       printf(" %s", sha1_to_hex(o->sha1));
+                       o->flags |= TMP_MARK;
+               }
+               /* TMP_MARK is a general purpose flag that can
+                * be used locally, but the user should clean
+                * things up after it is done with them.
+                */
+               for (parents = commit->parents;
+                    parents;
+                    parents = parents->next)
+                       parents->item->object.flags &= ~TMP_MARK;
+       }
+       if (revs.commit_format == CMIT_FMT_ONELINE)
+               putchar(' ');
+       else
+               putchar('\n');
+       if (revs.verbose_header) {
+               static char pretty_header[16384];
+               pretty_print_commit(revs.commit_format, commit, ~0,
+                                   pretty_header, sizeof(pretty_header),
++                                  revs.abbrev, NULL);
+               printf("%s%c", pretty_header, hdr_termination);
+       }
+       fflush(stdout);
+ }
+ static struct object_list **process_blob(struct blob *blob,
+                                        struct object_list **p,
+                                        struct name_path *path,
+                                        const char *name)
+ {
+       struct object *obj = &blob->object;
+       if (!revs.blob_objects)
+               return p;
+       if (obj->flags & (UNINTERESTING | SEEN))
+               return p;
+       obj->flags |= SEEN;
+       return add_object(obj, p, path, name);
+ }
+ static struct object_list **process_tree(struct tree *tree,
+                                        struct object_list **p,
+                                        struct name_path *path,
+                                        const char *name)
+ {
+       struct object *obj = &tree->object;
+       struct tree_entry_list *entry;
+       struct name_path me;
+       if (!revs.tree_objects)
+               return p;
+       if (obj->flags & (UNINTERESTING | SEEN))
+               return p;
+       if (parse_tree(tree) < 0)
+               die("bad tree object %s", sha1_to_hex(obj->sha1));
+       obj->flags |= SEEN;
+       p = add_object(obj, p, path, name);
+       me.up = path;
+       me.elem = name;
+       me.elem_len = strlen(name);
+       entry = tree->entries;
+       tree->entries = NULL;
+       while (entry) {
+               struct tree_entry_list *next = entry->next;
+               if (entry->directory)
+                       p = process_tree(entry->item.tree, p, &me, entry->name);
+               else
+                       p = process_blob(entry->item.blob, p, &me, entry->name);
+               free(entry);
+               entry = next;
+       }
+       return p;
+ }
+ static void show_commit_list(struct rev_info *revs)
+ {
+       struct commit *commit;
+       struct object_list *objects = NULL, **p = &objects, *pending;
+       while ((commit = get_revision(revs)) != NULL) {
+               p = process_tree(commit->tree, p, NULL, "");
+               show_commit(commit);
+       }
+       for (pending = revs->pending_objects; pending; pending = pending->next) {
+               struct object *obj = pending->item;
+               const char *name = pending->name;
+               if (obj->flags & (UNINTERESTING | SEEN))
+                       continue;
+               if (obj->type == tag_type) {
+                       obj->flags |= SEEN;
+                       p = add_object(obj, p, NULL, name);
+                       continue;
+               }
+               if (obj->type == tree_type) {
+                       p = process_tree((struct tree *)obj, p, NULL, name);
+                       continue;
+               }
+               if (obj->type == blob_type) {
+                       p = process_blob((struct blob *)obj, p, NULL, name);
+                       continue;
+               }
+               die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
+       }
+       while (objects) {
+               /* An object with name "foo\n0000000..." can be used to
+                * confuse downstream git-pack-objects very badly.
+                */
+               const char *ep = strchr(objects->name, '\n');
+               if (ep) {
+                       printf("%s %.*s\n", sha1_to_hex(objects->item->sha1),
+                              (int) (ep - objects->name),
+                              objects->name);
+               }
+               else
+                       printf("%s %s\n", sha1_to_hex(objects->item->sha1), objects->name);
+               objects = objects->next;
+       }
+ }
+ /*
+  * This is a truly stupid algorithm, but it's only
+  * used for bisection, and we just don't care enough.
+  *
+  * We care just barely enough to avoid recursing for
+  * non-merge entries.
+  */
+ static int count_distance(struct commit_list *entry)
+ {
+       int nr = 0;
+       while (entry) {
+               struct commit *commit = entry->item;
+               struct commit_list *p;
+               if (commit->object.flags & (UNINTERESTING | COUNTED))
+                       break;
+               if (!revs.prune_fn || (commit->object.flags & TREECHANGE))
+                       nr++;
+               commit->object.flags |= COUNTED;
+               p = commit->parents;
+               entry = p;
+               if (p) {
+                       p = p->next;
+                       while (p) {
+                               nr += count_distance(p);
+                               p = p->next;
+                       }
+               }
+       }
+       return nr;
+ }
+ static void clear_distance(struct commit_list *list)
+ {
+       while (list) {
+               struct commit *commit = list->item;
+               commit->object.flags &= ~COUNTED;
+               list = list->next;
+       }
+ }
+ static struct commit_list *find_bisection(struct commit_list *list)
+ {
+       int nr, closest;
+       struct commit_list *p, *best;
+       nr = 0;
+       p = list;
+       while (p) {
+               if (!revs.prune_fn || (p->item->object.flags & TREECHANGE))
+                       nr++;
+               p = p->next;
+       }
+       closest = 0;
+       best = list;
+       for (p = list; p; p = p->next) {
+               int distance;
+               if (revs.prune_fn && !(p->item->object.flags & TREECHANGE))
+                       continue;
+               distance = count_distance(p);
+               clear_distance(list);
+               if (nr - distance < distance)
+                       distance = nr - distance;
+               if (distance > closest) {
+                       best = p;
+                       closest = distance;
+               }
+       }
+       if (best)
+               best->next = NULL;
+       return best;
+ }
+ static void mark_edge_parents_uninteresting(struct commit *commit)
+ {
+       struct commit_list *parents;
+       for (parents = commit->parents; parents; parents = parents->next) {
+               struct commit *parent = parents->item;
+               if (!(parent->object.flags & UNINTERESTING))
+                       continue;
+               mark_tree_uninteresting(parent->tree);
+               if (revs.edge_hint && !(parent->object.flags & SHOWN)) {
+                       parent->object.flags |= SHOWN;
+                       printf("-%s\n", sha1_to_hex(parent->object.sha1));
+               }
+       }
+ }
+ static void mark_edges_uninteresting(struct commit_list *list)
+ {
+       for ( ; list; list = list->next) {
+               struct commit *commit = list->item;
+               if (commit->object.flags & UNINTERESTING) {
+                       mark_tree_uninteresting(commit->tree);
+                       continue;
+               }
+               mark_edge_parents_uninteresting(commit);
+       }
+ }
+ int cmd_rev_list(int argc, const char **argv, char **envp)
+ {
+       struct commit_list *list;
+       int i;
+       init_revisions(&revs);
+       revs.abbrev = 0;
+       revs.commit_format = CMIT_FMT_UNSPECIFIED;
+       argc = setup_revisions(argc, argv, &revs, NULL);
+       for (i = 1 ; i < argc; i++) {
+               const char *arg = argv[i];
+               if (!strcmp(arg, "--header")) {
+                       revs.verbose_header = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--timestamp")) {
+                       show_timestamp = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--bisect")) {
+                       bisect_list = 1;
+                       continue;
+               }
+               usage(rev_list_usage);
+       }
+       if (revs.commit_format != CMIT_FMT_UNSPECIFIED) {
+               /* The command line has a --pretty  */
+               hdr_termination = '\n';
+               if (revs.commit_format == CMIT_FMT_ONELINE)
+                       header_prefix = "";
+               else
+                       header_prefix = "commit ";
+       }
+       else if (revs.verbose_header)
+               /* Only --header was specified */
+               revs.commit_format = CMIT_FMT_RAW;
+       list = revs.commits;
+       if ((!list &&
+            (!(revs.tag_objects||revs.tree_objects||revs.blob_objects) &&
+             !revs.pending_objects)) ||
+           revs.diff)
+               usage(rev_list_usage);
+       save_commit_buffer = revs.verbose_header;
+       track_object_refs = 0;
+       if (bisect_list)
+               revs.limited = 1;
+       prepare_revision_walk(&revs);
+       if (revs.tree_objects)
+               mark_edges_uninteresting(revs.commits);
+       if (bisect_list)
+               revs.commits = find_bisection(revs.commits);
+       show_commit_list(&revs);
+       return 0;
+ }
diff --combined builtin.h
index 635a00bf7a20ef347944f364c5f89e978ce50af9,60541262c462a257cd80d6edb8525b0fbbac0f71..aaddef352a273970885f5c73182ea8592dd182ab
+++ b/builtin.h
@@@ -20,9 -20,12 +20,13 @@@ extern int cmd_whatchanged(int argc, co
  extern int cmd_show(int argc, const char **argv, char **envp);
  extern int cmd_log(int argc, const char **argv, char **envp);
  extern int cmd_diff(int argc, const char **argv, char **envp);
 +extern int cmd_format_patch(int argc, const char **argv, char **envp);
  extern int cmd_count_objects(int argc, const char **argv, char **envp);
  
  extern int cmd_push(int argc, const char **argv, char **envp);
+ extern int cmd_grep(int argc, const char **argv, char **envp);
+ extern int cmd_rev_list(int argc, const char **argv, char **envp);
+ extern int cmd_check_ref_format(int argc, const char **argv, char **envp);
+ extern int cmd_init_db(int argc, const char **argv, char **envp);
  
  #endif
diff --combined cache.h
index 605b80e268684ebdd1f0d707b307ade48ac24d03,afa8e4f0acbd36947590e428fd86ed0cd2000276..73ae0713bd7a991ffa922170a445da48b78212dd
+++ b/cache.h
@@@ -158,6 -158,12 +158,12 @@@ extern int index_pipe(unsigned char *sh
  extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
  extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
  
+ #define REFRESH_REALLY                0x0001  /* ignore_valid */
+ #define REFRESH_UNMERGED      0x0002  /* allow unmerged */
+ #define REFRESH_QUIET         0x0004  /* be quiet about it */
+ #define REFRESH_IGNORE_MISSING        0x0008  /* ignore non-existent */
+ extern int refresh_cache(unsigned int flags);
  struct cache_file {
        struct cache_file *next;
        char lockfile[PATH_MAX];
@@@ -251,7 -257,6 +257,7 @@@ extern void *read_object_with_reference
                                        unsigned char *sha1_ret);
  
  const char *show_date(unsigned long time, int timezone);
 +const char *show_rfc2822_date(unsigned long time, int timezone);
  int parse_date(const char *date, char *buf, int bufsize);
  void datestamp(char *buf, int bufsize);
  unsigned long approxidate(const char *);
@@@ -364,4 -369,8 +370,8 @@@ extern int receive_keep_pack(int fd[2]
  /* pager.c */
  extern void setup_pager(void);
  
+ /* base85 */
+ int decode_85(char *dst, char *line, int linelen);
+ void encode_85(char *buf, unsigned char *data, int bytes);
  #endif /* CACHE_H */
diff --combined commit.c
index 93b3903ea78fac0b5a40074ec64d0c98bb8cfa94,4a26070c137243bf099cf10415b2910eca230368..84558bac29afd64e295a451ad7baa099237ae045
+++ b/commit.c
@@@ -22,25 -22,33 +22,34 @@@ struct sort_nod
  
  const char *commit_type = "commit";
  
+ struct cmt_fmt_map {
+       const char *n;
+       size_t cmp_len;
+       enum cmit_fmt v;
+ } cmt_fmts[] = {
+       { "raw",        1,      CMIT_FMT_RAW },
+       { "medium",     1,      CMIT_FMT_MEDIUM },
+       { "short",      1,      CMIT_FMT_SHORT },
++      { "email",      1,      CMIT_FMT_EMAIL },
+       { "full",       5,      CMIT_FMT_FULL },
+       { "fuller",     5,      CMIT_FMT_FULLER },
+       { "oneline",    1,      CMIT_FMT_ONELINE },
+ };
  enum cmit_fmt get_commit_format(const char *arg)
  {
-       if (!*arg)
+       int i;
+       if (!arg || !*arg)
                return CMIT_FMT_DEFAULT;
-       if (!strcmp(arg, "=raw"))
-               return CMIT_FMT_RAW;
-       if (!strcmp(arg, "=medium"))
-               return CMIT_FMT_MEDIUM;
-       if (!strcmp(arg, "=short"))
-               return CMIT_FMT_SHORT;
-       if (!strcmp(arg, "=full"))
-               return CMIT_FMT_FULL;
-       if (!strcmp(arg, "=fuller"))
-               return CMIT_FMT_FULLER;
-       if (!strcmp(arg, "=email"))
-               return CMIT_FMT_EMAIL;
-       if (!strcmp(arg, "=oneline"))
-               return CMIT_FMT_ONELINE;
-       die("invalid --pretty format");
+       if (*arg == '=')
+               arg++;
+       for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
+               if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len))
+                       return cmt_fmts[i].v;
+       }
+       die("invalid --pretty format: %s", arg);
  }
  
  static struct commit *check_commit(struct object *obj,
@@@ -430,10 -438,6 +439,10 @@@ static int add_user_info(const char *wh
        time = strtoul(date, &date, 10);
        tz = strtol(date, NULL, 10);
  
 +      if (fmt == CMIT_FMT_EMAIL) {
 +              what = "From";
 +              filler = "";
 +      }
        ret = sprintf(buf, "%s: %.*s%.*s\n", what,
                      (fmt == CMIT_FMT_FULLER) ? 4 : 0,
                      filler, namelen, line);
        case CMIT_FMT_MEDIUM:
                ret += sprintf(buf + ret, "Date:   %s\n", show_date(time, tz));
                break;
 +      case CMIT_FMT_EMAIL:
 +              ret += sprintf(buf + ret, "Date: %s\n",
 +                             show_rfc2822_date(time, tz));
 +              break;
        case CMIT_FMT_FULLER:
                ret += sprintf(buf + ret, "%sDate: %s\n", what, show_date(time, tz));
                break;
        return ret;
  }
  
 -static int is_empty_line(const char *line, int len)
 +static int is_empty_line(const char *line, int *len_p)
  {
 +      int len = *len_p;
        while (len && isspace(line[len-1]))
                len--;
 +      *len_p = len;
        return !len;
  }
  
@@@ -469,8 -467,7 +478,8 @@@ static int add_merge_info(enum cmit_fm
        struct commit_list *parent = commit->parents;
        int offset;
  
 -      if ((fmt == CMIT_FMT_ONELINE) || !parent || !parent->next)
 +      if ((fmt == CMIT_FMT_ONELINE) || (fmt == CMIT_FMT_EMAIL) ||
 +          !parent || !parent->next)
                return 0;
  
        offset = sprintf(buf, "Merge:");
        return offset;
  }
  
 -unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev)
 +unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev, const char *subject)
  {
        int hdr = 1, body = 0;
        unsigned long offset = 0;
 -      int indent = (fmt == CMIT_FMT_ONELINE) ? 0 : 4;
 +      int indent = 4;
        int parents_shown = 0;
        const char *msg = commit->buffer;
  
 +      if (fmt == CMIT_FMT_ONELINE || fmt == CMIT_FMT_EMAIL)
 +              indent = 0;
 +
        for (;;) {
                const char *line = msg;
                int linelen = get_one_line(msg, len);
                if (hdr) {
                        if (linelen == 1) {
                                hdr = 0;
 -                              if (fmt != CMIT_FMT_ONELINE)
 +                              if ((fmt != CMIT_FMT_ONELINE) && !subject)
                                        buf[offset++] = '\n';
                                continue;
                        }
                        continue;
                }
  
 -              if (is_empty_line(line, linelen)) {
 +              if (is_empty_line(line, &linelen)) {
                        if (!body)
                                continue;
 +                      if (subject)
 +                              continue;
                        if (fmt == CMIT_FMT_SHORT)
                                break;
                } else {
                        body = 1;
                }
  
 +              if (subject) {
 +                      int slen = strlen(subject);
 +                      memcpy(buf + offset, subject, slen);
 +                      offset += slen;
 +              }
                memset(buf + offset, ' ', indent);
                memcpy(buf + offset + indent, line, linelen);
                offset += linelen + indent;
 +              buf[offset++] = '\n';
                if (fmt == CMIT_FMT_ONELINE)
                        break;
 +              subject = NULL;
        }
        while (offset && isspace(buf[offset-1]))
                offset--;
diff --combined git.c
index a8f7926b4875512b1d93768c1bc6d934111954f6,3216d311b2efdd238f0eb023d6a918e0a43b54ab..f4dff02bd3c2711a2eeac72be7b604bceda1a664
--- 1/git.c
--- 2/git.c
+++ b/git.c
@@@ -47,9 -47,12 +47,13 @@@ static void handle_internal_command(in
                { "whatchanged", cmd_whatchanged },
                { "show", cmd_show },
                { "push", cmd_push },
 +              { "fmt-patch", cmd_format_patch },
                { "count-objects", cmd_count_objects },
                { "diff", cmd_diff },
+               { "grep", cmd_grep },
+               { "rev-list", cmd_rev_list },
+               { "init-db", cmd_init_db },
+               { "check-ref-format", cmd_check_ref_format }
        };
        int i;