{"thread":{"id":"3895","subject":"Fixes for option parsing","startedAt":"2006-04-16T22:17:23Z","lastAt":"2006-04-18T18:33:19Z","messageCount":18,"participants":["Linus Torvalds","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"18757","messageId":"Pine.LNX.4.64.0604161433000.3701@g5.osdl.org","threadId":"3895","inReplyTo":null,"subject":"Fixes for option parsing","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-04-16T22:17:23Z","receivedAt":"2006-04-16T22:17:23Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nMake sure \"git show\" always show the header, regardless of whether there \nis a diff or not.\n\nAlso, make sure \"always_show_header\" actually works, since generate_header \nonly tested it in one out of three return paths.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n---\ndiff --git a/git.c b/git.c\nindex c5de8d3..fc4e429 100644\n--- a/git.c\n+++ b/git.c\n@@ -373,6 +373,7 @@ static int cmd_show(int argc, const char\n \trev.diffopt.recursive = 1;\n \trev.combine_merges = 1;\n \trev.dense_combined_merges = 1;\n+\trev.always_show_header = 1;\n \trev.ignore_merges = 0;\n \trev.no_walk = 1;\n \treturn cmd_log_wc(argc, argv, envp, &rev);\ndiff --git a/log-tree.c b/log-tree.c\nindex 7d9f41e..81dff8f 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -43,7 +43,7 @@ static int diff_root_tree(struct rev_inf\n \treturn retval;\n }\n \n-static const char *generate_header(struct rev_info *opt,\n+static const char *get_header(struct rev_info *opt,\n \t\t\t\t   const unsigned char *commit_sha1,\n \t\t\t\t   const unsigned char *parent_sha1,\n \t\t\t\t   const struct commit *commit)\n@@ -75,11 +75,21 @@ static const char *generate_header(struc\n \toffset += pretty_print_commit(opt->commit_format, commit, len,\n \t\t\t\t      this_header + offset,\n \t\t\t\t      sizeof(this_header) - offset, abbrev);\n+\treturn this_header;\n+}\n+\n+static const char *generate_header(struct rev_info *opt,     \n+\t\t\t\t\tconst unsigned char *commit_sha1,\n+\t\t\t\t\tconst unsigned char *parent_sha1,\n+\t\t\t\t\tconst struct commit *commit)\n+{\n+\tconst char *header = get_header(opt, commit_sha1, parent_sha1, commit);\n+\n \tif (opt->always_show_header) {\n-\t\tputs(this_header);\n-\t\treturn NULL;\n+\t\tputs(header);\n+\t\theader = NULL;\n \t}\n-\treturn this_header;\n+\treturn header;\n }\n \n static int do_diff_combined(struct rev_info *opt, struct commit *commit)\n"},{"id":"18759","messageId":"7vbqv1oxie.fsf@assigned-by-dhcp.cox.net","threadId":"3895","inReplyTo":"Pine.LNX.4.64.0604161433000.3701@g5.osdl.org","subject":"Re: Fixes for option parsing","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-17T00:29:13Z","receivedAt":"2006-04-17T00:29:13Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Thanks for fixing this.  I've applied and merged it to the\n\"next\" branch.\n\nIn the mid-term, I am hoping we can drop the generate_header()\ncallchain _and_ the custom code that formats commit log in-core,\nfound in cmd_log_wc().  rev->use_precomputed_header and perhaps\nrev->header can be dropped and replaced with a call to a\nfunction to print formatted log to stdout (or perhaps a FILE *,\nbecause I want to reuse this to make output part of\n\"git-format-patch\" internal) from log-tree.\n\nFor that, I'd need to define a new CMIT_FMT for the format-patch\noutput.  Another thing needed is to clean up the commit_prefix\nlocal variable defined in cmd_log_wc() -- it should use\nrev->header_prefix, but before that the part that rewrites\nopt->header_prefix to have a blank line between commits, in\nlog-tree.c::do_diff_combined() and log_tree_commit() need to be\ncleaned up.\n\nFor the header_prefix line, there also is a subtle difference\nbetween the one-commit diff-tree and log.  The former lists\n\"this commit (from that commit)\", which is perhaps useful for\ncut and paste purposes.  The latter just says \"this commit\".\n\nI wish this \"diff-tree SHA1 (from ANOTHERSHA1)\" format can be\ndropped and replaced with \"commit SHA1\" format like \"git log\"\ndoes uniformly, but it might be already depended upon by\nPorcelains.\n\nIf the commit is not a merge, \"commit SHA1\" means the same thing\nas \"diff-tree SHA1 (from SHA1^)\", and if it _is_ a merge, then\nthe merge parents are listed on the \"Merge: \" line anyway for\nall formats other than --pretty=oneline, so unless this change\nbreaks an Porcelain, there is really no downside.\n"},{"id":"18762","messageId":"Pine.LNX.4.64.0604161938070.3701@g5.osdl.org","threadId":"3895","inReplyTo":"7vbqv1oxie.fsf@assigned-by-dhcp.cox.net","subject":"Re: Fixes for option parsing","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-04-17T02:42:30Z","receivedAt":"2006-04-17T02:42:30Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 16 Apr 2006, Junio C Hamano wrote:\n> \n> In the mid-term, I am hoping we can drop the generate_header()\n> callchain _and_ the custom code that formats commit log in-core,\n> found in cmd_log_wc().\n\nMaybe. I'm not convinced, though. The reason? cmd_log_wc needs to generate \nit regardless, for the \"always\" case.\n\nAlso, I think the \"---\" printing should be removed, and moved into the \n\"diffstat\" flushing code. Right now it does the wrong thing entirely if no \ndiff exists, but we have always_show_header: it will print the \"---\" for \nno good reason. \n\n> I wish this \"diff-tree SHA1 (from ANOTHERSHA1)\" format can be\n> dropped and replaced with \"commit SHA1\" format like \"git log\"\n> does uniformly, but it might be already depended upon by\n> Porcelains.\n\nI think it might be worth just trying. A lot of the formats were pretty \nad-hoc, and since everybody had their own routines, there was no reason to \neven try to keep them similar. Now, we'd be better off trying to make them \nlook as close to each other as possible, and I suspect there's not a lot \nof porcelain that cares deeply about that format..\n\n\t\tLinus\n"},{"id":"18764","messageId":"Pine.LNX.4.64.0604161952500.3701@g5.osdl.org","threadId":"3895","inReplyTo":"Pine.LNX.4.64.0604161938070.3701@g5.osdl.org","subject":"Re: Fixes for option parsing","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-04-17T03:03:54Z","receivedAt":"2006-04-17T03:03:54Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 16 Apr 2006, Linus Torvalds wrote:\n> \n> Maybe. I'm not convinced, though. The reason? cmd_log_wc needs to generate \n> it regardless, for the \"always\" case.\n\nOf course, you could just have the \"diff\" logic unconditionally do the \ncall-back. That would be clean enough.\n\n> Also, I think the \"---\" printing should be removed, and moved into the \n> \"diffstat\" flushing code. Right now it does the wrong thing entirely if \n> no diff exists, but we have always_show_header: it will print the \"---\" \n> for no good reason.\n\nThe alternative is to do something like this, but because the \"diffstat\" \ndoesn't flush the header priperly, it doesn't add the \"---\" for merges \n(and it used to not show the log at all, even for \"git log\", before I \nfixed it).\n\nTry it with\n\n\tgit log --cc --patch-with-stat\n\nto see what I mean.\n\nI do agree that this would be much cleaner with a \"print header\" callback \nin the diffopt structure. This patch is the really hacky \"continue to do \nthings the ugly way\" approach to fix some of the uglier output.\n\nOnly meant as a RFC and to illustrate what I think the output should look \nlike (modulo the lack of \"---\" before a diffstat with no patch - for \nmerges). Not meant to actually be applied, I think this can be done much \nmore cleanly with the callback.\n\n\t\tLinus\n\n---\ndiff --git a/git.c b/git.c\nindex fc4e429..dc577fa 100644\n--- a/git.c\n+++ b/git.c\n@@ -284,7 +284,7 @@ static int cmd_log_wc(int argc, const ch\n \tstruct commit *commit;\n \tchar *buf = xmalloc(LOGSIZE);\n \tconst char *commit_prefix = \"commit \";\n-\tint shown = 0;\n+\tint shown = 0, always_show_header;\n \n \trev->abbrev = DEFAULT_ABBREV;\n \trev->commit_format = CMIT_FMT_DEFAULT;\n@@ -295,13 +295,19 @@ static int cmd_log_wc(int argc, const ch\n \tif (rev->commit_format == CMIT_FMT_ONELINE)\n \t\tcommit_prefix = \"\";\n \n+\t/*\n+\t * We handle always_show_header outselves, and leave the\n+\t * \"---\" handling to log_tree_commit\n+\t */\n+\talways_show_header = rev->always_show_header;\n+\trev->always_show_header = 0;\n+\n \tprepare_revision_walk(rev);\n \tsetup_pager();\n \twhile ((commit = get_revision(rev)) != NULL) {\n \t\tunsigned long ofs = 0;\n \n-\t\tif (shown && rev->diff &&\n-\t\t    rev->commit_format != CMIT_FMT_ONELINE)\n+\t\tif (shown && rev->commit_format != CMIT_FMT_ONELINE)\n \t\t\tputchar('\\n');\n \n \t\tofs = sprintf(buf, \"%s\", commit_prefix);\n@@ -338,14 +344,16 @@ static int cmd_log_wc(int argc, const ch\n \t\t\t\t\t   buf + ofs,\n \t\t\t\t\t   LOGSIZE - ofs - 20,\n \t\t\t\t\t   rev->abbrev);\n+\t\tif (always_show_header || !rev->diff) {\n+\t\t\tfputs(buf, stdout);\n+\t\t\tofs = 0;\n+\t\t}\n \n \t\tif (rev->diff) {\n+\t\t\tstrcpy(buf + ofs, rev->diffopt.with_stat ? \"---\" : \"\\n\");\n \t\t\trev->use_precomputed_header = buf;\n-\t\t\tstrcpy(buf + ofs, \"\\n---\\n\");\n \t\t\tlog_tree_commit(rev, commit);\n \t\t}\n-\t\telse\n-\t\t\tprintf(\"%s\\n\", buf);\n \t\tshown = 1;\n \t\tfree(commit->buffer);\n \t\tcommit->buffer = NULL;\n"},{"id":"18766","messageId":"7vu08soou3.fsf@assigned-by-dhcp.cox.net","threadId":"3895","inReplyTo":"Pine.LNX.4.64.0604161938070.3701@g5.osdl.org","subject":"Re: Fixes for option parsing","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-17T03:36:36Z","receivedAt":"2006-04-17T03:36:36Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> Also, I think the \"---\" printing should be removed, and moved into the \n> \"diffstat\" flushing code.\n\nI am not sure if that belongs to diffstat flush.  I was instead\nplanning to  move it to diff-flush code, and show it only when we\nactually show any diffs.  IOW, I think we would want it even\nwhen we are doing \"--pretty -p\", not \"--pretty --patch-with-stat\".\n"},{"id":"18772","messageId":"Pine.LNX.4.64.0604170747580.3701@g5.osdl.org","threadId":"3895","inReplyTo":"7vu08soou3.fsf@assigned-by-dhcp.cox.net","subject":"Re: Fixes for option parsing","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-04-17T14:51:49Z","receivedAt":"2006-04-17T14:51:49Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 16 Apr 2006, Junio C Hamano wrote:\n> \n> I am not sure if that belongs to diffstat flush.  I was instead\n> planning to  move it to diff-flush code\n\nWell, that's broken right now with the stat part, for some reason.\n\n> and show it only when we actually show any diffs.  IOW, I think we would \n> want it even when we are doing \"--pretty -p\", not \"--pretty \n> --patch-with-stat\".\n\nI'd personally prefer to see it only in front of diffstat, and leave the \npatches the way they have been before. For patches, I find the indentation \nof the commit message to be visually (a) much more pleasing and (b) more \npronounced than a \"---\" anyway.\n\nPerhaps configurable? git-format-patch doesn't want the indentation (and \nthus \"---\" makes sense for both patches and diffstat), while \"git log\" \ndefinitely does, because it's much more visually clear _and_ it makes the \nheader/comment structure totally unambiguous.\n\n\t\t\tLinus\n"},{"id":"18781","messageId":"Pine.LNX.4.64.0604171149330.3701@g5.osdl.org","threadId":"3895","inReplyTo":"7vbqv1oxie.fsf@assigned-by-dhcp.cox.net","subject":"Log message printout cleanups","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-04-17T18:59:32Z","receivedAt":"2006-04-17T18:59:32Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Sun, 16 Apr 2006, Junio C Hamano wrote:\n> \n> In the mid-term, I am hoping we can drop the generate_header()\n> callchain _and_ the custom code that formats commit log in-core,\n> found in cmd_log_wc().\n\nOk, this was nastier than expected, just because the dependencies between \nthe different log-printing stuff were absolutely _everywhere_, but here's \na patch that does exactly that.\n\nThe patch is not very easy to read, and the \"--patch-with-stat\" thing is \nstill broken (it does not call the \"show_log()\" thing properly for \nmerges). That's not a new bug. In the new world order it _should_ do \nsomething like\n\n\tif (rev->logopt)\n\t\tshow_log(rev, rev->logopt, \"---\\n\");\n\nbut it doesn't. I haven't looked at the --with-stat logic, so I left it \nalone.\n\nThat said, this patch removes more lines than it adds, and in particular, \nthe \"cmd_log_wc()\" loop is now a very clean:\n\n\twhile ((commit = get_revision(rev)) != NULL) {\n\t\tlog_tree_commit(rev, commit);\n\t\tfree(commit->buffer);\n\t\tcommit->buffer = NULL;\n\t}\n\nso it doesn't get much prettier than this. All the complexity is entirely \nhidden in log-tree.c, and any code that needs to flush the log literally \njust needs to do the \"if (rev->logopt) show_log(...)\" incantation.\n\nI had to make the combined_diff() logic take a \"struct rev_info\" instead \nof just a \"struct diff_options\", but that part is pretty clean.\n\nThis does change \"git whatchanged\" from using \"diff-tree\" as the commit \ndescriptor to \"commit\", and I changed one of the tests to reflect that new \nreality. Otherwise everything still passes, and my other tests look fine \ntoo.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n---\n combine-diff.c      |   47 ++++++--------\n diff-files.c        |   27 ++++----\n diff-tree.c         |    1 \n diff.h              |    8 +-\n git.c               |   57 +----------------\n log-tree.c          |  167 ++++++++++++++++++++++++++++-----------------------\n log-tree.h          |    5 ++\n rev-list.c          |    9 ++-\n revision.c          |    3 -\n revision.h          |    8 +-\n t/t1200-tutorial.sh |    4 +\n 11 files changed, 151 insertions(+), 185 deletions(-)\n\ndiff --git a/combine-diff.c b/combine-diff.c\nindex 9bd27f8..b4fa9c9 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -5,6 +5,7 @@ #include \"diff.h\"\n #include \"diffcore.h\"\n #include \"quote.h\"\n #include \"xdiff-interface.h\"\n+#include \"log-tree.h\"\n \n static int uninteresting(struct diff_filepair *p)\n {\n@@ -585,9 +586,9 @@ static void reuse_combine_diff(struct sl\n }\n \n static int show_patch_diff(struct combine_diff_path *elem, int num_parent,\n-\t\t\t   int dense, const char *header,\n-\t\t\t   struct diff_options *opt)\n+\t\t\t   int dense, struct rev_info *rev)\n {\n+\tstruct diff_options *opt = &rev->diffopt;\n \tunsigned long result_size, cnt, lno;\n \tchar *result, *cp, *ep;\n \tstruct sline *sline; /* survived lines */\n@@ -689,10 +690,8 @@ static int show_patch_diff(struct combin\n \tif (show_hunks || mode_differs || working_tree_file) {\n \t\tconst char *abb;\n \n-\t\tif (header) {\n-\t\t\tshown_header++;\n-\t\t\tprintf(\"%s%c\", header, opt->line_termination);\n-\t\t}\n+\t\tif (rev->loginfo)\n+\t\t\tshow_log(rev, rev->loginfo, \"\\n\");\n \t\tprintf(\"diff --%s \", dense ? \"cc\" : \"combined\");\n \t\tif (quote_c_style(elem->path, NULL, NULL, 0))\n \t\t\tquote_c_style(elem->path, NULL, stdout, 0);\n@@ -750,8 +749,9 @@ static int show_patch_diff(struct combin\n \n #define COLONS \"::::::::::::::::::::::::::::::::\"\n \n-static void show_raw_diff(struct combine_diff_path *p, int num_parent, const char *header, struct diff_options *opt)\n+static void show_raw_diff(struct combine_diff_path *p, int num_parent, struct rev_info *rev)\n {\n+\tstruct diff_options *opt = &rev->diffopt;\n \tint i, offset, mod_type = 'A';\n \tconst char *prefix;\n \tint line_termination, inter_name_termination;\n@@ -761,8 +761,8 @@ static void show_raw_diff(struct combine\n \tif (!line_termination)\n \t\tinter_name_termination = 0;\n \n-\tif (header)\n-\t\tprintf(\"%s%c\", header, line_termination);\n+\tif (rev->loginfo)\n+\t\tshow_log(rev, rev->loginfo, \"\\n\");\n \n \tfor (i = 0; i < num_parent; i++) {\n \t\tif (p->parent[i].mode)\n@@ -810,31 +810,31 @@ static void show_raw_diff(struct combine\n \t}\n }\n \n-int show_combined_diff(struct combine_diff_path *p,\n+void show_combined_diff(struct combine_diff_path *p,\n \t\t       int num_parent,\n \t\t       int dense,\n-\t\t       const char *header,\n-\t\t       struct diff_options *opt)\n+\t\t       struct rev_info *rev)\n {\n+\tstruct diff_options *opt = &rev->diffopt;\n \tif (!p->len)\n-\t\treturn 0;\n+\t\treturn;\n \tswitch (opt->output_format) {\n \tcase DIFF_FORMAT_RAW:\n \tcase DIFF_FORMAT_NAME_STATUS:\n \tcase DIFF_FORMAT_NAME:\n-\t\tshow_raw_diff(p, num_parent, header, opt);\n-\t\treturn 1;\n+\t\tshow_raw_diff(p, num_parent, rev);\n+\t\treturn;\n \n \tdefault:\n \tcase DIFF_FORMAT_PATCH:\n-\t\treturn show_patch_diff(p, num_parent, dense, header, opt);\n+\t\tshow_patch_diff(p, num_parent, dense, rev);\n \t}\n }\n \n-const char *diff_tree_combined_merge(const unsigned char *sha1,\n-\t\t\t     const char *header, int dense,\n-\t\t\t     struct diff_options *opt)\n+void diff_tree_combined_merge(const unsigned char *sha1,\n+\t\t\t     int dense, struct rev_info *rev)\n {\n+\tstruct diff_options *opt = &rev->diffopt;\n \tstruct commit *commit = lookup_commit(sha1);\n \tstruct diff_options diffopts;\n \tstruct commit_list *parents;\n@@ -874,17 +874,13 @@ const char *diff_tree_combined_merge(con\n \t\t\tint saved_format = opt->output_format;\n \t\t\topt->output_format = DIFF_FORMAT_RAW;\n \t\t\tfor (p = paths; p; p = p->next) {\n-\t\t\t\tif (show_combined_diff(p, num_parent, dense,\n-\t\t\t\t\t\t       header, opt))\n-\t\t\t\t\theader = NULL;\n+\t\t\t\tshow_combined_diff(p, num_parent, dense, rev);\n \t\t\t}\n \t\t\topt->output_format = saved_format;\n \t\t\tputchar(opt->line_termination);\n \t\t}\n \t\tfor (p = paths; p; p = p->next) {\n-\t\t\tif (show_combined_diff(p, num_parent, dense,\n-\t\t\t\t\t       header, opt))\n-\t\t\t\theader = NULL;\n+\t\t\tshow_combined_diff(p, num_parent, dense, rev);\n \t\t}\n \t}\n \n@@ -894,5 +890,4 @@ const char *diff_tree_combined_merge(con\n \t\tpaths = paths->next;\n \t\tfree(tmp);\n \t}\n-\treturn header;\n }\ndiff --git a/diff-files.c b/diff-files.c\nindex 3e7f5f1..ffbef48 100644\n--- a/diff-files.c\n+++ b/diff-files.c\n@@ -5,12 +5,14 @@\n  */\n #include \"cache.h\"\n #include \"diff.h\"\n+#include \"commit.h\"\n+#include \"revision.h\"\n \n static const char diff_files_usage[] =\n \"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]\"\n COMMON_DIFF_OPTIONS_HELP;\n \n-static struct diff_options diff_options;\n+static struct rev_info rev;\n static int silent = 0;\n static int diff_unmerged_stage = 2;\n static int combine_merges = 0;\n@@ -18,12 +20,12 @@ static int dense_combined_merges = 0;\n \n static void show_unmerge(const char *path)\n {\n-\tdiff_unmerge(&diff_options, path);\n+\tdiff_unmerge(&rev.diffopt, path);\n }\n \n static void show_file(int pfx, struct cache_entry *ce)\n {\n-\tdiff_addremove(&diff_options, pfx, ntohl(ce->ce_mode),\n+\tdiff_addremove(&rev.diffopt, pfx, ntohl(ce->ce_mode),\n \t\t       ce->sha1, ce->name, NULL);\n }\n \n@@ -31,7 +33,7 @@ static void show_modified(int oldmode, i\n \t\t\t  const unsigned char *old_sha1, const unsigned char *sha1,\n \t\t\t  char *path)\n {\n-\tdiff_change(&diff_options, oldmode, mode, old_sha1, sha1, path, NULL);\n+\tdiff_change(&rev.diffopt, oldmode, mode, old_sha1, sha1, path, NULL);\n }\n \n int main(int argc, const char **argv)\n@@ -41,7 +43,7 @@ int main(int argc, const char **argv)\n \tint entries, i;\n \n \tgit_config(git_diff_config);\n-\tdiff_setup(&diff_options);\n+\tdiff_setup(&rev.diffopt);\n \twhile (1 < argc && argv[1][0] == '-') {\n \t\tif (!strcmp(argv[1], \"--\")) {\n \t\t\targv++;\n@@ -74,7 +76,7 @@ int main(int argc, const char **argv)\n \t\t\tdense_combined_merges = combine_merges = 1;\n \t\telse {\n \t\t\tint diff_opt_cnt;\n-\t\t\tdiff_opt_cnt = diff_opt_parse(&diff_options,\n+\t\t\tdiff_opt_cnt = diff_opt_parse(&rev.diffopt,\n \t\t\t\t\t\t      argv+1, argc-1);\n \t\t\tif (diff_opt_cnt < 0)\n \t\t\t\tusage(diff_files_usage);\n@@ -89,13 +91,13 @@ int main(int argc, const char **argv)\n \t\targv++; argc--;\n \t}\n \tif (dense_combined_merges)\n-\t\tdiff_options.output_format = DIFF_FORMAT_PATCH;\n+\t\trev.diffopt.output_format = DIFF_FORMAT_PATCH;\n \n \t/* Find the directory, and set up the pathspec */\n \tpathspec = get_pathspec(prefix, argv + 1);\n \tentries = read_cache();\n \n-\tif (diff_setup_done(&diff_options) < 0)\n+\tif (diff_setup_done(&rev.diffopt) < 0)\n \t\tusage(diff_files_usage);\n \n \t/* At this point, if argc == 1, then we are doing everything.\n@@ -167,8 +169,7 @@ int main(int argc, const char **argv)\n \t\t\tif (combine_merges && num_compare_stages == 2) {\n \t\t\t\tshow_combined_diff(&combine.p, 2,\n \t\t\t\t\t\t   dense_combined_merges,\n-\t\t\t\t\t\t   NULL,\n-\t\t\t\t\t\t   &diff_options);\n+\t\t\t\t\t\t   &rev);\n \t\t\t\tfree(combine.p.path);\n \t\t\t\tcontinue;\n \t\t\t}\n@@ -194,7 +195,7 @@ int main(int argc, const char **argv)\n \t\t\tcontinue;\n \t\t}\n \t\tchanged = ce_match_stat(ce, &st, 0);\n-\t\tif (!changed && !diff_options.find_copies_harder)\n+\t\tif (!changed && !rev.diffopt.find_copies_harder)\n \t\t\tcontinue;\n \t\toldmode = ntohl(ce->ce_mode);\n \n@@ -207,7 +208,7 @@ int main(int argc, const char **argv)\n \t\t\t      ce->sha1, (changed ? null_sha1 : ce->sha1),\n \t\t\t      ce->name);\n \t}\n-\tdiffcore_std(&diff_options);\n-\tdiff_flush(&diff_options);\n+\tdiffcore_std(&rev.diffopt);\n+\tdiff_flush(&rev.diffopt);\n \treturn 0;\n }\ndiff --git a/diff-tree.c b/diff-tree.c\nindex e578798..7207867 100644\n--- a/diff-tree.c\n+++ b/diff-tree.c\n@@ -71,6 +71,7 @@ int main(int argc, const char **argv)\n \tnr_sha1 = 0;\n \tinit_revisions(opt);\n \topt->abbrev = 0;\n+\topt->diff = 1;\n \targc = setup_revisions(argc, argv, opt, NULL);\n \n \twhile (--argc > 0) {\ndiff --git a/diff.h b/diff.h\nindex f783bae..52fff66 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -6,6 +6,7 @@ #define DIFF_H\n \n #include \"tree-walk.h\"\n \n+struct rev_info;\n struct diff_options;\n \n typedef void (*change_fn_t)(struct diff_options *options,\n@@ -70,11 +71,10 @@ #define combine_diff_path_size(n, l) \\\n \t(sizeof(struct combine_diff_path) + \\\n \t sizeof(struct combine_diff_parent) * (n) + (l) + 1)\n \n-extern int show_combined_diff(struct combine_diff_path *elem, int num_parent,\n-\t\t\t      int dense, const char *header,\n-\t\t\t      struct diff_options *);\n+extern void show_combined_diff(struct combine_diff_path *elem, int num_parent,\n+\t\t\t      int dense, struct rev_info *);\n \n-extern const char *diff_tree_combined_merge(const unsigned char *sha1, const char *, int, struct diff_options *opt);\n+extern void diff_tree_combined_merge(const unsigned char *sha1, int, struct rev_info *);\n \n extern void diff_addremove(struct diff_options *,\n \t\t\t   int addremove,\ndiff --git a/git.c b/git.c\nindex fc4e429..016fa30 100644\n--- a/git.c\n+++ b/git.c\n@@ -282,75 +282,22 @@ static int cmd_log_wc(int argc, const ch\n \t\t      struct rev_info *rev)\n {\n \tstruct commit *commit;\n-\tchar *buf = xmalloc(LOGSIZE);\n-\tconst char *commit_prefix = \"commit \";\n-\tint shown = 0;\n \n \trev->abbrev = DEFAULT_ABBREV;\n \trev->commit_format = CMIT_FMT_DEFAULT;\n+\trev->verbose_header = 1;\n \targc = setup_revisions(argc, argv, rev, \"HEAD\");\n \n \tif (argc > 1)\n \t\tdie(\"unrecognized argument: %s\", argv[1]);\n-\tif (rev->commit_format == CMIT_FMT_ONELINE)\n-\t\tcommit_prefix = \"\";\n \n \tprepare_revision_walk(rev);\n \tsetup_pager();\n \twhile ((commit = get_revision(rev)) != NULL) {\n-\t\tunsigned long ofs = 0;\n-\n-\t\tif (shown && rev->diff &&\n-\t\t    rev->commit_format != CMIT_FMT_ONELINE)\n-\t\t\tputchar('\\n');\n-\n-\t\tofs = sprintf(buf, \"%s\", commit_prefix);\n-\t\tif (rev->abbrev_commit && rev->abbrev)\n-\t\t\tofs += sprintf(buf + ofs, \"%s\",\n-\t\t\t\t       find_unique_abbrev(commit->object.sha1,\n-\t\t\t\t\t\t\t  rev->abbrev));\n-\t\telse\n-\t\t\tofs += sprintf(buf + ofs, \"%s\",\n-\t\t\t\t       sha1_to_hex(commit->object.sha1));\n-\t\tif (rev->parents) {\n-\t\t\tstruct commit_list *parents = commit->parents;\n-\t\t\twhile (parents) {\n-\t\t\t\tstruct object *o = &(parents->item->object);\n-\t\t\t\tparents = parents->next;\n-\t\t\t\tif (o->flags & TMP_MARK)\n-\t\t\t\t\tcontinue;\n-\t\t\t\tofs += sprintf(buf + ofs, \" %s\",\n-\t\t\t\t\t       sha1_to_hex(o->sha1));\n-\t\t\t\to->flags |= TMP_MARK;\n-\t\t\t}\n-\t\t\t/* TMP_MARK is a general purpose flag that can\n-\t\t\t * be used locally, but the user should clean\n-\t\t\t * things up after it is done with them.\n-\t\t\t */\n-\t\t\tfor (parents = commit->parents;\n-\t\t\t     parents;\n-\t\t\t     parents = parents->next)\n-\t\t\t\tparents->item->object.flags &= ~TMP_MARK;\n-\t\t}\n-\t\tbuf[ofs++] = \n-\t\t\t(rev->commit_format == CMIT_FMT_ONELINE) ? ' ' : '\\n';\n-\t\tofs += pretty_print_commit(rev->commit_format, commit, ~0,\n-\t\t\t\t\t   buf + ofs,\n-\t\t\t\t\t   LOGSIZE - ofs - 20,\n-\t\t\t\t\t   rev->abbrev);\n-\n-\t\tif (rev->diff) {\n-\t\t\trev->use_precomputed_header = buf;\n-\t\t\tstrcpy(buf + ofs, \"\\n---\\n\");\n-\t\t\tlog_tree_commit(rev, commit);\n-\t\t}\n-\t\telse\n-\t\t\tprintf(\"%s\\n\", buf);\n-\t\tshown = 1;\n+\t\tlog_tree_commit(rev, commit);\n \t\tfree(commit->buffer);\n \t\tcommit->buffer = NULL;\n \t}\n-\tfree(buf);\n \treturn 0;\n }\n \ndiff --git a/log-tree.c b/log-tree.c\nindex af36f70..c0a4432 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -1,11 +1,51 @@\n+\n #include \"cache.h\"\n #include \"diff.h\"\n #include \"commit.h\"\n #include \"log-tree.h\"\n \n+void show_log(struct rev_info *opt, struct log_info *log, const char *sep)\n+{\n+\tstatic char this_header[16384];\n+\tstruct commit *commit = log->commit, *parent = log->parent;\n+\tint abbrev = opt->diffopt.abbrev;\n+\tint abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;\n+\tint len;\n+\n+\topt->loginfo = NULL;\n+\tif (!opt->verbose_header) {\n+\t\tputs(sha1_to_hex(commit->object.sha1));\n+\t\treturn;\n+\t}\n+\n+\t/*\n+\t * Whitespace between commit messages, unless we are oneline\n+\t */\n+\tif (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE)\n+\t\tputchar('\\n');\n+\topt->shown_one = 1;\n+\n+\t/*\n+\t * Print header line of header..\n+\t */\n+\tprintf(\"%s%s\",\n+\t\topt->commit_format == CMIT_FMT_ONELINE ? \"\" : \"commit \",\n+\t\tdiff_unique_abbrev(commit->object.sha1, abbrev_commit));\n+\tif (parent) \n+\t\tprintf(\" (from %s)\", diff_unique_abbrev(parent->object.sha1, abbrev_commit));\n+\tputchar(opt->commit_format == CMIT_FMT_ONELINE ? ' ' : '\\n');\n+\n+\t/*\n+\t * And then the pretty-printed message itself\n+\t */\n+\tlen = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev);\n+\tprintf(\"%s%s\", this_header, sep);\n+}\n+\n int log_tree_diff_flush(struct rev_info *opt)\n {\n \tdiffcore_std(&opt->diffopt);\n+\n \tif (diff_queue_is_empty()) {\n \t\tint saved_fmt = opt->diffopt.output_format;\n \t\topt->diffopt.output_format = DIFF_FORMAT_NO_OUTPUT;\n@@ -13,12 +53,9 @@ int log_tree_diff_flush(struct rev_info \n \t\topt->diffopt.output_format = saved_fmt;\n \t\treturn 0;\n \t}\n-\tif (opt->header) {\n-\t\tif (!opt->no_commit_id)\n-\t\t\tprintf(\"%s%c\", opt->header,\n-\t\t\t       opt->diffopt.line_termination);\n-\t\topt->header = NULL;\n-\t}\n+\n+\tif (opt->loginfo && !opt->no_commit_id)\n+\t\tshow_log(opt, opt->loginfo, \"\\n\");\n \tdiff_flush(&opt->diffopt);\n \treturn 1;\n }\n@@ -43,96 +80,78 @@ static int diff_root_tree(struct rev_inf\n \treturn retval;\n }\n \n-static const char *get_header(struct rev_info *opt,\n-\t\t\t\t   const unsigned char *commit_sha1,\n-\t\t\t\t   const unsigned char *parent_sha1,\n-\t\t\t\t   const struct commit *commit)\n-{\n-\tstatic char this_header[16384];\n-\tint offset;\n-\tunsigned long len;\n-\tint abbrev = opt->diffopt.abbrev;\n-\tconst char *msg = commit->buffer;\n-\n-\tif (opt->use_precomputed_header)\n-\t\treturn opt->use_precomputed_header;\n-\n-\tif (!opt->verbose_header)\n-\t\treturn sha1_to_hex(commit_sha1);\n-\n-\tlen = strlen(msg);\n-\n-\toffset = sprintf(this_header, \"%s%s \",\n-\t\t\t opt->header_prefix,\n-\t\t\t diff_unique_abbrev(commit_sha1, abbrev));\n-\tif (commit_sha1 != parent_sha1)\n-\t\toffset += sprintf(this_header + offset, \"(from %s)\\n\",\n-\t\t\t\t  parent_sha1\n-\t\t\t\t  ? diff_unique_abbrev(parent_sha1, abbrev)\n-\t\t\t\t  : \"root\");\n-\telse\n-\t\toffset += sprintf(this_header + offset, \"(from parents)\\n\");\n-\toffset += pretty_print_commit(opt->commit_format, commit, len,\n-\t\t\t\t      this_header + offset,\n-\t\t\t\t      sizeof(this_header) - offset, abbrev);\n-\treturn this_header;\n-}\n-\n-static const char *generate_header(struct rev_info *opt,\n-\t\t\t\t\tconst unsigned char *commit_sha1,\n-\t\t\t\t\tconst unsigned char *parent_sha1,\n-\t\t\t\t\tconst struct commit *commit)\n-{\n-\tconst char *header = get_header(opt, commit_sha1, parent_sha1, commit);\n-\n-\tif (opt->always_show_header) {\n-\t\tputs(header);\n-\t\theader = NULL;\n-\t}\n-\treturn header;\n-}\n-\n static int do_diff_combined(struct rev_info *opt, struct commit *commit)\n {\n \tunsigned const char *sha1 = commit->object.sha1;\n \n-\topt->header = generate_header(opt, sha1, sha1, commit);\n-\topt->header = diff_tree_combined_merge(sha1, opt->header,\n-\t\t\t\t\t\topt->dense_combined_merges,\n-\t\t\t\t\t\t&opt->diffopt);\n-\tif (!opt->header && opt->verbose_header)\n-\t\topt->header_prefix = \"\\ndiff-tree \";\n-\treturn 0;\n+\tdiff_tree_combined_merge(sha1, opt->dense_combined_merges, opt);\n+\treturn !opt->loginfo;\n }\n \n-int log_tree_commit(struct rev_info *opt, struct commit *commit)\n+/*\n+ * Show the diff of a commit.\n+ *\n+ * Return true if we printed any log info messages\n+ */\n+static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log_info *log)\n {\n+\tint showed_log;\n \tstruct commit_list *parents;\n \tunsigned const char *sha1 = commit->object.sha1;\n \n+\tif (!opt->diff)\n+\t\treturn 0;\n+\n \t/* Root commit? */\n-\tif (opt->show_root_diff && !commit->parents) {\n-\t\topt->header = generate_header(opt, sha1, NULL, commit);\n-\t\tdiff_root_tree(opt, sha1, \"\");\n+\tparents = commit->parents;\n+\tif (!parents) {\n+\t\tif (opt->show_root_diff)\n+\t\t\tdiff_root_tree(opt, sha1, \"\");\n+\t\treturn !opt->loginfo;\n \t}\n \n \t/* More than one parent? */\n-\tif (commit->parents && commit->parents->next) {\n+\tif (parents && parents->next) {\n \t\tif (opt->ignore_merges)\n \t\t\treturn 0;\n \t\telse if (opt->combine_merges)\n \t\t\treturn do_diff_combined(opt, commit);\n+\n+\t\t/* If we show individual diffs, show the parent info */\n+\t\tlog->parent = parents->item;\n \t}\n \n-\tfor (parents = commit->parents; parents; parents = parents->next) {\n+\tshowed_log = 0;\n+\tfor (;;) {\n \t\tstruct commit *parent = parents->item;\n-\t\tunsigned const char *psha1 = parent->object.sha1;\n-\t\topt->header = generate_header(opt, sha1, psha1, commit);\n-\t\tdiff_tree_sha1(psha1, sha1, \"\", &opt->diffopt);\n-\t\tlog_tree_diff_flush(opt);\t\t\n \n-\t\tif (!opt->header && opt->verbose_header)\n-\t\t\topt->header_prefix = \"\\ndiff-tree \";\n+\t\tdiff_tree_sha1(parent->object.sha1, sha1, \"\", &opt->diffopt);\n+\t\tlog_tree_diff_flush(opt);\n+\n+\t\tshowed_log |= !opt->loginfo;\n+\n+\t\t/* Set up the log info for the next parent, if any.. */\n+\t\tparents = parents->next;\n+\t\tif (!parents)\n+\t\t\tbreak;\n+\t\tlog->parent = parents->item;\n+\t\topt->loginfo = log;\n+\t}\n+\treturn showed_log;\n+}\n+\n+int log_tree_commit(struct rev_info *opt, struct commit *commit)\n+{\n+\tstruct log_info log;\n+\n+\tlog.commit = commit;\n+\tlog.parent = NULL;\n+\topt->loginfo = &log;\n+\n+\tif (!log_tree_diff(opt, commit, &log) && opt->loginfo && opt->always_show_header) {\n+\t\tlog.parent = NULL;\n+\t\tshow_log(opt, opt->loginfo, \"\");\n \t}\n+\topt->loginfo = NULL;\n \treturn 0;\n }\ndiff --git a/log-tree.h b/log-tree.h\nindex 91a909b..a26e484 100644\n--- a/log-tree.h\n+++ b/log-tree.h\n@@ -3,9 +3,14 @@ #define LOG_TREE_H\n \n #include \"revision.h\"\n \n+struct log_info {\n+\tstruct commit *commit, *parent;\n+};\n+\n void init_log_tree_opt(struct rev_info *);\n int log_tree_diff_flush(struct rev_info *);\n int log_tree_commit(struct rev_info *, struct commit *);\n int log_tree_opt_parse(struct rev_info *, const char **, int);\n+void show_log(struct rev_info *opt, struct log_info *log, const char *sep);\n \n #endif\ndiff --git a/rev-list.c b/rev-list.c\nindex b75da12..099373c 100644\n--- a/rev-list.c\n+++ b/rev-list.c\n@@ -41,13 +41,14 @@ struct rev_info revs;\n static int bisect_list = 0;\n static int show_timestamp = 0;\n static int hdr_termination = 0;\n+static const char *header_prefix;\n \n static void show_commit(struct commit *commit)\n {\n \tif (show_timestamp)\n \t\tprintf(\"%lu \", commit->date);\n-\tif (*revs.header_prefix)\n-\t\tfputs(revs.header_prefix, stdout);\n+\tif (header_prefix)\n+\t\tfputs(header_prefix, stdout);\n \tif (commit->object.flags & BOUNDARY)\n \t\tputchar('-');\n \tif (revs.abbrev_commit && revs.abbrev)\n@@ -322,9 +323,9 @@ int main(int argc, const char **argv)\n \t\t/* The command line has a --pretty  */\n \t\thdr_termination = '\\n';\n \t\tif (revs.commit_format == CMIT_FMT_ONELINE)\n-\t\t\trevs.header_prefix = \"\";\n+\t\t\theader_prefix = \"\";\n \t\telse\n-\t\t\trevs.header_prefix = \"commit \";\n+\t\t\theader_prefix = \"commit \";\n \t}\n \n \tlist = revs.commits;\ndiff --git a/revision.c b/revision.c\nindex 3cd6a2e..2976497 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -498,7 +498,6 @@ void init_revisions(struct rev_info *rev\n \trevs->topo_setter = topo_sort_default_setter;\n \trevs->topo_getter = topo_sort_default_getter;\n \n-\trevs->header_prefix = \"\";\n \trevs->commit_format = CMIT_FMT_DEFAULT;\n \n \tdiff_setup(&revs->diffopt);\n@@ -675,12 +674,10 @@ int setup_revisions(int argc, const char\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"-v\")) {\n \t\t\t\trevs->verbose_header = 1;\n-\t\t\t\trevs->header_prefix = \"diff-tree \";\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strncmp(arg, \"--pretty\", 8)) {\n \t\t\t\trevs->verbose_header = 1;\n-\t\t\t\trevs->header_prefix = \"diff-tree \";\n \t\t\t\trevs->commit_format = get_commit_format(arg+8);\n \t\t\t\tcontinue;\n \t\t\t}\ndiff --git a/revision.h b/revision.h\nindex 872bcd8..48d7b4c 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -11,6 +11,7 @@ #define BOUNDARY_SHOW\t(1u<<6)\n #define ADDED\t\t(1u<<7)\t/* Parents already parsed and added? */\n \n struct rev_info;\n+struct log_info;\n \n typedef void (prune_fn_t)(struct rev_info *revs, struct commit *commit);\n \n@@ -52,12 +53,11 @@ struct rev_info {\n \t\t\talways_show_header:1;\n \n \t/* Format info */\n-\tunsigned int\tabbrev_commit:1;\n+\tunsigned int\tshown_one:1,\n+\t\t\tabbrev_commit:1;\n \tunsigned int\tabbrev;\n \tenum cmit_fmt\tcommit_format;\n-\tconst char\t*header_prefix;\n-\tconst char\t*header;\n-\tconst char\t*use_precomputed_header;\n+\tstruct log_info *loginfo;\n \n \t/* special limits */\n \tint max_count;\ndiff --git a/t/t1200-tutorial.sh b/t/t1200-tutorial.sh\nindex 1002413..16b3ea9 100755\n--- a/t/t1200-tutorial.sh\n+++ b/t/t1200-tutorial.sh\n@@ -49,7 +49,7 @@ #rm hello\n #test_expect_success 'git-read-tree --reset HEAD' \"git-read-tree --reset HEAD ; test \\\"hello: needs update\\\" = \\\"$(git-update-index --refresh)\\\"\"\n \n cat > whatchanged.expect << EOF\n-diff-tree VARIABLE (from root)\n+commit VARIABLE\n Author: VARIABLE\n Date:   VARIABLE\n \n@@ -72,7 +72,7 @@ index 0000000..557db03\n EOF\n \n git-whatchanged -p --root | \\\n-\tsed -e \"1s/^\\(.\\{10\\}\\).\\{40\\}/\\1VARIABLE/\" \\\n+\tsed -e \"1s/^\\(.\\{7\\}\\).\\{40\\}/\\1VARIABLE/\" \\\n \t\t-e \"2,3s/^\\(.\\{8\\}\\).*$/\\1VARIABLE/\" \\\n > whatchanged.output\n test_expect_success 'git-whatchanged -p --root' 'cmp whatchanged.expect whatchanged.output'\n"},{"id":"18790","messageId":"7vodyzkehq.fsf@assigned-by-dhcp.cox.net","threadId":"3895","inReplyTo":"Pine.LNX.4.64.0604171149330.3701@g5.osdl.org","subject":"Re: Log message printout cleanups","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-17T22:45:53Z","receivedAt":"2006-04-17T22:45:53Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> On Sun, 16 Apr 2006, Junio C Hamano wrote:\n>> \n>> In the mid-term, I am hoping we can drop the generate_header()\n>> callchain _and_ the custom code that formats commit log in-core,\n>> found in cmd_log_wc().\n>\n> Ok, this was nastier than expected, just because the dependencies between \n> the different log-printing stuff were absolutely _everywhere_, but here's \n> a patch that does exactly that.\n\nI like the new cmd_log_wc() loop very much; I was planning to do\nthis myself, but I was too slow ;-).\n\nThanks.\n"},{"id":"18791","messageId":"Pine.LNX.4.64.0604171647380.3701@g5.osdl.org","threadId":"3895","inReplyTo":"7vodyzkehq.fsf@assigned-by-dhcp.cox.net","subject":"Re: Log message printout cleanups","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-04-17T23:59:42Z","receivedAt":"2006-04-17T23:59:42Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 17 Apr 2006, Junio C Hamano wrote:\n> \n> I like the new cmd_log_wc() loop very much; I was planning to do\n> this myself, but I was too slow ;-).\n> \n> Thanks.\n\nHere's a further patch on top of the previous one with cosmetic \nimprovements (no \"real\" code changes, just trivial updates):\n\n - it gets the \"---\" before a diffstat right, including for the combined \n   merge case. Righ now the logic is that we always use \"---\" when we have \n   a diffstat, and an empty line otherwise. That's how I visually prefer \n   it, but hey, it can be tweaked later.\n\n - I made \"diff --cc/combined\" add the \"---/+++\" header lines too. The \n   thing won't be mistaken for a valid diff, since the \"@@\" lines have too \n   many \"@\" characters (three or more), but it just makes it visually \n   match a real diff, which at least to me makes a big difference in \n   readability. Without them, it just looks very \"wrong\".\n\n   I guess I should have taken the filename from each individual entry \n   (and had one \"---\" file per parent), but I didn't even bother to try to \n   see how that works, so this was the simple thing. \n\nWith this, doing a\n\n\tgit log --cc --patch-with-stat\n\nlooks quite readable, I think. The only nagging issue - as far as I'm \nconcerned - is that diffstats for merges are pretty questionable the way \nthey are done now. I suspect it would be better to just have the _first_ \ndiffstat, and always make the merge diffstat be the one for \"result \nagainst first parent\".\n\nI realize that Dscho has been looking at some much \"fancier\" merge \ndiffstats, but I really do believe that the \"what got merged\" difference \nto the original branch (ie parent 1) is what you want in practice.\n\nAnyway, that's a totally separate issue, I'll let you guys fight that out.\n\n\t\tLinus\n\n---\ndiff --git a/combine-diff.c b/combine-diff.c\nindex b4fa9c9..aef9006 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -585,6 +585,16 @@ static void reuse_combine_diff(struct sl\n \tsline->p_lno[i] = sline->p_lno[j];\n }\n \n+static void dump_quoted_path(const char *prefix, const char *path)\n+{\n+\tfputs(prefix, stdout);\n+\tif (quote_c_style(path, NULL, NULL, 0))\n+\t\tquote_c_style(path, NULL, stdout, 0);\n+\telse\n+\t\tprintf(\"%s\", path);\n+\tputchar('\\n');\n+}\n+\n static int show_patch_diff(struct combine_diff_path *elem, int num_parent,\n \t\t\t   int dense, struct rev_info *rev)\n {\n@@ -692,12 +702,7 @@ static int show_patch_diff(struct combin\n \n \t\tif (rev->loginfo)\n \t\t\tshow_log(rev, rev->loginfo, \"\\n\");\n-\t\tprintf(\"diff --%s \", dense ? \"cc\" : \"combined\");\n-\t\tif (quote_c_style(elem->path, NULL, NULL, 0))\n-\t\t\tquote_c_style(elem->path, NULL, stdout, 0);\n-\t\telse\n-\t\t\tprintf(\"%s\", elem->path);\n-\t\tputchar('\\n');\n+\t\tdump_quoted_path(dense ? \"diff --cc \" : \"diff --combined \", elem->path);\n \t\tprintf(\"index \");\n \t\tfor (i = 0; i < num_parent; i++) {\n \t\t\tabb = find_unique_abbrev(elem->parent[i].sha1,\n@@ -728,6 +733,8 @@ static int show_patch_diff(struct combin\n \t\t\t}\n \t\t\tputchar('\\n');\n \t\t}\n+\t\tdump_quoted_path(\"--- a/\", elem->path);\n+\t\tdump_quoted_path(\"+++ b/\", elem->path);\n \t\tdump_sline(sline, cnt, num_parent);\n \t}\n \tfree(result);\n@@ -861,6 +868,9 @@ void diff_tree_combined_merge(const unsi\n \t\t\t       &diffopts);\n \t\tdiffcore_std(&diffopts);\n \t\tpaths = intersect_paths(paths, i, num_parent);\n+\n+\t\tif (diffopts.with_stat && rev->loginfo)\n+\t\t\tshow_log(rev, rev->loginfo, \"---\\n\");\n \t\tdiff_flush(&diffopts);\n \t}\n \ndiff --git a/log-tree.c b/log-tree.c\nindex c0a4432..9e54164 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -1,4 +1,3 @@\n-\n #include \"cache.h\"\n #include \"diff.h\"\n #include \"commit.h\"\n@@ -55,7 +54,7 @@ int log_tree_diff_flush(struct rev_info \n \t}\n \n \tif (opt->loginfo && !opt->no_commit_id)\n-\t\tshow_log(opt, opt->loginfo, \"\\n\");\n+\t\tshow_log(opt, opt->loginfo, opt->diffopt.with_stat ? \"---\\n\" : \"\\n\");\n \tdiff_flush(&opt->diffopt);\n \treturn 1;\n }\n"},{"id":"18792","messageId":"7vejzvka09.fsf@assigned-by-dhcp.cox.net","threadId":"3895","inReplyTo":"Pine.LNX.4.64.0604171647380.3701@g5.osdl.org","subject":"Re: Log message printout cleanups","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-18T00:22:46Z","receivedAt":"2006-04-18T00:22:46Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> I suspect it would be better to just have the _first_ \n> diffstat, and always make the merge diffstat be the one for \"result \n> against first parent\".\n>\n> I realize that Dscho has been looking at some much \"fancier\" merge \n> diffstats, but I really do believe that the \"what got merged\" difference \n> to the original branch (ie parent 1) is what you want in practice.\n>\n> Anyway, that's a totally separate issue, I'll let you guys fight that out.\n\nI agree with your opinion 100% from usability point of view,\nalthough I admit one of the fancier format was suggested by me.\n\nShowing stat from the first parent makes --stat inconsistent\nfrom what the \"fake diff\" shows, but I think it is a sensible\nthing to do.  The purpose of two are different in that (1) stat\nis a way to see the extent of damage to what you had before you\nmerged, and (2) --cc is a way to see how complex the merge was,\nto spot obvious merge mistakes and sanity check the merge at the\ncontents level.\n"},{"id":"18794","messageId":"7v7j5nk9bf.fsf@assigned-by-dhcp.cox.net","threadId":"3895","inReplyTo":"7vejzvka09.fsf@assigned-by-dhcp.cox.net","subject":"Re: Log message printout cleanups","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-18T00:37:40Z","receivedAt":"2006-04-18T00:37:40Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":" Linus Torvalds <torvalds@osdl.org> writes:\n\n> I realize that Dscho has been looking at some much \"fancier\" merge \n> diffstats, but I really do believe that the \"what got merged\" difference \n> to the original branch (ie parent 1) is what you want in practice.\n\nThese days I find myself running \"git log --stat\" more often\nthan \"git whatchanged\"; it looks so much nicer ;-).\n\nThanks for a job well done.\n"},{"id":"18795","messageId":"Pine.LNX.4.64.0604171727050.3701@g5.osdl.org","threadId":"3895","inReplyTo":"7vejzvka09.fsf@assigned-by-dhcp.cox.net","subject":"Re: Log message printout cleanups","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-04-18T00:43:40Z","receivedAt":"2006-04-18T00:43:40Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 17 Apr 2006, Junio C Hamano wrote:\n> \n> Showing stat from the first parent makes --stat inconsistent\n> from what the \"fake diff\" shows, but I think it is a sensible\n> thing to do.\n\nIt might actually be even more than sensible: it's a huge hint especially \nfor users of \"--cc\" (or even --combined) that the diff we show really \nisn't all there is to the merge. So having the \"full\" diffstat is not just \nusually what you want to see, it's probably also a really good hint for \npeople who otherwise might not realize that our combined diffs have \nskipped all the common parts.\n\nBtw, I just noticed that I broke \"--pretty=oneline\" AGAIN. That thing is \nvery special, since pretty_print_commit() will _remove_ the newline at the \nend of it, so we want to have an extra separator between the things.\n\nI added a honking big comment this time, so that (a) I don't forget this \n_again_ (I broke \"oneline\" several times during this printout cleanup), \nand so that people can understand _why_ the code does what it does.\n\nNow, arguably the alternate fix is to always have the '\\n' at the end in \npretty-print-commit, but git-rev-list depends on the current behaviour \n(but we could have git-rev-list remove it, whatever).\n\nWith the big comment, the code hopefully doesn't get broken again. And now \nthings like\n\n\tgit log --pretty=oneline --cc --patch-with-stat\n\nworks (even if that is admittedly a totally insane combination: if you \nwant the patch, having the \"oneline\" log format is just crazy, but hey, \nit _works_. Even insane people are people).\n\n\t\tLinus\n---\ndiff --git a/log-tree.c b/log-tree.c\nindex c0a4432..9634c46 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -10,6 +9,7 @@ void show_log(struct rev_info *opt, stru\n \tstruct commit *commit = log->commit, *parent = log->parent;\n \tint abbrev = opt->diffopt.abbrev;\n \tint abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;\n+\tconst char *extra;\n \tint len;\n \n \topt->loginfo = NULL;\n@@ -19,8 +19,17 @@ void show_log(struct rev_info *opt, stru\n \t}\n \n \t/*\n-\t * Whitespace between commit messages, unless we are oneline\n+\t * The \"oneline\" format has several special cases:\n+\t *  - The pretty-printed commit lacks a newline at the end\n+\t *    of the buffer, but we do want to make sure that we\n+\t *    have a newline there. If the separator isn't already\n+\t *    a newline, add an extra one.\n+\t *  - unlike other log messages, the one-line format does\n+\t *    not have an empty line between entries.\n \t */\n+\textra = \"\";\n+\tif (*sep != '\\n' && opt->commit_format == CMIT_FMT_ONELINE)\n+\t\textra = \"\\n\";\n \tif (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE)\n \t\tputchar('\\n');\n \topt->shown_one = 1;\n@@ -39,7 +48,7 @@ void show_log(struct rev_info *opt, stru\n \t * And then the pretty-printed message itself\n \t */\n \tlen = pretty_print_commit(opt->commit_format, commit, ~0u, this_header, sizeof(this_header), abbrev);\n-\tprintf(\"%s%s\", this_header, sep);\n+\tprintf(\"%s%s%s\", this_header, extra, sep);\n }\n \n int log_tree_diff_flush(struct rev_info *opt)\n"},{"id":"18796","messageId":"Pine.LNX.4.64.0604171751540.3701@g5.osdl.org","threadId":"3895","inReplyTo":"7v7j5nk9bf.fsf@assigned-by-dhcp.cox.net","subject":"Re: Log message printout cleanups","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-04-18T00:57:20Z","receivedAt":"2006-04-18T00:57:20Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 17 Apr 2006, Junio C Hamano wrote:\n> \n> These days I find myself running \"git log --stat\" more often\n> than \"git whatchanged\"; it looks so much nicer ;-).\n> \n> Thanks for a job well done.\n\nThere's actually something _wrong_ with \"git log --stat\". \n\nWhat happens is that \"git log\" will enable \"rev.combine_merges\" by \ndefault, and that means that together with \"--stat\" ignoring merges by \ndefault, it will _instead_ generate a \"--cc\" diff for that merge.\n\nI think that behaviour was introduced by the \"--stat\" code by Dscho, and \nnot by my rewrite.\n\nYou can see it on the kernel archive on commit 88dd9c16, for example:\n\n\tgit log --stat 88dd9c16\n\nwill make it pretty obvious what I'm talking about.\n\nI still haven't looked a lot at what the diffstat code does, so I don't \nknow what the obvious fix is. Maybe setting \"ignore_merges\". And maybe \njust making the diffstat thing do the right thing for merges. \n\nI'll leave that up to you, I'm getting pretty fed up with \"git log\" right \nnow ;)\n\n\t\tLinus\n"},{"id":"18797","messageId":"7vd5ffig70.fsf@assigned-by-dhcp.cox.net","threadId":"3895","inReplyTo":"Pine.LNX.4.64.0604171751540.3701@g5.osdl.org","subject":"Re: Log message printout cleanups","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-18T05:52:03Z","receivedAt":"2006-04-18T05:52:03Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> There's actually something _wrong_ with \"git log --stat\". \n>\n> What happens is that \"git log\" will enable \"rev.combine_merges\" by \n> default, and that means that together with \"--stat\" ignoring merges by \n> default, it will _instead_ generate a \"--cc\" diff for that merge.\n>\n> I'll leave that up to you, I'm getting pretty fed up with \"git log\" right \n> now ;)\n\nThe primary problem is that the code in \"next\" was before the\n\"fancy diffstat for combined\" work by Johannes; it punts and\nalways shows the patch output even when stat is asked for.\n\nI think this does what both of us want.  One thing I noticed is\nthat log-tree-diff-flush does \"---\\n\" under --patch-with-stat\nbut not under --stat; I matched that here.\n\n-- >8 --\ncombine-diff: show diffstat with the first parent.\n\nEven under combine-diff, asking for stat (either with --stat or\n--patch-with-stat) gives you diffstat for the first parent.\n\nWhile the combined patch is useful to highlight the complexity\nand interaction of the parts touched by all branches when\nreviewing a merge commit, diffstat is a tool to assess the\nextent of damage the merge brings in, and showing stat with the\nfirst parent is more sensible than clever per-parent diffstat.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n\n---\n\n combine-diff.c |   24 +++++++++++++++++++-----\n 1 files changed, 19 insertions(+), 5 deletions(-)\n\ndiff --git a/combine-diff.c b/combine-diff.c\nindex aef9006..27f6f57 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -831,10 +831,11 @@ void show_combined_diff(struct combine_d\n \tcase DIFF_FORMAT_NAME:\n \t\tshow_raw_diff(p, num_parent, rev);\n \t\treturn;\n-\n-\tdefault:\n \tcase DIFF_FORMAT_PATCH:\n \t\tshow_patch_diff(p, num_parent, dense, rev);\n+\t\treturn;\n+\tdefault:\n+\t\treturn;\n \t}\n }\n \n@@ -847,10 +848,13 @@ void diff_tree_combined_merge(const unsi\n \tstruct commit_list *parents;\n \tstruct combine_diff_path *p, *paths = NULL;\n \tint num_parent, i, num_paths;\n+\tint do_diffstat;\n \n+\tdo_diffstat = (opt->output_format == DIFF_FORMAT_DIFFSTAT ||\n+\t\t       opt->with_stat);\n \tdiffopts = *opt;\n-\tdiffopts.output_format = DIFF_FORMAT_NO_OUTPUT;\n \tdiffopts.with_raw = 0;\n+\tdiffopts.with_stat = 0;\n \tdiffopts.recursive = 1;\n \n \t/* count parents */\n@@ -864,14 +868,24 @@ void diff_tree_combined_merge(const unsi\n \t     parents;\n \t     parents = parents->next, i++) {\n \t\tstruct commit *parent = parents->item;\n+\t\t/* show stat against the first parent even\n+\t\t * when doing combined diff.\n+\t\t */\n+\t\tif (i == 0 && do_diffstat)\n+\t\t\tdiffopts.output_format = DIFF_FORMAT_DIFFSTAT;\n+\t\telse\n+\t\t\tdiffopts.output_format = DIFF_FORMAT_NO_OUTPUT;\n \t\tdiff_tree_sha1(parent->object.sha1, commit->object.sha1, \"\",\n \t\t\t       &diffopts);\n \t\tdiffcore_std(&diffopts);\n \t\tpaths = intersect_paths(paths, i, num_parent);\n \n-\t\tif (diffopts.with_stat && rev->loginfo)\n-\t\t\tshow_log(rev, rev->loginfo, \"---\\n\");\n+\t\tif (do_diffstat && rev->loginfo)\n+\t\t\tshow_log(rev, rev->loginfo,\n+\t\t\t\t opt->with_stat ? \"---\\n\" : \"\\n\");\n \t\tdiff_flush(&diffopts);\n+\t\tif (opt->with_stat)\n+\t\t\tputchar('\\n');\n \t}\n \n \t/* find out surviving paths */\n"},{"id":"18808","messageId":"Pine.LNX.4.64.0604180827040.3701@g5.osdl.org","threadId":"3895","inReplyTo":"7vd5ffig70.fsf@assigned-by-dhcp.cox.net","subject":"Re: Log message printout cleanups","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-04-18T15:37:22Z","receivedAt":"2006-04-18T15:37:22Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 17 Apr 2006, Junio C Hamano wrote:\n> \n> I think this does what both of us want.  One thing I noticed is\n> that log-tree-diff-flush does \"---\\n\" under --patch-with-stat\n> but not under --stat; I matched that here.\n\nLooks very nice now. Thanks.\n\nMy only slight complaint is that I think it should be possible to do\n\n\tgit log --cc --stat\n\nto get both patches and stat, but that doesn't work. You have to write\n\n\tgit log --cc --patch-with-stat\n\nto get both ;/\n\nBut that's just a small (and unimportant) detail.\n\nHere's another really small detail: with merges, there's two newlines \nbetween the diffstat and the diff. That bug doesn't show up with regular \ncommits.\n\nBut this all looks very nice. I agree that \"git log --stat\" is a very nice \nway to look at the log, much better than \"git-whatchanged\". And the thing \nthat has really fallen out of this all is how you can do\n\n\tgit log --stat --full-diff drivers/pci/\n\nwhich git-whatchanged historically didn't support (of course, now it \ndoes, but you're right, the new \"git log\" is so nice that I'm starting to \nteach myself not to use \"git whatchanged\" any more).\n\n\t\tLinus\n"},{"id":"18809","messageId":"Pine.LNX.4.64.0604180854550.3701@g5.osdl.org","threadId":"3895","inReplyTo":"Pine.LNX.4.64.0604180827040.3701@g5.osdl.org","subject":"Re: Log message printout cleanups","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-04-18T16:06:02Z","receivedAt":"2006-04-18T16:06:02Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 18 Apr 2006, Linus Torvalds wrote:\n> \n> But this all looks very nice. I agree that \"git log --stat\" is a very nice \n> way to look at the log, much better than \"git-whatchanged\". And the thing \n> that has really fallen out of this all is how you can do\n> \n> \tgit log --stat --full-diff drivers/pci/\n> \n> which git-whatchanged historically didn't support (of course, now it \n> does, but you're right, the new \"git log\" is so nice that I'm starting to \n> teach myself not to use \"git whatchanged\" any more).\n\nJunio, I just tested the \"master\" branch, and \"git log --stat\" doesn't \nwork there. You may _think_ it works because you've tested it on the git \ntree, were it looks like it is working, but it's missing setting \n\"recursive\", so it won't actually go into any subdirectories (so it mostly \nworks for git itself which has most stuff in the top-level directory, but \nit almost completely doesn't work for linux)\n\n\"git log -r --stat\" works, so it's really just a missing that.\n\nSomething like this (this is master, not \"next\").\n\n\"next\" actually has the same bug, but there it is hidden because \"git log\" \nsets recursive automatically. Which may or may not be appropriate. It \nmight be worthwhile to fix it properly in \"next\" too (there the same \n\n\tif (...output_format == DIFF_FORMAT_PATCH)\n\t\t...recursive = 1;\n\nis in revision.c: setup_revisions()).\n\n\t\tLinus\n\n----\ndiff --git a/git.c b/git.c\nindex 140ed18..f98c9ba 100644\n--- a/git.c\n+++ b/git.c\n@@ -344,7 +344,8 @@ static int cmd_log(int argc, const char \n \t\t\topt.ignore_merges = 0;\n \t\tif (opt.dense_combined_merges)\n \t\t\topt.diffopt.output_format = DIFF_FORMAT_PATCH;\n-\t\tif (opt.diffopt.output_format == DIFF_FORMAT_PATCH)\n+\t\tif (opt.diffopt.output_format == DIFF_FORMAT_PATCH ||\n+\t\t    opt.diffopt.output_format == DIFF_FORMAT_DIFFSTAT)\n \t\t\topt.diffopt.recursive = 1;\n \t\tif (!full_diff && rev.prune_data)\n \t\t\tdiff_tree_setup_paths(rev.prune_data, &opt.diffopt);\n"},{"id":"18810","messageId":"7vodyyg77u.fsf@assigned-by-dhcp.cox.net","threadId":"3895","inReplyTo":"Pine.LNX.4.64.0604180854550.3701@g5.osdl.org","subject":"Re: Log message printout cleanups","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-18T16:48:53Z","receivedAt":"2006-04-18T16:48:53Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> Junio, I just tested the \"master\" branch, and \"git log --stat\" doesn't \n> work there.\n\nAhhhh.\n\n>  \t\t\topt.diffopt.output_format = DIFF_FORMAT_PATCH;\n> -\t\tif (opt.diffopt.output_format == DIFF_FORMAT_PATCH)\n> +\t\tif (opt.diffopt.output_format == DIFF_FORMAT_PATCH ||\n> +\t\t    opt.diffopt.output_format == DIFF_FORMAT_DIFFSTAT)\n>  \t\t\topt.diffopt.recursive = 1;\n\nWhen pointed out in a patch from, it looks so obvious.  *BLUSH*\n\nThanks.\n"},{"id":"18818","messageId":"7v7j5mg2ds.fsf_-_@assigned-by-dhcp.cox.net","threadId":"3895","inReplyTo":"Pine.LNX.4.64.0604180854550.3701@g5.osdl.org","subject":"[PATCH] diff --stat: make sure to set recursive.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-04-18T18:33:19Z","receivedAt":"2006-04-18T18:33:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> Junio, I just tested the \"master\" branch, and \"git log --stat\" doesn't \n> work there. You may _think_ it works because you've tested it on the git \n> tree, were it looks like it is working, but it's missing setting \n> \"recursive\", so it won't actually go into any subdirectories (so it mostly \n> works for git itself which has most stuff in the top-level directory, but \n> it almost completely doesn't work for linux)\n\nTrue.  It shows that I usually install and use \"next\" version\nexclusively, which is fine during the normal development phase,\nbut it was a wrong thing to keep doing just before the release.\n\nI think diff-tree --stat has the same problem in \"master\", so\nI'd do it slightly differently.\n\n-- >8 --\nJust like \"patch\" format always needs recursive, \"diffstat\"\nformat does not make sense without setting recursive.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n\n---\n\n diff-tree.c |    3 ---\n diff.c      |   10 ++++++++++\n git.c       |    2 --\n 3 files changed, 10 insertions(+), 5 deletions(-)\n\nf56ef54174598d5362d0446c5a560cb5892537c2\ndiff --git a/diff-tree.c b/diff-tree.c\nindex 7015b06..d1c61c8 100644\n--- a/diff-tree.c\n+++ b/diff-tree.c\n@@ -117,9 +117,6 @@ int main(int argc, const char **argv)\n \tif (opt->dense_combined_merges)\n \t\topt->diffopt.output_format = DIFF_FORMAT_PATCH;\n \n-\tif (opt->diffopt.output_format == DIFF_FORMAT_PATCH)\n-\t\topt->diffopt.recursive = 1;\n-\n \tdiff_tree_setup_paths(get_pathspec(prefix, argv), &opt->diffopt);\n \tdiff_setup_done(&opt->diffopt);\n \ndiff --git a/diff.c b/diff.c\nindex b54bbfa..3a1e6ce 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1029,6 +1029,16 @@ int diff_setup_done(struct diff_options \n \t     options->detect_rename != DIFF_DETECT_COPY) ||\n \t    (0 <= options->rename_limit && !options->detect_rename))\n \t\treturn -1;\n+\n+\t/*\n+\t * These cases always need recursive; we do not drop caller-supplied\n+\t * recursive bits for other formats here.\n+\t */\n+\tif ((options->output_format == DIFF_FORMAT_PATCH) ||\n+\t    (options->output_format == DIFF_FORMAT_DIFFSTAT) ||\n+\t    (options->with_stat))\n+\t\toptions->recursive = 1;\n+\n \tif (options->detect_rename && options->rename_limit < 0)\n \t\toptions->rename_limit = diff_rename_limit_default;\n \tif (options->setup & DIFF_SETUP_USE_CACHE) {\ndiff --git a/git.c b/git.c\nindex 140ed18..5209b04 100644\n--- a/git.c\n+++ b/git.c\n@@ -344,8 +344,6 @@ static int cmd_log(int argc, const char \n \t\t\topt.ignore_merges = 0;\n \t\tif (opt.dense_combined_merges)\n \t\t\topt.diffopt.output_format = DIFF_FORMAT_PATCH;\n-\t\tif (opt.diffopt.output_format == DIFF_FORMAT_PATCH)\n-\t\t\topt.diffopt.recursive = 1;\n \t\tif (!full_diff && rev.prune_data)\n \t\t\tdiff_tree_setup_paths(rev.prune_data, &opt.diffopt);\n \t\tdiff_setup_done(&opt.diffopt);\n-- \n1.3.0.rc4.g8060\n"}]}