Merge branch 'rs/pretty-safety'

* rs/pretty-safety:
  Make "--pretty=format" parser a bit more careful.
diff --git a/Documentation/git-check-attr.txt b/Documentation/git-check-attr.txt
index 856d2af..47cb1bd 100644
--- a/Documentation/git-check-attr.txt
+++ b/Documentation/git-check-attr.txt
@@ -23,6 +23,11 @@
 	be treated as an attribute.
 
 
+SEE ALSO
+--------
+gitlink:gitattributes[5].
+
+
 Author
 ------
 Written by Junio C Hamano <junkio@cox.net>
diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 4261384..96383b6 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -11,7 +11,7 @@
 'git-commit' [-a | --interactive] [-s] [-v] [-u]
 	   [(-c | -C) <commit> | -F <file> | -m <msg> | --amend]
 	   [--allow-empty] [--no-verify] [-e] [--author <author>]
-	   [--] [[-i | -o ]<file>...]
+	   [--cleanup=<mode>] [--] [[-i | -o ]<file>...]
 
 DESCRIPTION
 -----------
@@ -95,6 +95,16 @@
 	from making such a commit.  This option bypasses the safety, and
 	is primarily for use by foreign scm interface scripts.
 
+--cleanup=<mode>::
+	This option sets how the commit message is cleaned up.
+	The  '<mode>' can be one of 'verbatim', 'whitespace', 'strip',
+	and 'default'. The 'default' mode will strip leading and
+	trailing empty lines and #commentary from the commit message
+	only if the message is to be edited. Otherwise only whitespace
+	removed. The 'verbatim' mode does not change message at all,
+	'whitespace' removes just leading/trailing whitespace lines
+	and 'strip' removes both whitespace and commentary.
+
 -e|--edit::
 	The message taken from file with `-F`, command line with
 	`-m`, and from file with `-C` are usually used as the
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 5920d17..d71e51a 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -43,8 +43,12 @@
 
 --first-parent::
 	Follow only the first parent commit upon seeing a merge
-	commit.  This  option gives a better overview of the
-	evolution of a particular branch.
+	commit.  This option can give a better overview when
+	viewing the evolution of a particular topic branch,
+	because merges into a topic branch tend to be only about
+	adjusting to updated upstream from time to time, and
+	this option allows you to ignore the individual commits
+	brought in to your history by such a merge.
 
 -g, \--walk-reflogs::
 	Show commits as they were recorded in the reflog. The log contains
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index eabd7ef..1521a9e 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -163,7 +163,8 @@
 
 SEE ALSO
 --------
-gitlink:git-fmt-merge-msg[1], gitlink:git-pull[1]
+gitlink:git-fmt-merge-msg[1], gitlink:git-pull[1],
+gitlink:gitattributes[5]
 
 
 Author
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index a03f9fe..438dae0 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -15,6 +15,7 @@
 	     [ \--min-age=timestamp ]
 	     [ \--sparse ]
 	     [ \--no-merges ]
+	     [ \--first-parent ]
 	     [ \--remove-empty ]
 	     [ \--full-history ]
 	     [ \--not ]
@@ -256,6 +257,15 @@
 
 	Do not print commits with more than one parent.
 
+--first-parent::
+	Follow only the first parent commit upon seeing a merge
+	commit.  This option can give a better overview when
+	viewing the evolution of a particular topic branch,
+	because merges into a topic branch tend to be only about
+	adjusting to updated upstream from time to time, and
+	this option allows you to ignore the individual commits
+	brought in to your history by such a merge.
+
 --not::
 
 	Reverses the meaning of the '{caret}' prefix (or lack thereof)
diff --git a/Documentation/git-shortlog.txt b/Documentation/git-shortlog.txt
index e14720b..36510a8 100644
--- a/Documentation/git-shortlog.txt
+++ b/Documentation/git-shortlog.txt
@@ -38,15 +38,16 @@
 FILES
 -----
 
-.mailmap::
-	If this file exists, it will be used for mapping author email
-	addresses to a real author name. One mapping per line, first
-	the author name followed by the email address enclosed by
-	'<' and '>'. Use hash '#' for comments. Example:
+If the file `.mailmap` exists, it will be used for mapping author
+email addresses to a real author name. One mapping per line, first
+the author name followed by the email address enclosed by
+'<' and '>'. Use hash '#' for comments. Example:
 
-		# Keep alphabetized
-		Adam Morrow <adam@localhost.localdomain>
-		Eve Jones <eve@laptop.(none)>
+------------
+# Keep alphabetized
+Adam Morrow <adam@localhost.localdomain>
+Eve Jones <eve@laptop.(none)>
+------------
 
 Author
 ------
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index 71c7ad7..cc9c7c5 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -322,12 +322,43 @@
 	requested with "binary".
 
 
+Built-in merge drivers
+^^^^^^^^^^^^^^^^^^^^^^
+
+There are a few built-in low-level merge drivers defined that
+can be asked for via the `merge` attribute.
+
+text::
+
+	Usual 3-way file level merge for text files.  Conflicted
+	regions are marked with conflict markers `<<<<<<<`,
+	`=======` and `>>>>>>>`.  The version from your branch
+	appears before the `=======` marker, and the version
+	from the merged branch appears after the `=======`
+	marker.
+
+binary::
+
+	Keep the version from your branch in the work tree, but
+	leave the path in the conflicted state for the user to
+	sort out.
+
+union::
+
+	Run 3-way file level merge for text files, but take
+	lines from both versions, instead of leaving conflict
+	markers.  This tends to leave the added lines in the
+	resulting file in random order and the user should
+	verify the result. Do not use this if you do not
+	understand the implications.
+
+
 Defining a custom merge driver
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-The definition of a merge driver is done in `gitconfig` not
-`gitattributes` file, so strictly speaking this manual page is a
-wrong place to talk about it.  However...
+The definition of a merge driver is done in the `.git/config`
+file, not in the `gitattributes` file, so strictly speaking this
+manual page is a wrong place to talk about it.  However...
 
 To define a custom merge driver `filfre`, add a section to your
 `$GIT_DIR/config` file (or `$HOME/.gitconfig` file) like this:
diff --git a/builtin-commit.c b/builtin-commit.c
index 96410de..73f1e35 100644
--- a/builtin-commit.c
+++ b/builtin-commit.c
@@ -47,8 +47,21 @@
 static char *edit_message, *use_message;
 static int all, edit_flag, also, interactive, only, amend, signoff;
 static int quiet, verbose, untracked_files, no_verify, allow_empty;
+/*
+ * The default commit message cleanup mode will remove the lines
+ * beginning with # (shell comments) and leading and trailing
+ * whitespaces (empty lines or containing only whitespaces)
+ * if editor is used, and only the whitespaces if the message
+ * is specified explicitly.
+ */
+static enum {
+	CLEANUP_SPACE,
+	CLEANUP_NONE,
+	CLEANUP_ALL,
+} cleanup_mode;
+static char *cleanup_arg;
 
-static int no_edit, initial_commit, in_merge;
+static int use_editor = 1, initial_commit, in_merge;
 const char *only_include_assumed;
 struct strbuf message;
 
@@ -88,6 +101,7 @@
 	OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
 	OPT_BOOLEAN(0, "untracked-files", &untracked_files, "show all untracked files"),
 	OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
+	OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
 
 	OPT_END()
 };
@@ -346,7 +360,8 @@
 	if (fp == NULL)
 		die("could not open %s", git_path(commit_editmsg));
 
-	stripspace(&sb, 0);
+	if (cleanup_mode != CLEANUP_NONE)
+		stripspace(&sb, 0);
 
 	if (signoff) {
 		struct strbuf sob;
@@ -372,9 +387,9 @@
 
 	strbuf_release(&sb);
 
-	if (no_edit) {
+	if (!use_editor) {
 		struct rev_info rev;
-		unsigned char sha1[40];
+		unsigned char sha1[20];
 		const char *parent = "HEAD";
 
 		fclose(fp);
@@ -382,12 +397,12 @@
 		if (!active_nr && read_cache() < 0)
 			die("Cannot read index");
 
-		if (get_sha1("HEAD", sha1) != 0)
-			return !!active_nr;
-
 		if (amend)
 			parent = "HEAD^1";
 
+		if (get_sha1(parent, sha1))
+			return !!active_nr;
+
 		init_revisions(&rev, "");
 		rev.abbrev = 0;
 		setup_revisions(0, NULL, &rev, parent);
@@ -398,7 +413,7 @@
 		return !!DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES);
 	}
 
-	if (in_merge && !no_edit)
+	if (in_merge)
 		fprintf(fp,
 			"#\n"
 			"# It looks like you may be committing a MERGE.\n"
@@ -411,7 +426,12 @@
 	fprintf(fp,
 		"\n"
 		"# Please enter the commit message for your changes.\n"
-		"# (Comment lines starting with '#' will not be included)\n");
+		"# (Comment lines starting with '#' will ");
+	if (cleanup_mode == CLEANUP_ALL)
+		fprintf(fp, "not be included)\n");
+	else /* CLEANUP_SPACE, that is. */
+		fprintf(fp, "be kept.\n"
+			"# You can remove them yourself if you want to)\n");
 	if (only_include_assumed)
 		fprintf(fp, "# %s\n", only_include_assumed);
 
@@ -435,10 +455,13 @@
 	const char *nl;
 	int eol, i;
 
+	if (cleanup_mode == CLEANUP_NONE && sb->len)
+		return 0;
+
 	/* See if the template is just a prefix of the message. */
 	strbuf_init(&tmpl, 0);
 	if (template_file && strbuf_read_file(&tmpl, template_file, 0) > 0) {
-		stripspace(&tmpl, 1);
+		stripspace(&tmpl, cleanup_mode == CLEANUP_ALL);
 		if (start + tmpl.len <= sb->len &&
 		    memcmp(tmpl.buf, sb->buf + start, tmpl.len) == 0)
 			start += tmpl.len;
@@ -513,9 +536,9 @@
 	argc = parse_options(argc, argv, builtin_commit_options, usage, 0);
 
 	if (logfile || message.len || use_message)
-		no_edit = 1;
+		use_editor = 0;
 	if (edit_flag)
-		no_edit = 0;
+		use_editor = 1;
 
 	if (get_sha1("HEAD", head_sha1))
 		initial_commit = 1;
@@ -591,6 +614,16 @@
 		only_include_assumed = "Explicit paths specified without -i nor -o; assuming --only paths...";
 		also = 0;
 	}
+	if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
+		cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;
+	else if (!strcmp(cleanup_arg, "verbatim"))
+		cleanup_mode = CLEANUP_NONE;
+	else if (!strcmp(cleanup_arg, "whitespace"))
+		cleanup_mode = CLEANUP_SPACE;
+	else if (!strcmp(cleanup_arg, "strip"))
+		cleanup_mode = CLEANUP_ALL;
+	else
+		die("Invalid cleanup mode %s", cleanup_arg);
 
 	if (all && argc > 0)
 		die("Paths with -a does not make sense.");
@@ -796,7 +829,7 @@
 
 	/* Get the commit message and validate it */
 	header_len = sb.len;
-	if (!no_edit) {
+	if (use_editor) {
 		char index[PATH_MAX];
 		const char *env[2] = { index, NULL };
 		snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file);
@@ -817,7 +850,8 @@
 	if (p != NULL)
 		strbuf_setlen(&sb, p - sb.buf + 1);
 
-	stripspace(&sb, 1);
+	if (cleanup_mode != CLEANUP_NONE)
+		stripspace(&sb, cleanup_mode == CLEANUP_ALL);
 	if (sb.len < header_len || message_is_empty(&sb, header_len)) {
 		rollback_index_files();
 		die("no commit message?  aborting commit.");
diff --git a/combine-diff.c b/combine-diff.c
index e22db89..0e19cba 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -646,12 +646,19 @@
 	sline->p_lno[i] = sline->p_lno[j];
 }
 
-static void dump_quoted_path(const char *prefix, const char *path,
+static void dump_quoted_path(const char *head,
+			     const char *prefix,
+			     const char *path,
 			     const char *c_meta, const char *c_reset)
 {
-	printf("%s%s", c_meta, prefix);
-	quote_c_style(path, NULL, stdout, 0);
-	printf("%s\n", c_reset);
+	static struct strbuf buf = STRBUF_INIT;
+
+	strbuf_reset(&buf);
+	strbuf_addstr(&buf, c_meta);
+	strbuf_addstr(&buf, head);
+	quote_two_c_style(&buf, prefix, path, 0);
+	strbuf_addstr(&buf, c_reset);
+	puts(buf.buf);
 }
 
 static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
@@ -793,7 +800,7 @@
 		if (rev->loginfo && !rev->no_commit_id)
 			show_log(rev, opt->msg_sep);
 		dump_quoted_path(dense ? "diff --cc " : "diff --combined ",
-				 elem->path, c_meta, c_reset);
+				 "", elem->path, c_meta, c_reset);
 		printf("%sindex ", c_meta);
 		for (i = 0; i < num_parent; i++) {
 			abb = find_unique_abbrev(elem->parent[i].sha1,
@@ -829,14 +836,19 @@
 			printf("%s\n", c_reset);
 		}
 		if (added)
-			dump_quoted_path("--- /dev/", "null", c_meta, c_reset);
+			dump_quoted_path("--- ", "", "/dev/null",
+					 c_meta, c_reset);
 		else
-			dump_quoted_path("--- a/", elem->path, c_meta, c_reset);
+			dump_quoted_path("--- ", opt->a_prefix, elem->path,
+					 c_meta, c_reset);
 		if (deleted)
-			dump_quoted_path("+++ /dev/", "null", c_meta, c_reset);
+			dump_quoted_path("+++ ", "", "/dev/null",
+					 c_meta, c_reset);
 		else
-			dump_quoted_path("+++ b/", elem->path, c_meta, c_reset);
-		dump_sline(sline, cnt, num_parent, DIFF_OPT_TST(opt, COLOR_DIFF));
+			dump_quoted_path("+++ ", opt->b_prefix, elem->path,
+					 c_meta, c_reset);
+		dump_sline(sline, cnt, num_parent,
+			   DIFF_OPT_TST(opt, COLOR_DIFF));
 	}
 	free(result);
 
diff --git a/config.c b/config.c
index 9a5c547..80db929 100644
--- a/config.c
+++ b/config.c
@@ -234,17 +234,23 @@
 	die("bad config file line %d in %s", config_linenr, config_file_name);
 }
 
-static unsigned long get_unit_factor(const char *end)
+static int parse_unit_factor(const char *end, unsigned long *val)
 {
 	if (!*end)
 		return 1;
-	else if (!strcasecmp(end, "k"))
-		return 1024;
-	else if (!strcasecmp(end, "m"))
-		return 1024 * 1024;
-	else if (!strcasecmp(end, "g"))
-		return 1024 * 1024 * 1024;
-	die("unknown unit: '%s'", end);
+	else if (!strcasecmp(end, "k")) {
+		*val *= 1024;
+		return 1;
+	}
+	else if (!strcasecmp(end, "m")) {
+		*val *= 1024 * 1024;
+		return 1;
+	}
+	else if (!strcasecmp(end, "g")) {
+		*val *= 1024 * 1024 * 1024;
+		return 1;
+	}
+	return 0;
 }
 
 int git_parse_long(const char *value, long *ret)
@@ -252,7 +258,10 @@
 	if (value && *value) {
 		char *end;
 		long val = strtol(value, &end, 0);
-		*ret = val * get_unit_factor(end);
+		unsigned long factor = 1;
+		if (!parse_unit_factor(end, &factor))
+			return 0;
+		*ret = val * factor;
 		return 1;
 	}
 	return 0;
@@ -263,7 +272,9 @@
 	if (value && *value) {
 		char *end;
 		unsigned long val = strtoul(value, &end, 0);
-		*ret = val * get_unit_factor(end);
+		if (!parse_unit_factor(end, &val))
+			return 0;
+		*ret = val;
 		return 1;
 	}
 	return 0;
diff --git a/contrib/examples/git-revert.sh b/contrib/examples/git-revert.sh
new file mode 100755
index 0000000..49f0032
--- /dev/null
+++ b/contrib/examples/git-revert.sh
@@ -0,0 +1,197 @@
+#!/bin/sh
+#
+# Copyright (c) 2005 Linus Torvalds
+# Copyright (c) 2005 Junio C Hamano
+#
+
+case "$0" in
+*-revert* )
+	test -t 0 && edit=-e
+	replay=
+	me=revert
+	USAGE='[--edit | --no-edit] [-n] <commit-ish>' ;;
+*-cherry-pick* )
+	replay=t
+	edit=
+	me=cherry-pick
+	USAGE='[--edit] [-n] [-r] [-x] <commit-ish>'  ;;
+* )
+	echo >&2 "What are you talking about?"
+	exit 1 ;;
+esac
+
+SUBDIRECTORY_OK=Yes ;# we will cd up
+. git-sh-setup
+require_work_tree
+cd_to_toplevel
+
+no_commit=
+while case "$#" in 0) break ;; esac
+do
+	case "$1" in
+	-n|--n|--no|--no-|--no-c|--no-co|--no-com|--no-comm|\
+	    --no-commi|--no-commit)
+		no_commit=t
+		;;
+	-e|--e|--ed|--edi|--edit)
+		edit=-e
+		;;
+	--n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit)
+		edit=
+		;;
+	-r)
+		: no-op ;;
+	-x|--i-really-want-to-expose-my-private-commit-object-name)
+		replay=
+		;;
+	-*)
+		usage
+		;;
+	*)
+		break
+		;;
+	esac
+	shift
+done
+
+set_reflog_action "$me"
+
+test "$me,$replay" = "revert,t" && usage
+
+case "$no_commit" in
+t)
+	# We do not intend to commit immediately.  We just want to
+	# merge the differences in.
+	head=$(git-write-tree) ||
+		die "Your index file is unmerged."
+	;;
+*)
+	head=$(git-rev-parse --verify HEAD) ||
+		die "You do not have a valid HEAD"
+	files=$(git-diff-index --cached --name-only $head) || exit
+	if [ "$files" ]; then
+		die "Dirty index: cannot $me (dirty: $files)"
+	fi
+	;;
+esac
+
+rev=$(git-rev-parse --verify "$@") &&
+commit=$(git-rev-parse --verify "$rev^0") ||
+	die "Not a single commit $@"
+prev=$(git-rev-parse --verify "$commit^1" 2>/dev/null) ||
+	die "Cannot run $me a root commit"
+git-rev-parse --verify "$commit^2" >/dev/null 2>&1 &&
+	die "Cannot run $me a multi-parent commit."
+
+encoding=$(git config i18n.commitencoding || echo UTF-8)
+
+# "commit" is an existing commit.  We would want to apply
+# the difference it introduces since its first parent "prev"
+# on top of the current HEAD if we are cherry-pick.  Or the
+# reverse of it if we are revert.
+
+case "$me" in
+revert)
+	git show -s --pretty=oneline --encoding="$encoding" $commit |
+	sed -e '
+		s/^[^ ]* /Revert "/
+		s/$/"/
+	'
+	echo
+	echo "This reverts commit $commit."
+	test "$rev" = "$commit" ||
+	echo "(original 'git revert' arguments: $@)"
+	base=$commit next=$prev
+	;;
+
+cherry-pick)
+	pick_author_script='
+	/^author /{
+		s/'\''/'\''\\'\'\''/g
+		h
+		s/^author \([^<]*\) <[^>]*> .*$/\1/
+		s/'\''/'\''\'\'\''/g
+		s/.*/GIT_AUTHOR_NAME='\''&'\''/p
+
+		g
+		s/^author [^<]* <\([^>]*\)> .*$/\1/
+		s/'\''/'\''\'\'\''/g
+		s/.*/GIT_AUTHOR_EMAIL='\''&'\''/p
+
+		g
+		s/^author [^<]* <[^>]*> \(.*\)$/\1/
+		s/'\''/'\''\'\'\''/g
+		s/.*/GIT_AUTHOR_DATE='\''&'\''/p
+
+		q
+	}'
+
+	logmsg=`git show -s --pretty=raw --encoding="$encoding" "$commit"`
+	set_author_env=`echo "$logmsg" |
+	LANG=C LC_ALL=C sed -ne "$pick_author_script"`
+	eval "$set_author_env"
+	export GIT_AUTHOR_NAME
+	export GIT_AUTHOR_EMAIL
+	export GIT_AUTHOR_DATE
+
+	echo "$logmsg" |
+	sed -e '1,/^$/d' -e 's/^    //'
+	case "$replay" in
+	'')
+		echo "(cherry picked from commit $commit)"
+		test "$rev" = "$commit" ||
+		echo "(original 'git cherry-pick' arguments: $@)"
+		;;
+	esac
+	base=$prev next=$commit
+	;;
+
+esac >.msg
+
+eval GITHEAD_$head=HEAD
+eval GITHEAD_$next='`git show -s \
+	--pretty=oneline --encoding="$encoding" "$commit" |
+	sed -e "s/^[^ ]* //"`'
+export GITHEAD_$head GITHEAD_$next
+
+# This three way merge is an interesting one.  We are at
+# $head, and would want to apply the change between $commit
+# and $prev on top of us (when reverting), or the change between
+# $prev and $commit on top of us (when cherry-picking or replaying).
+
+git-merge-recursive $base -- $head $next &&
+result=$(git-write-tree 2>/dev/null) || {
+	mv -f .msg "$GIT_DIR/MERGE_MSG"
+	{
+	    echo '
+Conflicts:
+'
+		git ls-files --unmerged |
+		sed -e 's/^[^	]*	/	/' |
+		uniq
+	} >>"$GIT_DIR/MERGE_MSG"
+	echo >&2 "Automatic $me failed.  After resolving the conflicts,"
+	echo >&2 "mark the corrected paths with 'git-add <paths>'"
+	echo >&2 "and commit the result."
+	case "$me" in
+	cherry-pick)
+		echo >&2 "You may choose to use the following when making"
+		echo >&2 "the commit:"
+		echo >&2 "$set_author_env"
+	esac
+	exit 1
+}
+echo >&2 "Finished one $me."
+
+# If we are cherry-pick, and if the merge did not result in
+# hand-editing, we will hit this commit and inherit the original
+# author date and name.
+# If we are revert, or if our cherry-pick results in a hand merge,
+# we had better say that the current user is responsible for that.
+
+case "$no_commit" in
+'')
+	git-commit -n -F .msg $edit
+	rm -f .msg
+	;;
+esac
diff --git a/diff.c b/diff.c
index 61fd492..5bdc111 100644
--- a/diff.c
+++ b/diff.c
@@ -300,19 +300,25 @@
 	const char *old = diff_get_color(color_diff, DIFF_FILE_OLD);
 	const char *new = diff_get_color(color_diff, DIFF_FILE_NEW);
 	const char *reset = diff_get_color(color_diff, DIFF_RESET);
+	static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
 
 	name_a += (*name_a == '/');
 	name_b += (*name_b == '/');
 	name_a_tab = strchr(name_a, ' ') ? "\t" : "";
 	name_b_tab = strchr(name_b, ' ') ? "\t" : "";
 
+	strbuf_reset(&a_name);
+	strbuf_reset(&b_name);
+	quote_two_c_style(&a_name, o->a_prefix, name_a, 0);
+	quote_two_c_style(&b_name, o->b_prefix, name_b, 0);
+
 	diff_populate_filespec(one, 0);
 	diff_populate_filespec(two, 0);
 	lc_a = count_lines(one->data, one->size);
 	lc_b = count_lines(two->data, two->size);
-	printf("%s--- %s%s%s%s\n%s+++ %s%s%s%s\n%s@@ -",
-	       metainfo, o->a_prefix, name_a, name_a_tab, reset,
-	       metainfo, o->b_prefix, name_b, name_b_tab, reset, fraginfo);
+	printf("%s--- %s%s%s\n%s+++ %s%s%s\n%s@@ -",
+	       metainfo, a_name.buf, name_a_tab, reset,
+	       metainfo, b_name.buf, name_b_tab, reset, fraginfo);
 	print_line_count(lc_a);
 	printf(" +");
 	print_line_count(lc_b);
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index 1fa9a22..6d8ff93 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -632,6 +632,7 @@
 	    print $cvspsfh $_;
 	}
 	close CVSPS;
+	$? == 0 or die "git-cvsimport: fatal: cvsps reported error\n";
 	close $cvspsfh;
 } else {
 	$cvspsfile = $opt_P;
diff --git a/quote.c b/quote.c
index 6986b44..d061626 100644
--- a/quote.c
+++ b/quote.c
@@ -213,6 +213,22 @@
 	return quote_c_style_counted(name, -1, sb, fp, nodq);
 }
 
+void quote_two_c_style(struct strbuf *sb, const char *prefix, const char *path, int nodq)
+{
+	if (quote_c_style(prefix, NULL, NULL, 0) ||
+	    quote_c_style(path, NULL, NULL, 0)) {
+		if (!nodq)
+			strbuf_addch(sb, '"');
+		quote_c_style(prefix, sb, NULL, 1);
+		quote_c_style(path, sb, NULL, 1);
+		if (!nodq)
+			strbuf_addch(sb, '"');
+	} else {
+		strbuf_addstr(sb, prefix);
+		strbuf_addstr(sb, path);
+	}
+}
+
 void write_name_quoted(const char *name, FILE *fp, int terminator)
 {
 	if (terminator) {
diff --git a/quote.h b/quote.h
index ab7596f..4da110e 100644
--- a/quote.h
+++ b/quote.h
@@ -41,6 +41,7 @@
 
 extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
 extern size_t quote_c_style(const char *name, struct strbuf *, FILE *, int no_dq);
+extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
 
 extern void write_name_quoted(const char *name, FILE *, int terminator);
 extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
diff --git a/revision.c b/revision.c
index 7e2f4f1..6e85aaa 100644
--- a/revision.c
+++ b/revision.c
@@ -1290,8 +1290,10 @@
 	if (revs->diffopt.output_format & ~DIFF_FORMAT_NO_OUTPUT)
 		revs->diff = 1;
 
-	/* Pickaxe and rename following needs diffs */
-	if (revs->diffopt.pickaxe || DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
+	/* Pickaxe, diff-filter and rename following need diffs */
+	if (revs->diffopt.pickaxe ||
+	    revs->diffopt.filter ||
+	    DIFF_OPT_TST(&revs->diffopt, FOLLOW_RENAMES))
 		revs->diff = 1;
 
 	if (revs->topo_order)
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index e894629..42eac2a 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -448,6 +448,23 @@
 	test z1048576 = "z$m"
 '
 
+cat > expect <<EOF
+fatal: bad config value for 'aninvalid.unit' in .git/config
+EOF
+
+test_expect_success 'invalid unit' '
+
+	git config aninvalid.unit "1auto" &&
+	s=$(git config aninvalid.unit) &&
+	test "z1auto" = "z$s" &&
+	if git config --int --get aninvalid.unit 2>actual
+	then
+		echo config should have failed
+		false
+	fi &&
+	cmp actual expect
+'
+
 cat > expect << EOF
 true
 false
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
new file mode 100755
index 0000000..b536454
--- /dev/null
+++ b/t/t4202-log.sh
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+test_description='git log'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+	echo one >one &&
+	git add one &&
+	test_tick &&
+	git commit -m initial &&
+
+	echo ichi >one &&
+	git add one &&
+	test_tick &&
+	git commit -m second &&
+
+	mkdir a &&
+	echo ni >a/two &&
+	git add a/two &&
+	test_tick &&
+	git commit -m third &&
+
+	echo san >a/three &&
+	git add a/three &&
+	test_tick &&
+	git commit -m fourth &&
+
+	git rm a/three &&
+	test_tick &&
+	git commit -m fifth
+
+'
+
+test_expect_success 'diff-filter=A' '
+
+	actual=$(git log --pretty="format:%s" --diff-filter=A HEAD) &&
+	expect=$(echo fourth ; echo third ; echo initial) &&
+	test "$actual" = "$expect" || {
+		echo Oops
+		echo "Actual: $actual"
+		false
+	}
+
+'
+
+test_expect_success 'diff-filter=M' '
+
+	actual=$(git log --pretty="format:%s" --diff-filter=M HEAD) &&
+	expect=$(echo second) &&
+	test "$actual" = "$expect" || {
+		echo Oops
+		echo "Actual: $actual"
+		false
+	}
+
+'
+
+test_expect_success 'diff-filter=D' '
+
+	actual=$(git log --pretty="format:%s" --diff-filter=D HEAD) &&
+	expect=$(echo fifth) &&
+	test "$actual" = "$expect" || {
+		echo Oops
+		echo "Actual: $actual"
+		false
+	}
+
+'
+
+
+
+test_done
\ No newline at end of file
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
index 44228b5..c1cec55 100755
--- a/t/t7005-editor.sh
+++ b/t/t7005-editor.sh
@@ -37,7 +37,7 @@
 	if git commit --amend
 	then
 		echo "Oops?"
-		exit 1
+		false
 	else
 		: happy
 	fi
diff --git a/t/t7502-commit.sh b/t/t7502-commit.sh
index 21ac785..aaf497e 100755
--- a/t/t7502-commit.sh
+++ b/t/t7502-commit.sh
@@ -89,4 +89,69 @@
 
 '
 
+test_expect_success 'cleanup commit messages (verbatim,-t)' '
+
+	echo >>negative &&
+	{ echo;echo "# text";echo; } >expect &&
+	git commit --cleanup=verbatim -t expect -a &&
+	git cat-file -p HEAD |sed -e "1,/^\$/d" |head -n 3 >actual &&
+	diff -u expect actual
+
+'
+
+test_expect_success 'cleanup commit messages (verbatim,-F)' '
+
+	echo >>negative &&
+	git commit --cleanup=verbatim -F expect -a &&
+	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+	diff -u expect actual
+
+'
+
+test_expect_success 'cleanup commit messages (verbatim,-m)' '
+
+	echo >>negative &&
+	git commit --cleanup=verbatim -m "$(cat expect)" -a &&
+	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+	diff -u expect actual
+
+'
+
+test_expect_success 'cleanup commit messages (whitespace,-F)' '
+
+	echo >>negative &&
+	{ echo;echo "# text";echo; } >text &&
+	echo "# text" >expect &&
+	git commit --cleanup=whitespace -F text -a &&
+	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+	diff -u expect actual
+
+'
+
+test_expect_success 'cleanup commit messages (strip,-F)' '
+
+	echo >>negative &&
+	{ echo;echo "# text";echo sample;echo; } >text &&
+	echo sample >expect &&
+	git commit --cleanup=strip -F text -a &&
+	git cat-file -p HEAD |sed -e "1,/^\$/d">actual &&
+	diff -u expect actual
+
+'
+
+echo "sample
+
+# Please enter the commit message for your changes.
+# (Comment lines starting with '#' will not be included)" >expect
+
+test_expect_success 'cleanup commit messages (strip,-F,-e)' '
+
+	echo >>negative &&
+	{ echo;echo sample;echo; } >text &&
+	git commit -e -F text -a &&
+	head -n 4 .git/COMMIT_EDITMSG >actual &&
+	diff -u expect actual
+
+'
+
 test_done