Merge branch 'je/hooks'
authorJunio C Hamano <gitster@pobox.com>
Wed, 3 Oct 2007 10:03:40 +0000 (03:03 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 3 Oct 2007 10:03:40 +0000 (03:03 -0700)
* je/hooks:
post-checkout hook, tests, and docs

31 files changed:
Documentation/RelNotes-1.5.3.3.txt
Documentation/RelNotes-1.5.3.4.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-branch.txt
Documentation/git-checkout.txt
Documentation/git-for-each-ref.txt
Documentation/git-rebase.txt
Documentation/git-stash.txt
Documentation/git.txt
INSTALL
builtin-for-each-ref.c
builtin-ls-files.c
cache.h
configure.ac
contrib/emacs/git.el
contrib/hooks/post-receive-email
date.c
diff.c
diffcore-rename.c
diffcore.h
git-add--interactive.perl
git-commit.sh
git-pull.sh
git-rebase--interactive.sh
git-remote.perl
git-submodule.sh
revision.c
t/t3060-ls-files-with-tree.sh [new file with mode: 0755]
t/t3404-rebase-interactive.sh
t/t6300-for-each-ref.sh [new file with mode: 0644]
index e91bd84162456e54a565551eff3101f3eb261f3e..2a7bfdd5cc7b0a9494f3f151ba593146553a4945 100644 (file)
@@ -29,9 +29,3 @@ Fixes since v1.5.3.2
 
  * git-log sometimes invoked underlying "diff" machinery
    unnecessarily.
-
---
-exec >/var/tmp/1
-O=v1.5.3.2-29-gb7bb760
-echo O=`git describe refs/heads/maint`
-git shortlog --no-merges $O..refs/heads/maint
diff --git a/Documentation/RelNotes-1.5.3.4.txt b/Documentation/RelNotes-1.5.3.4.txt
new file mode 100644 (file)
index 0000000..b04b3a4
--- /dev/null
@@ -0,0 +1,35 @@
+GIT v1.5.3.4 Release Notes
+==========================
+
+Fixes since v1.5.3.3
+--------------------
+
+ * Change to "git-ls-files" in v1.5.3.3 that was introduced to support
+   partial commit of removal better had a segfaulting bug, which was
+   diagnosed and fixed by Keith and Carl.
+
+ * Performance improvements for rename detection has been backported
+   from the 'master' branch.
+
+ * "git-for-each-ref --format='%(numparent)'" was not working
+   correctly at all, and --format='%(parent)' was not working for
+   merge commits.
+
+ * Sample "post-receive-hook" incorrectly sent out push
+   notification e-mails marked as "From: " the committer of the
+   commit that happened to be at the tip of the branch that was
+   pushed, not from the person who pushed.
+
+ * "git-remote" did not exit non-zero status upon error.
+
+ * "git-add -i" did not respond very well to EOF from tty nor
+   bogus input.
+
+ * "git-rebase -i" squash subcommand incorrectly made the
+   author of later commit the author of resulting commit,
+   instead of taking from the first one in the squashed series.
+
+ * "git-stash apply --index" was not documented.
+
+ * autoconfiguration learned that "ar" command is found as "gas" on
+   some systems.
index 015910f27a450cdaec80f3bfc2679243126736c0..f59fbb30248d9ac1f8e0d44364957359a3f152cb 100644 (file)
@@ -579,7 +579,7 @@ merge.summary::
 
 merge.tool::
        Controls which merge resolution program is used by
-       gitlink:git-mergetool[l].  Valid values are: "kdiff3", "tkdiff",
+       gitlink:git-mergetool[1].  Valid values are: "kdiff3", "tkdiff",
        "meld", "xxdiff", "emerge", "vimdiff", "gvimdiff", and "opendiff".
 
 merge.verbosity::
index 228ccaf10ab6bf7e4a3909d31ac31afbd2361d8c..b1f528ae8864e429a509365a79d16bb8341620e1 100644 (file)
 
 --ext-diff::
        Allow an external diff helper to be executed. If you set an
-       external diff driver with gitlink:gitattributes(5), you need
-       to use this option with gitlink:git-log(1) and friends.
+       external diff driver with gitlink:gitattributes[5], you need
+       to use this option with gitlink:git-log[1] and friends.
 
 --no-ext-diff::
        Disallow external diff drivers.
index 33bc31b0d4491169cdbfb06f3dc6c42ec84b1365..b7285bcdbc80a2aa574fa13050c00f1ce9975419 100644 (file)
@@ -26,6 +26,10 @@ It will start out with a head equal to the one given as <start-point>.
 If no <start-point> is given, the branch will be created with a head
 equal to that of the currently checked out branch.
 
+Note that this will create the new branch, but it will not switch the
+working tree to it; use "git checkout <newbranch>" to switch to the
+new branch.
+
 When a local branch is started off a remote branch, git can setup the
 branch so that gitlink:git-pull[1] will appropriately merge from that
 remote branch.  If this behavior is desired, it is possible to make it
@@ -91,6 +95,21 @@ OPTIONS
 --no-abbrev::
        Display the full sha1s in output listing rather than abbreviating them.
 
+--track::
+       Set up configuration so that git-pull will automatically
+       retrieve data from the remote branch.  Use this if you always
+       pull from the same remote branch into the new branch, or if you
+       don't want to use "git pull <repository> <refspec>" explicitly.  Set the
+       branch.autosetupmerge configuration variable to true if you
+       want git-checkout and git-branch to always behave as if
+       '--track' were given.
+
+--no-track::
+       When -b is given and a branch is created off a remote branch,
+       set up configuration so that git-pull will not retrieve data
+       from the remote branch, ignoring the branch.autosetupmerge
+       configuration variable.
+
 <branchname>::
        The name of the branch to create or delete.
        The new branch name must pass all checks defined by
index 734928bf96c35fbe3f66c6693415e0a0d05f9412..2e58481ed68b0cc8540b7126a9c5c389e3f67197 100644 (file)
@@ -50,7 +50,9 @@ OPTIONS
 --track::
        When -b is given and a branch is created off a remote branch,
        set up configuration so that git-pull will automatically
-       retrieve data from the remote branch.  Set the
+       retrieve data from the remote branch.  Use this if you always
+       pull from the same remote branch into the new branch, or if you
+       don't want to use "git pull <repository> <refspec>" explicitly.  Set the
        branch.autosetupmerge configuration variable to true if you
        want git-checkout and git-branch to always behave as if
        '--track' were given.
index 6df8e8500450ad65a2de86e3daa0ab2f7692a2d2..f1f90cca62f61327a13b5ab41862596ca24a9b8f 100644 (file)
@@ -100,6 +100,11 @@ In any case, a field name that refers to a field inapplicable to
 the object referred by the ref does not cause an error.  It
 returns an empty string instead.
 
+As a special case for the date-type fields, you may specify a format for
+the date by adding one of `:default`, `:relative`, `:short`, `:local`,
+`:iso8601` or `:rfc2822` to the end of the fieldname; e.g.
+`%(taggerdate:relative)`.
+
 
 EXAMPLES
 --------
index 0858fa8a6326a0c457bc9b4bb63ae35d3001ef65..e8e75790fc21b403f428b8cca72ab42a072e1b38 100644 (file)
@@ -298,7 +298,7 @@ rebasing.
 If you want to fold two or more commits into one, replace the command
 "pick" with "squash" for the second and subsequent commit.  If the
 commits had different authors, it will attribute the squashed commit to
-the author of the last commit.
+the author of the first commit.
 
 In both cases, or when a "pick" does not succeed (because of merge
 errors), the loop will stop to let you fix things, and you can continue
index 05f40cff6cbaa9d8a08176a5a601b7e43d17e5bf..5723bb06f087f62e463b110686b850987103140d 100644 (file)
@@ -63,7 +63,7 @@ show [<stash>]::
        it will accept any format known to `git-diff` (e.g., `git-stash show
        -p stash@\{1}` to view the second most recent stash in patch form).
 
-apply [<stash>]::
+apply [--index] [<stash>]::
 
        Restore the changes recorded in the stash on top of the current
        working tree state.  When no `<stash>` is given, applies the latest
@@ -71,6 +71,11 @@ apply [<stash>]::
 +
 This operation can fail with conflicts; you need to resolve them
 by hand in the working tree.
++
+If the `--index` option is used, then tries to reinstate not only the working
+tree's changes, but also the index's ones. However, this can fail, when you
+have conflicts (which are stored in the index, where you therefore can no
+longer apply the changes as they were originally).
 
 clear::
        Remove all the stashed states. Note that those states will then
index cb59639f777889cb909933886d31ebcce1bbd199..abce801e48386e07fb4014ad596d1b0ad1d382a1 100644 (file)
@@ -46,6 +46,7 @@ Documentation for older releases are available here:
 * link:v1.5.3/git.html[documentation for release 1.5.3]
 
 * release notes for
+  link:RelNotes-1.5.3.3.txt[1.5.3.3],
   link:RelNotes-1.5.3.2.txt[1.5.3.2],
   link:RelNotes-1.5.3.1.txt[1.5.3.1].
 
diff --git a/INSTALL b/INSTALL
index 289b046a443c0647624607d471289b2c7dcd470b..f1eb4049b9f839c1b3d1aa5a4d7387d0e93f0f5c 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -79,6 +79,9 @@ Issues of note:
        - "perl" and POSIX-compliant shells are needed to use most of
          the barebone Porcelainish scripts.
 
+       - "cpio" is used by git-merge for saving and restoring the index,
+         and by git-clone when doing a local (possibly hardlinked) clone.
+
  - Some platform specific issues are dealt with Makefile rules,
    but depending on your specific installation, you may not
    have all the libraries/tools needed, or you may have
index 0afa1c5c41e79a05ddebb7d874956163510daf0b..5dbf3e59f297b1849c1289b7cc7980bddf1852f7 100644 (file)
@@ -43,7 +43,7 @@ static struct {
        { "objectsize", FIELD_ULONG },
        { "objectname" },
        { "tree" },
-       { "parent" }, /* NEEDSWORK: how to address 2nd and later parents? */
+       { "parent" },
        { "numparent", FIELD_ULONG },
        { "object" },
        { "type" },
@@ -106,7 +106,16 @@ static int parse_atom(const char *atom, const char *ep)
        /* Is the atom a valid one? */
        for (i = 0; i < ARRAY_SIZE(valid_atom); i++) {
                int len = strlen(valid_atom[i].name);
-               if (len == ep - sp && !memcmp(valid_atom[i].name, sp, len))
+               /*
+                * If the atom name has a colon, strip it and everything after
+                * it off - it specifies the format for this entry, and
+                * shouldn't be used for checking against the valid_atom
+                * table.
+                */
+               const char *formatp = strchr(sp, ':');
+               if (!formatp || ep < formatp)
+                       formatp = ep;
+               if (len == formatp - sp && !memcmp(valid_atom[i].name, sp, len))
                        break;
        }
 
@@ -262,24 +271,26 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
                }
                if (!strcmp(name, "numparent")) {
                        char *s = xmalloc(40);
+                       v->ul = num_parents(commit);
                        sprintf(s, "%lu", v->ul);
                        v->s = s;
-                       v->ul = num_parents(commit);
                }
                else if (!strcmp(name, "parent")) {
                        int num = num_parents(commit);
                        int i;
                        struct commit_list *parents;
-                       char *s = xmalloc(42 * num);
+                       char *s = xmalloc(41 * num + 1);
                        v->s = s;
                        for (i = 0, parents = commit->parents;
                             parents;
-                            parents = parents->next, i = i + 42) {
+                            parents = parents->next, i = i + 41) {
                                struct commit *parent = parents->item;
                                strcpy(s+i, sha1_to_hex(parent->object.sha1));
                                if (parents->next)
                                        s[i+40] = ' ';
                        }
+                       if (!i)
+                               *s = '\0';
                }
        }
 }
@@ -347,12 +358,26 @@ static const char *copy_email(const char *buf)
        return line;
 }
 
-static void grab_date(const char *buf, struct atom_value *v)
+static void grab_date(const char *buf, struct atom_value *v, const char *atomname)
 {
        const char *eoemail = strstr(buf, "> ");
        char *zone;
        unsigned long timestamp;
        long tz;
+       enum date_mode date_mode = DATE_NORMAL;
+       const char *formatp;
+
+       /*
+        * We got here because atomname ends in "date" or "date<something>";
+        * it's not possible that <something> is not ":<format>" because
+        * parse_atom() wouldn't have allowed it, so we can assume that no
+        * ":" means no format is specified, and use the default.
+        */
+       formatp = strchr(atomname, ':');
+       if (formatp != NULL) {
+               formatp++;
+               date_mode = parse_date_format(formatp);
+       }
 
        if (!eoemail)
                goto bad;
@@ -362,7 +387,7 @@ static void grab_date(const char *buf, struct atom_value *v)
        tz = strtol(zone, NULL, 10);
        if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
                goto bad;
-       v->s = xstrdup(show_date(timestamp, tz, 0));
+       v->s = xstrdup(show_date(timestamp, tz, date_mode));
        v->ul = timestamp;
        return;
  bad:
@@ -389,7 +414,7 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
                if (name[wholen] != 0 &&
                    strcmp(name + wholen, "name") &&
                    strcmp(name + wholen, "email") &&
-                   strcmp(name + wholen, "date"))
+                   prefixcmp(name + wholen, "date"))
                        continue;
                if (!wholine)
                        wholine = find_wholine(who, wholen, buf, sz);
@@ -401,8 +426,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
                        v->s = copy_name(wholine);
                else if (!strcmp(name + wholen, "email"))
                        v->s = copy_email(wholine);
-               else if (!strcmp(name + wholen, "date"))
-                       grab_date(wholine, v);
+               else if (!prefixcmp(name + wholen, "date"))
+                       grab_date(wholine, v, name);
        }
 
        /* For a tag or a commit object, if "creator" or "creatordate" is
@@ -422,8 +447,8 @@ static void grab_person(const char *who, struct atom_value *val, int deref, stru
                if (deref)
                        name++;
 
-               if (!strcmp(name, "creatordate"))
-                       grab_date(wholine, v);
+               if (!prefixcmp(name, "creatordate"))
+                       grab_date(wholine, v, name);
                else if (!strcmp(name, "creator"))
                        v->s = copy_line(wholine);
        }
index 6c1db86e8056285cb25359d55f5ebf9dd0e6f489..171d449048d304b043ed33776fab4bf95434e30a 100644 (file)
@@ -280,7 +280,8 @@ static void prune_cache(const char *prefix)
 
        if (pos < 0)
                pos = -pos-1;
-       active_cache += pos;
+       memmove(active_cache, active_cache + pos,
+               (active_nr - pos) * sizeof(struct cache_entry *));
        active_nr -= pos;
        first = 0;
        last = active_nr;
diff --git a/cache.h b/cache.h
index 824650016677353cfa8c8a140eb3d904f56d60ee..5587f7ebc74a597b54de6014896fcd5481fe4ec9 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -432,6 +432,7 @@ const char *show_date(unsigned long time, int timezone, enum date_mode mode);
 int parse_date(const char *date, char *buf, int bufsize);
 void datestamp(char *buf, int bufsize);
 unsigned long approxidate(const char *);
+enum date_mode parse_date_format(const char *format);
 
 extern const char *git_author_info(int);
 extern const char *git_committer_info(int);
index 84fd7f1e1f2fd670de38a43bfe65d3988fc4767e..ed7cc895d276ac4d7548211a364a4e6952f9b7bd 100644 (file)
@@ -104,7 +104,7 @@ AC_MSG_NOTICE([CHECKS for programs])
 #
 AC_PROG_CC([cc gcc])
 #AC_PROG_INSTALL               # needs install-sh or install.sh in sources
-AC_CHECK_TOOL(AR, ar, :)
+AC_CHECK_TOOLS(AR, [gar ar], :)
 AC_CHECK_PROGS(TAR, [gtar tar])
 # TCLTK_PATH will be set to some value if we want Tcl/Tk
 # or will be empty otherwise.
index 2d77fd47ece21af7b79b3e5806521e208d5d92b0..4286d160a02bdbeff272cc306ab01b85b87b8cbe 100644 (file)
@@ -36,7 +36,6 @@
 ;; TODO
 ;;  - portability to XEmacs
 ;;  - better handling of subprocess errors
-;;  - hook into file save (after-save-hook)
 ;;  - diff against other branch
 ;;  - renaming files from the status buffer
 ;;  - creating tags
@@ -220,22 +219,15 @@ and returns the process output as a string."
     (message "Running git %s...done" (car args))
     buffer))
 
-(defun git-run-command (buffer env &rest args)
-  (message "Running git %s..." (car args))
-  (apply #'git-call-process-env buffer env args)
-  (message "Running git %s...done" (car args)))
-
 (defun git-run-command-region (buffer start end env &rest args)
   "Run a git command with specified buffer region as input."
-  (message "Running git %s..." (car args))
   (unless (eq 0 (if env
                     (git-run-process-region
                      buffer start end "env"
                      (append (git-get-env-strings env) (list "git") args))
                   (git-run-process-region
                    buffer start end "git" args)))
-    (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string)))
-  (message "Running git %s...done" (car args)))
+    (error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))))
 
 (defun git-run-hook (hook env &rest args)
   "Run a git hook and display its output if any."
@@ -312,6 +304,13 @@ and returns the process output as a string."
               "\"")
     name))
 
+(defun git-success-message (text files)
+  "Print a success message after having handled FILES."
+  (let ((n (length files)))
+    (if (equal n 1)
+        (message "%s %s" text (car files))
+      (message "%s %d files" text n))))
+
 (defun git-get-top-dir (dir)
   "Retrieve the top-level directory of a git tree."
   (let ((cdup (with-output-to-string
@@ -338,7 +337,7 @@ and returns the process output as a string."
     (sort-lines nil (point-min) (point-max))
     (save-buffer))
   (when created
-    (git-run-command nil nil "update-index" "--add" "--" (file-relative-name ignore-name)))
+    (git-call-process-env nil nil "update-index" "--add" "--" (file-relative-name ignore-name)))
   (git-update-status-files (list (file-relative-name ignore-name)) 'unknown)))
 
 ; propertize definition for XEmacs, stolen from erc-compat
@@ -485,33 +484,34 @@ and returns the process output as a string."
   "Remove everything from the status list."
   (ewoc-filter status (lambda (info) nil)))
 
-(defun git-set-files-state (files state)
-  "Set the state of a list of files."
-  (dolist (info files)
-    (unless (eq (git-fileinfo->state info) state)
-      (setf (git-fileinfo->state info) state)
-      (setf (git-fileinfo->rename-state info) nil)
-      (setf (git-fileinfo->orig-name info) nil)
-      (setf (git-fileinfo->needs-refresh info) t))))
-
-(defun git-set-filenames-state (status files state)
-  "Set the state of a list of named files."
+(defun git-set-fileinfo-state (info state)
+  "Set the state of a file info."
+  (unless (eq (git-fileinfo->state info) state)
+    (setf (git-fileinfo->state info) state
+          (git-fileinfo->old-perm info) 0
+          (git-fileinfo->new-perm info) 0
+          (git-fileinfo->rename-state info) nil
+          (git-fileinfo->orig-name info) nil
+          (git-fileinfo->needs-refresh info) t)))
+
+(defun git-status-filenames-map (status func files &rest args)
+  "Apply FUNC to the status files names in the FILES list."
   (when files
     (setq files (sort files #'string-lessp))
     (let ((file (pop files))
           (node (ewoc-nth status 0)))
       (while (and file node)
         (let ((info (ewoc-data node)))
-          (cond ((string-lessp (git-fileinfo->name info) file)
-                 (setq node (ewoc-next status node)))
-                ((string-equal (git-fileinfo->name info) file)
-                 (unless (eq (git-fileinfo->state info) state)
-                   (setf (git-fileinfo->state info) state)
-                   (setf (git-fileinfo->rename-state info) nil)
-                   (setf (git-fileinfo->orig-name info) nil)
-                   (setf (git-fileinfo->needs-refresh info) t))
-                 (setq file (pop files)))
-                (t (setq file (pop files)))))))
+          (if (string-lessp (git-fileinfo->name info) file)
+              (setq node (ewoc-next status node))
+            (if (string-equal (git-fileinfo->name info) file)
+                (apply func info args))
+            (setq file (pop files))))))))
+
+(defun git-set-filenames-state (status files state)
+  "Set the state of a list of named files."
+  (when files
+    (git-status-filenames-map status #'git-set-fileinfo-state files state)
     (unless state  ;; delete files whose state has been set to nil
       (ewoc-filter status (lambda (info) (git-fileinfo->state info))))))
 
@@ -599,7 +599,7 @@ and returns the process output as a string."
 Return the list of files that haven't been handled."
   (let (infolist)
     (with-temp-buffer
-      (apply #'git-run-command t nil "diff-index" "-z" "-M" "HEAD" "--" files)
+      (apply #'git-call-process-env t nil "diff-index" "-z" "-M" "HEAD" "--" files)
       (goto-char (point-min))
       (while (re-search-forward
               ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMU]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
@@ -632,7 +632,7 @@ Return the list of files that haven't been handled."
 Return the list of files that haven't been handled."
   (let (infolist)
     (with-temp-buffer
-      (apply #'git-run-command t nil "ls-files" "-z" (append options (list "--") files))
+      (apply #'git-call-process-env t nil "ls-files" "-z" (append options (list "--") files))
       (goto-char (point-min))
       (while (re-search-forward "\\([^\0]*\\)\0" nil t 1)
         (let ((name (match-string 1)))
@@ -644,7 +644,7 @@ Return the list of files that haven't been handled."
 (defun git-run-ls-unmerged (status files)
   "Run git-ls-files -u on FILES and parse the results into STATUS."
   (with-temp-buffer
-    (apply #'git-run-command t nil "ls-files" "-z" "-u" "--" files)
+    (apply #'git-call-process-env t nil "ls-files" "-z" "-u" "--" files)
     (goto-char (point-min))
     (let (unmerged-files)
       (while (re-search-forward "[0-7]\\{6\\} [0-9a-f]\\{40\\} [123]\t\\([^\0]+\\)\0" nil t)
@@ -747,11 +747,11 @@ Return the list of files that haven't been handled."
         ('deleted (push info deleted))
         ('modified (push info modified))))
     (when added
-      (apply #'git-run-command nil env "update-index" "--add" "--" (git-get-filenames added)))
+      (apply #'git-call-process-env nil env "update-index" "--add" "--" (git-get-filenames added)))
     (when deleted
-      (apply #'git-run-command nil env "update-index" "--remove" "--" (git-get-filenames deleted)))
+      (apply #'git-call-process-env nil env "update-index" "--remove" "--" (git-get-filenames deleted)))
     (when modified
-      (apply #'git-run-command nil env "update-index" "--" (git-get-filenames modified)))))
+      (apply #'git-call-process-env nil env "update-index" "--" (git-get-filenames modified)))))
 
 (defun git-run-pre-commit-hook ()
   "Run the pre-commit hook if any."
@@ -783,6 +783,7 @@ Return the list of files that haven't been handled."
                       head-tree (git-rev-parse "HEAD^{tree}")))
               (if files
                   (progn
+                    (message "Running git commit...")
                     (git-read-tree head-tree index-file)
                     (git-update-index nil files)         ;update both the default index
                     (git-update-index index-file files)  ;and the temporary one
@@ -793,8 +794,8 @@ Return the list of files that haven't been handled."
                             (condition-case nil (delete-file ".git/MERGE_HEAD") (error nil))
                             (condition-case nil (delete-file ".git/MERGE_MSG") (error nil))
                             (with-current-buffer buffer (erase-buffer))
-                            (git-set-files-state files 'uptodate)
-                            (git-run-command nil nil "rerere")
+                            (dolist (info files) (git-set-fileinfo-state info 'uptodate))
+                            (git-call-process-env nil nil "rerere")
                             (git-refresh-files)
                             (git-refresh-ewoc-hf git-status)
                             (message "Committed %s." commit)
@@ -905,8 +906,9 @@ Return the list of files that haven't been handled."
   (let ((files (git-get-filenames (git-marked-files-state 'unknown 'ignored))))
     (unless files
       (push (file-relative-name (read-file-name "File to add: " nil nil t)) files))
-    (apply #'git-run-command nil nil "update-index" "--add" "--" files)
-    (git-update-status-files files 'uptodate)))
+    (apply #'git-call-process-env nil nil "update-index" "--add" "--" files)
+    (git-update-status-files files 'uptodate)
+    (git-success-message "Added" files)))
 
 (defun git-ignore-file ()
   "Add marked file(s) to the ignore list."
@@ -915,7 +917,8 @@ Return the list of files that haven't been handled."
     (unless files
       (push (file-relative-name (read-file-name "File to ignore: " nil nil t)) files))
     (dolist (f files) (git-append-to-ignore f))
-    (git-update-status-files files 'ignored)))
+    (git-update-status-files files 'ignored)
+    (git-success-message "Ignored" files)))
 
 (defun git-remove-file ()
   "Remove the marked file(s)."
@@ -928,8 +931,9 @@ Return the list of files that haven't been handled."
         (progn
           (dolist (name files)
             (when (file-exists-p name) (delete-file name)))
-          (apply #'git-run-command nil nil "update-index" "--remove" "--" files)
-          (git-update-status-files files nil))
+          (apply #'git-call-process-env nil nil "update-index" "--remove" "--" files)
+          (git-update-status-files files nil)
+          (git-success-message "Removed" files))
       (message "Aborting"))))
 
 (defun git-revert-file ()
@@ -947,18 +951,20 @@ Return the list of files that haven't been handled."
           ('unmerged (push (git-fileinfo->name info) modified))
           ('modified (push (git-fileinfo->name info) modified))))
       (when added
-        (apply #'git-run-command nil nil "update-index" "--force-remove" "--" added))
+        (apply #'git-call-process-env nil nil "update-index" "--force-remove" "--" added))
       (when modified
-        (apply #'git-run-command nil nil "checkout" "HEAD" modified))
-      (git-update-status-files (append added modified) 'uptodate))))
+        (apply #'git-call-process-env nil nil "checkout" "HEAD" modified))
+      (git-update-status-files (append added modified) 'uptodate)
+      (git-success-message "Reverted" files))))
 
 (defun git-resolve-file ()
   "Resolve conflicts in marked file(s)."
   (interactive)
   (let ((files (git-get-filenames (git-marked-files-state 'unmerged))))
     (when files
-      (apply #'git-run-command nil nil "update-index" "--" files)
-      (git-update-status-files files 'uptodate))))
+      (apply #'git-call-process-env nil nil "update-index" "--" files)
+      (git-update-status-files files 'uptodate)
+      (git-success-message "Resolved" files))))
 
 (defun git-remove-handled ()
   "Remove handled files from the status list."
@@ -985,9 +991,11 @@ Return the list of files that haven't been handled."
   (interactive)
   (if (setq git-show-ignored (not git-show-ignored))
       (progn
+        (message "Inserting ignored files...")
         (git-run-ls-files-with-excludes git-status nil 'ignored "-o" "-i")
         (git-refresh-files)
-        (git-refresh-ewoc-hf git-status))
+        (git-refresh-ewoc-hf git-status)
+        (message "Inserting ignored files...done"))
     (git-remove-handled)))
 
 (defun git-toggle-show-unknown ()
@@ -995,9 +1003,11 @@ Return the list of files that haven't been handled."
   (interactive)
   (if (setq git-show-unknown (not git-show-unknown))
       (progn
+        (message "Inserting unknown files...")
         (git-run-ls-files-with-excludes git-status nil 'unknown "-o")
         (git-refresh-files)
-        (git-refresh-ewoc-hf git-status))
+        (git-refresh-ewoc-hf git-status)
+        (message "Inserting unknown files...done"))
     (git-remove-handled)))
 
 (defun git-setup-diff-buffer (buffer)
@@ -1197,12 +1207,23 @@ Return the list of files that haven't been handled."
   (interactive)
   (let* ((status git-status)
          (pos (ewoc-locate status))
+         (marked-files (git-get-filenames (ewoc-collect status (lambda (info) (git-fileinfo->marked info)))))
          (cur-name (and pos (git-fileinfo->name (ewoc-data pos)))))
     (unless status (error "Not in git-status buffer."))
-    (git-run-command nil nil "update-index" "--refresh")
+    (message "Refreshing git status...")
+    (git-call-process-env nil nil "update-index" "--refresh")
     (git-clear-status status)
     (git-update-status-files nil)
+    ; restore file marks
+    (when marked-files
+      (git-status-filenames-map status
+                                (lambda (info)
+                                        (setf (git-fileinfo->marked info) t)
+                                        (setf (git-fileinfo->needs-refresh info) t))
+                                marked-files)
+      (git-refresh-files))
     ; move point to the current file name if any
+    (message "Refreshing git status...done")
     (let ((node (and cur-name (git-find-status-file status cur-name))))
       (when node (ewoc-goto-node status node)))))
 
@@ -1324,9 +1345,24 @@ Commands:
         (cd dir)
         (git-status-mode)
         (git-refresh-status)
-        (goto-char (point-min)))
+        (goto-char (point-min))
+        (add-hook 'after-save-hook 'git-update-saved-file))
     (message "%s is not a git working tree." dir)))
 
+(defun git-update-saved-file ()
+  "Update the corresponding git-status buffer when a file is saved.
+Meant to be used in `after-save-hook'."
+  (let* ((file (expand-file-name buffer-file-name))
+         (dir (condition-case nil (git-get-top-dir (file-name-directory file))))
+         (buffer (and dir (git-find-status-buffer dir))))
+    (when buffer
+      (with-current-buffer buffer
+        (let ((filename (file-relative-name file dir)))
+          ; skip files located inside the .git directory
+          (unless (string-match "^\\.git/" filename)
+            (git-call-process-env nil nil "add" "--refresh" "--" filename)
+            (git-update-status-files (list filename) 'uptodate)))))))
+
 (defun git-help ()
   "Display help for Git mode."
   (interactive)
index 1f88099df4b2213c0dca58b22567bc1d91e4adc2..cbbd02fadd753215b3477c178b74d79a59424ee7 100644 (file)
@@ -177,7 +177,6 @@ generate_email_header()
        # --- Email (all stdout will be the email)
        # Generate header
        cat <<-EOF
-       From: $committer
        To: $recipients
        Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe
        X-Git-Refname: $refname
diff --git a/date.c b/date.c
index 93bef6efbe38cb8983fdda14b75ce772f90e1b6a..8f7050027053a4e2390097e341327b117404c26a 100644 (file)
--- a/date.c
+++ b/date.c
@@ -584,6 +584,26 @@ int parse_date(const char *date, char *result, int maxlen)
        return date_string(then, offset, result, maxlen);
 }
 
+enum date_mode parse_date_format(const char *format)
+{
+       if (!strcmp(format, "relative"))
+               return DATE_RELATIVE;
+       else if (!strcmp(format, "iso8601") ||
+                !strcmp(format, "iso"))
+               return DATE_ISO8601;
+       else if (!strcmp(format, "rfc2822") ||
+                !strcmp(format, "rfc"))
+               return DATE_RFC2822;
+       else if (!strcmp(format, "short"))
+               return DATE_SHORT;
+       else if (!strcmp(format, "local"))
+               return DATE_LOCAL;
+       else if (!strcmp(format, "default"))
+               return DATE_NORMAL;
+       else
+               die("unknown date format %s", format);
+}
+
 void datestamp(char *buf, int bufsize)
 {
        time_t now;
diff --git a/diff.c b/diff.c
index 35e3c619864809a46cd5b6a36d08e8103b5ce485..71b340c5368fb287ce7d7b242aa51436ed5dda93 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1675,7 +1675,7 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
        return 0;
 }
 
-void diff_free_filespec_data_large(struct diff_filespec *s)
+void diff_free_filespec_blob(struct diff_filespec *s)
 {
        if (s->should_free)
                free(s->data);
@@ -1690,7 +1690,7 @@ void diff_free_filespec_data_large(struct diff_filespec *s)
 
 void diff_free_filespec_data(struct diff_filespec *s)
 {
-       diff_free_filespec_data_large(s);
+       diff_free_filespec_blob(s);
        free(s->cnt_data);
        s->cnt_data = NULL;
 }
index 4fc200064ae655c6936828c6bf04234afde778ce..142e5376dd741377c311075816f139a0949ee82f 100644 (file)
@@ -378,10 +378,10 @@ void diffcore_rename(struct diff_options *options)
                        m->score = estimate_similarity(one, two,
                                                       minimum_score);
                        m->name_score = basename_same(one, two);
-                       diff_free_filespec_data_large(one);
+                       diff_free_filespec_blob(one);
                }
                /* We do not need the text anymore */
-               diff_free_filespec_data_large(two);
+               diff_free_filespec_blob(two);
                dst_cnt++;
        }
        /* cost matrix sorted by most to least similar pair */
index 4bf175bda99f51f8df1445349d87bc59b56095e7..eb618b1ec00113dabcd5231f141f82e1cdfdca46 100644 (file)
@@ -48,7 +48,7 @@ extern void fill_filespec(struct diff_filespec *, const unsigned char *,
 
 extern int diff_populate_filespec(struct diff_filespec *, int);
 extern void diff_free_filespec_data(struct diff_filespec *);
-extern void diff_free_filespec_data_large(struct diff_filespec *);
+extern void diff_free_filespec_blob(struct diff_filespec *);
 extern int diff_filespec_is_binary(struct diff_filespec *);
 
 struct diff_filepair {
index 7921cde8cbd3b58f1d7222e9828bf85e10f363e3..be6881496c88fcffc52031e482bfdd8d03faa66c 100755 (executable)
@@ -213,9 +213,13 @@ sub list_and_choose {
                        print ">> ";
                }
                my $line = <STDIN>;
-               last if (!$line);
+               if (!$line) {
+                       print "\n";
+                       $opts->{ON_EOF}->() if $opts->{ON_EOF};
+                       last;
+               }
                chomp $line;
-               my $donesomething = 0;
+               last if $line eq '';
                for my $choice (split(/[\s,]+/, $line)) {
                        my $choose = 1;
                        my ($bottom, $top);
@@ -247,12 +251,11 @@ sub list_and_choose {
                                next TOPLOOP;
                        }
                        for ($i = $bottom-1; $i <= $top-1; $i++) {
-                               next if (@stuff <= $i);
+                               next if (@stuff <= $i || $i < 0);
                                $chosen[$i] = $choose;
-                               $donesomething++;
                        }
                }
-               last if (!$donesomething || $opts->{IMMEDIATE});
+               last if ($opts->{IMMEDIATE});
        }
        for ($i = 0; $i < @stuff; $i++) {
                if ($chosen[$i]) {
@@ -791,6 +794,7 @@ sub main_loop {
                                             SINGLETON => 1,
                                             LIST_FLAT => 4,
                                             HEADER => '*** Commands ***',
+                                            ON_EOF => \&quit_cmd,
                                             IMMEDIATE => 1 }, @cmd);
                if ($it) {
                        eval {
index cb14f0621651d2006b08d1eddf67ab3269df84d0..c3b881bfadd7ca20d9e14ab6f1c53a9c365fca72 100755 (executable)
@@ -25,6 +25,7 @@ refuse_partial () {
        exit 1
 }
 
+TMP_INDEX=
 THIS_INDEX="$GIT_DIR/index"
 NEXT_INDEX="$GIT_DIR/next-index$$"
 rm -f "$NEXT_INDEX"
index c3f05f56de300ad48d940def184698fb37c40028..74bfc16744d69d8394f89b6f77d81697a8336032 100755 (executable)
@@ -97,10 +97,24 @@ case "$merge_head" in
        esac
        curr_branch=${curr_branch#refs/heads/}
 
-       echo >&2 "Warning: No merge candidate found because value of config option
-         \"branch.${curr_branch}.merge\" does not match any remote branch fetched."
-       echo >&2 "No changes."
-       exit 0
+       echo >&2 "You asked me to pull without telling me which branch you"
+       echo >&2 "want to merge with, and 'branch.${curr_branch}.merge' in"
+       echo >&2 "your configuration file does not tell me either.  Please"
+       echo >&2 "name which branch you want to merge on the command line and"
+       echo >&2 "try again (e.g. 'git pull <repository> <refspec>')."
+       echo >&2 "See git-pull(1) for details on the refspec."
+       echo >&2
+       echo >&2 "If you often merge with the same branch, you may want to"
+       echo >&2 "configure the following variables in your configuration"
+       echo >&2 "file:"
+       echo >&2
+       echo >&2 "    branch.${curr_branch}.remote = <nickname>"
+       echo >&2 "    branch.${curr_branch}.merge = <remote-ref>"
+       echo >&2 "    remote.<nickname>.url = <url>"
+       echo >&2 "    remote.<nickname>.fetch = <refspec>"
+       echo >&2
+       echo >&2 "See git-config(1) for details."
+       exit 1
        ;;
 ?*' '?*)
        if test -z "$orig_head"
index 268a629c434c3cc1bad8a59861f3f093291ec540..8568a4fd421d7e60512f0dfc37392bded07419b1 100755 (executable)
@@ -232,14 +232,14 @@ do_next () {
        '#'*|'')
                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
@@ -254,7 +254,7 @@ do_next () {
                warn
                exit 0
                ;;
-       squash)
+       squash|s)
                comment_for_reflog squash
 
                has_action "$DONE" ||
@@ -263,7 +263,7 @@ do_next () {
                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"
@@ -276,9 +276,9 @@ do_next () {
                esac
 
                failed=f
+               author_script=$(get_author_ident_from_commit HEAD)
                output git reset --soft HEAD^
                pick_one -n $sha1 || failed=t
-               author_script=$(get_author_ident_from_commit $sha1)
                echo "$author_script" > "$DOTEST"/author-script
                case $failed in
                f)
index b7c1e01d7d7d5dfb68870d65c0f0419138ccc506..9ca3e7ef3792260e98d68e1a95a2ccfceeac5c55 100755 (executable)
@@ -218,7 +218,7 @@ sub prune_remote {
        my ($name, $ls_remote) = @_;
        if (!exists $remote->{$name}) {
                print STDERR "No such remote $name\n";
-               return;
+               return 1;
        }
        my $info = $remote->{$name};
        update_ls_remote($ls_remote, $info);
@@ -229,13 +229,14 @@ sub prune_remote {
                my @v = $git->command(qw(rev-parse --verify), "$prefix/$to_prune");
                $git->command(qw(update-ref -d), "$prefix/$to_prune", $v[0]);
        }
+       return 0;
 }
 
 sub show_remote {
        my ($name, $ls_remote) = @_;
        if (!exists $remote->{$name}) {
                print STDERR "No such remote $name\n";
-               return;
+               return 1;
        }
        my $info = $remote->{$name};
        update_ls_remote($ls_remote, $info);
@@ -265,6 +266,7 @@ sub show_remote {
                print "  Local branch(es) pushed with 'git push'\n";
                print "    @pushed\n";
        }
+       return 0;
 }
 
 sub add_remote {
@@ -320,7 +322,7 @@ sub rm_remote {
        my ($name) = @_;
        if (!exists $remote->{$name}) {
                print STDERR "No such remote $name\n";
-               return;
+               return 1;
        }
 
        $git->command('config', '--remove-section', "remote.$name");
@@ -335,13 +337,13 @@ sub rm_remote {
                }
        };
 
-
        my @refs = $git->command('for-each-ref',
                '--format=%(refname) %(objectname)', "refs/remotes/$name");
        for (@refs) {
                ($ref, $object) = split;
                $git->command(qw(update-ref -d), $ref, $object);
        }
+       return 0;
 }
 
 sub add_usage {
@@ -381,9 +383,11 @@ sub add_usage {
                print STDERR "Usage: git remote show <remote>\n";
                exit(1);
        }
+       my $status = 0;
        for (; $i < @ARGV; $i++) {
-               show_remote($ARGV[$i], $ls_remote);
+               $status |= show_remote($ARGV[$i], $ls_remote);
        }
+       exit($status);
 }
 elsif ($ARGV[0] eq 'update') {
        if (@ARGV <= 1) {
@@ -409,9 +413,11 @@ sub add_usage {
                print STDERR "Usage: git remote prune <remote>\n";
                exit(1);
        }
+       my $status = 0;
        for (; $i < @ARGV; $i++) {
-               prune_remote($ARGV[$i], $ls_remote);
+               $status |= prune_remote($ARGV[$i], $ls_remote);
        }
+        exit($status);
 }
 elsif ($ARGV[0] eq 'add') {
        my %opts = ();
@@ -455,7 +461,7 @@ sub add_usage {
                print STDERR "Usage: git remote rm <remote>\n";
                exit(1);
        }
-       rm_remote($ARGV[1]);
+       exit(rm_remote($ARGV[1]));
 }
 else {
        print STDERR "Usage: git remote\n";
index 727b1d3206da6964affe80d92699cd39c1878e34..4aaaaab0d8450094efb8d2f8e82cc63383b91b48 100755 (executable)
@@ -138,8 +138,8 @@ module_add()
                # it is local
                if base=$(get_repo_base "$repo"); then
                        repo="$base"
-                       realrepo=$repo
                fi
+               realrepo=$repo
                ;;
        esac
 
index 658471385cfaa2480ee78b19fe38fc51d9b51ab2..5d294be308eed42c244c4424618b43d11048d4e6 100644 (file)
@@ -1134,22 +1134,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
                                continue;
                        }
                        if (!strncmp(arg, "--date=", 7)) {
-                               if (!strcmp(arg + 7, "relative"))
-                                       revs->date_mode = DATE_RELATIVE;
-                               else if (!strcmp(arg + 7, "iso8601") ||
-                                        !strcmp(arg + 7, "iso"))
-                                       revs->date_mode = DATE_ISO8601;
-                               else if (!strcmp(arg + 7, "rfc2822") ||
-                                        !strcmp(arg + 7, "rfc"))
-                                       revs->date_mode = DATE_RFC2822;
-                               else if (!strcmp(arg + 7, "short"))
-                                       revs->date_mode = DATE_SHORT;
-                               else if (!strcmp(arg + 7, "local"))
-                                       revs->date_mode = DATE_LOCAL;
-                               else if (!strcmp(arg + 7, "default"))
-                                       revs->date_mode = DATE_NORMAL;
-                               else
-                                       die("unknown date format %s", arg);
+                               revs->date_mode = parse_date_format(arg + 7);
                                continue;
                        }
                        if (!strcmp(arg, "--log-size")) {
diff --git a/t/t3060-ls-files-with-tree.sh b/t/t3060-ls-files-with-tree.sh
new file mode 100755 (executable)
index 0000000..68eb266
--- /dev/null
@@ -0,0 +1,71 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Carl D. Worth
+#
+
+test_description='git ls-files test (--with-tree).
+
+This test runs git ls-files --with-tree and in particular in
+a scenario known to trigger a crash with some versions of git.
+'
+. ./test-lib.sh
+
+test_expect_success setup '
+
+       # The bug we are exercising requires a fair number of entries
+       # in a sub-directory so that add_index_entry will trigger a
+       # realloc.
+
+       echo file >expected &&
+       mkdir sub &&
+       bad= &&
+       for n in 0 1 2 3 4 5
+       do
+               for m in 0 1 2 3 4 5 6 7 8 9
+               do
+                       num=00$n$m &&
+                       >sub/file-$num &&
+                       echo file-$num >>expected || {
+                               bad=t
+                               break
+                       }
+               done && test -z "$bad" || {
+                       bad=t
+                       break
+               }
+       done && test -z "$bad" &&
+       git add . &&
+       git commit -m "add a bunch of files" &&
+
+       # We remove them all so that we will have something to add
+       # back with --with-tree and so that we will definitely be
+       # under the realloc size to trigger the bug.
+       rm -rf sub &&
+       git commit -a -m "remove them all" &&
+
+       # The bug also requires some entry before our directory so that
+       # prune_path will modify the_index.cache
+
+       mkdir a_directory_that_sorts_before_sub &&
+       >a_directory_that_sorts_before_sub/file &&
+       mkdir sub &&
+       >sub/file &&
+       git add .
+'
+
+# We have to run from a sub-directory to trigger prune_path
+# Then we finally get to run our --with-tree test
+cd sub
+
+test_expect_success 'git -ls-files --with-tree should succeed from subdir' '
+
+       git ls-files --with-tree=HEAD~1 >../output
+
+'
+
+cd ..
+test_expect_success \
+    'git -ls-files --with-tree should add entries from named tree.' \
+    'diff -u expected output'
+
+test_done
index 1af73a47c6af80c51a6ac6df4c5b20529c1a3565..f5ef8c22586525a23f53aaac7b895d634a410805 100755 (executable)
@@ -180,7 +180,7 @@ test_expect_success 'squash' '
 '
 
 test_expect_success 'retain authorship when squashing' '
-       git show HEAD | grep "^Author: Nitfol"
+       git show HEAD | grep "^Author: Twerp Snog"
 '
 
 test_expect_success 'preserve merges with -p' '
diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh
new file mode 100644 (file)
index 0000000..d0809eb
--- /dev/null
@@ -0,0 +1,151 @@
+#!/bin/sh
+#
+# Copyright (c) 2007 Andy Parkins
+#
+
+test_description='for-each-ref test'
+
+. ./test-lib.sh
+
+# Mon Jul 3 15:18:43 2006 +0000
+datestamp=1151939923
+setdate_and_increment () {
+    GIT_COMMITTER_DATE="$datestamp +0200"
+    datestamp=$(expr "$datestamp" + 1)
+    GIT_AUTHOR_DATE="$datestamp +0200"
+    datestamp=$(expr "$datestamp" + 1)
+    export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
+}
+
+test_expect_success 'Create sample commit with known timestamp' '
+       setdate_and_increment &&
+       echo "Using $datestamp" > one &&
+       git add one &&
+       git commit -m "Initial" &&
+       setdate_and_increment &&
+       git tag -a -m "Tagging at $datestamp" testtag
+'
+
+test_expect_success 'Check atom names are valid' '
+       bad=
+       for token in \
+               refname objecttype objectsize objectname tree parent \
+               numparent object type author authorname authoremail \
+               authordate committer committername committeremail \
+               committerdate tag tagger taggername taggeremail \
+               taggerdate creator creatordate subject body contents
+       do
+               git for-each-ref --format="$token=%($token)" refs/heads || {
+                       bad=$token
+                       break
+               }
+       done
+       test -z "$bad"
+'
+
+test_expect_failure 'Check invalid atoms names are errors' '
+       git-for-each-ref --format="%(INVALID)" refs/heads
+'
+
+test_expect_success 'Check format specifiers are ignored in naming date atoms' '
+       git-for-each-ref --format="%(authordate)" refs/heads &&
+       git-for-each-ref --format="%(authordate:default) %(authordate)" refs/heads &&
+       git-for-each-ref --format="%(authordate) %(authordate:default)" refs/heads &&
+       git-for-each-ref --format="%(authordate:default) %(authordate:default)" refs/heads
+'
+
+test_expect_success 'Check valid format specifiers for date fields' '
+       git-for-each-ref --format="%(authordate:default)" refs/heads &&
+       git-for-each-ref --format="%(authordate:relative)" refs/heads &&
+       git-for-each-ref --format="%(authordate:short)" refs/heads &&
+       git-for-each-ref --format="%(authordate:local)" refs/heads &&
+       git-for-each-ref --format="%(authordate:iso8601)" refs/heads &&
+       git-for-each-ref --format="%(authordate:rfc2822)" refs/heads
+'
+
+test_expect_failure 'Check invalid format specifiers are errors' '
+       git-for-each-ref --format="%(authordate:INVALID)" refs/heads
+'
+
+cat >expected <<\EOF
+'refs/heads/master' 'Mon Jul 3 17:18:43 2006 +0200' 'Mon Jul 3 17:18:44 2006 +0200'
+'refs/tags/testtag' 'Mon Jul 3 17:18:45 2006 +0200'
+EOF
+
+test_expect_success 'Check unformatted date fields output' '
+       (git for-each-ref --shell --format="%(refname) %(committerdate) %(authordate)" refs/heads &&
+       git for-each-ref --shell --format="%(refname) %(taggerdate)" refs/tags) >actual &&
+       git diff expected actual
+'
+
+test_expect_success 'Check format "default" formatted date fields output' '
+       f=default &&
+       (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+       git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+       git diff expected actual
+'
+
+# Don't know how to do relative check because I can't know when this script
+# is going to be run and can't fake the current time to git, and hence can't
+# provide expected output.  Instead, I'll just make sure that "relative"
+# doesn't exit in error
+#
+#cat >expected <<\EOF
+#
+#EOF
+#
+test_expect_success 'Check format "relative" date fields output' '
+       f=relative &&
+       (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+       git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual
+'
+
+cat >expected <<\EOF
+'refs/heads/master' '2006-07-03' '2006-07-03'
+'refs/tags/testtag' '2006-07-03'
+EOF
+
+test_expect_success 'Check format "short" date fields output' '
+       f=short &&
+       (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+       git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+       git diff expected actual
+'
+
+cat >expected <<\EOF
+'refs/heads/master' 'Mon Jul 3 15:18:43 2006' 'Mon Jul 3 15:18:44 2006'
+'refs/tags/testtag' 'Mon Jul 3 15:18:45 2006'
+EOF
+
+test_expect_success 'Check format "local" date fields output' '
+       f=local &&
+       (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+       git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+       git diff expected actual
+'
+
+cat >expected <<\EOF
+'refs/heads/master' '2006-07-03 17:18:43 +0200' '2006-07-03 17:18:44 +0200'
+'refs/tags/testtag' '2006-07-03 17:18:45 +0200'
+EOF
+
+test_expect_success 'Check format "iso8601" date fields output' '
+       f=iso8601 &&
+       (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+       git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+       git diff expected actual
+'
+
+cat >expected <<\EOF
+'refs/heads/master' 'Mon, 3 Jul 2006 17:18:43 +0200' 'Mon, 3 Jul 2006 17:18:44 +0200'
+'refs/tags/testtag' 'Mon, 3 Jul 2006 17:18:45 +0200'
+EOF
+
+test_expect_success 'Check format "rfc2822" date fields output' '
+       f=rfc2822 &&
+       (git for-each-ref --shell --format="%(refname) %(committerdate:$f) %(authordate:$f)" refs/heads &&
+       git for-each-ref --shell --format="%(refname) %(taggerdate:$f)" refs/tags) >actual &&
+       git diff expected actual
+'
+
+test_done