{"thread":{"id":"27015","subject":"BUG? in --dirstat when rearranging lines in a file","startedAt":"2011-04-07T13:49:37Z","lastAt":"2011-04-29T09:36:23Z","messageCount":91,"participants":["Johan Herland","Linus Torvalds","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"165355","messageId":"201104071549.37187.johan@herland.net","threadId":"27015","inReplyTo":null,"subject":"BUG? in --dirstat when rearranging lines in a file","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-07T13:49:37Z","receivedAt":"2011-04-07T13:49:37Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Hi,\n\n(CCed the two main authors of --dirstat and diffcore-delta.c)\n\nConsider the following sequence of commands:\n\n$ git init\n$ mkdir dir\n$ echo -e \"foo\\nbar\" > dir/file\n$ git add dir\n$ git commit -m \"first\"\n$ # Rearrange lines in dir/file\n$ echo -e \"bar\\nfoo\" > dir/file\n$ git diff\ndiff --git a/dir/file b/dir/file\nindex 3bd1f0e..1289765 100644\n--- a/dir/file\n+++ b/dir/file\n@@ -1,2 +1,2 @@\n-foo\n bar\n+foo\n$ git diff --stat\n dir/file |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n$ git diff --dirstat\n$ # WTF!?\n\n\"git diff\" and \"git diff --stat\" generates the expected output, but \"git \ndiff --dirstat\" unexpectedly generates no output at all. I've traced \nthis down through show_dirstat(), to diffcore_count_changes() which \nprocesses the pre-image and post-image to accumulate two counts:\n\n- src_copied (#lines (or 64-byte chunks) copied from pre- to post-)\n\n- literal_added (#lines/chunks added in post-).\n\nWhen the diff consists only of rearranging lines (like the above \nexample) the line-based hashing and subsequent sorting in \ndiffcore-delta.c ends up hiding he fact that lines have been moved \naround, and the resulting --dirstat reports less changes than expected.\n\nIs this a bug or a feature? :)\n\n\n(This issue was originally found by a colleague at $dayjob who wrote a \nscript (using --dirstat) to produce a summary of the areas of the \nsource tree touched by a given commit)\n\n\nHave fun! :)\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"165358","messageId":"BANLkTinEipewx2+Cx7Us0BSoSbjjU9uE6A@mail.gmail.com","threadId":"27015","inReplyTo":"201104071549.37187.johan@herland.net","subject":"Re: BUG? in --dirstat when rearranging lines in a file","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2011-04-07T14:56:02Z","receivedAt":"2011-04-07T14:56:02Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Thu, Apr 7, 2011 at 6:49 AM, Johan Herland <johan@herland.net> wrote:\n>\n> Consider the following sequence of commands:\n> [...]\n> $ git diff --stat\n>  dir/file |    2 +-\n>  1 files changed, 1 insertions(+), 1 deletions(-)\n> $ git diff --dirstat\n> $ # WTF!?\n\nSo the \"--dirstat\" thing really is different - it has never done a\nfull patch, it really only does a line hash count and then estimates\nthe amount of deleted/new code from that.\n\n> Is this a bug or a feature? :)\n\nIt's a \"bueature\" or a \"featug\". The dirstat code counts \"damage\" as\nyou noticed, and it does that because it's easy and often relevant. It\ncan be computed without actually generating the whole diff, the same\nway we do rename-detection without actually generating the diff, by\njust looking at \"hash and count each line\" information. In fact, it\nuses the same \"diffcore_count_changes()\" function for it.\n\nSo the reason I wouldn't call it a bug is that it very much is on\npurpose. Generating a real diff is much more expensive, and instead\nusing the line hashes gives us a quick and efficient O(n) way to\ngather up differences. But because it does the difference by basically\njust comparing hashes of the lines without taking _ordering_ into\naccount, it gives you a \"how many lines do these files have in common\"\nrather than a real diff.\n\nSo git internally has *three* different \"difference\" engines:\n\n - the \"delta\" algorithm that we use for packing (and binary diffs)\n - the traditional line-based diff for normal diffs\n - the \"rename/copy/dirstat damage detectior\" that doesn't take line\nordering into account, only some unordered \"heap of data contents\"\ncomparison.\n\nYou could think of the damage detection as a \"rename detection within\na file\". It's actually quite nice for \"git diff -M --dirstat\", where\nit means that pure code movement - whether inside a file or by\nrenaming a file - doesn't count as damage.\n\n(Of course, moving a piece of code _from_ one file to another still\ncounts as damage, so it's not really ignoring pure code movement).\n\nNOTE! Speed isn't the only reason we do that \"unordered heap of data\ncontents\" comparison. For rename detection, we really don't want\nmoving a big function around in a file to be counted as \"rewriting the\nfile\". So there are actually other reasons to like these semantics.\nThat said, honestly, for dirstat, the big issue was that it made it\nreally really simple. Look at how small the dirstat patch was (commit\n7df7c019c2a4), and realize that it's because it just re-used the\nexisting damage counting code.\n\n                            Linus\n"},{"id":"165383","messageId":"7vsjtt65t9.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"BANLkTinEipewx2+Cx7Us0BSoSbjjU9uE6A@mail.gmail.com","subject":"Re: BUG? in --dirstat when rearranging lines in a file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-07T22:43:30Z","receivedAt":"2011-04-07T22:43:30Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> That said, honestly, for dirstat, the big issue was that it made it\n> really really simple. Look at how small the dirstat patch was (commit\n> 7df7c019c2a4), and realize that it's because it just re-used the\n> existing damage counting code.\n\nYes, the most of the logic added by the patch is to percolate the damage\npoint up the tree to either coalesce or filter the result into manageable\nsize.\n\nSpeaking of that logic, I've been wondering for about a year and a half if\nthis \"if (permille)\" exclusion was intentional:\n\n\t/*\n\t * We don't report dirstat's for\n\t *  - the top level\n\t *  - or cases where everything came from a single directory\n\t *    under this directory (sources == 1).\n\t */\n\tif (baselen && sources != 1) {\n\t\tint permille = this_dir * 1000 / changed;\n\t\tif (permille) {\n\t\t\tint percent = permille / 10;\n\t\t\tif (percent >= dir->percent) {\n\t\t\t\tfprintf(opt->file, \"%s%4d.%01d%% %.*s\\n\", line_prefix,\n\t\t\t\t\tpercent, permille % 10, baselen, base);\n\t\t\t\tif (!dir->cumulative)\n\t\t\t\t\treturn 0;\n\t\t\t}\n\t\t}\n\t}\n\nIf the user sets dir->percent to zero, with an expectation that it will\ndisable all filtering, shouldn't we show everything?\n"},{"id":"165389","messageId":"BANLkTi=cAOduobWXfd=EeVgE4fHo=G-DTg@mail.gmail.com","threadId":"27015","inReplyTo":"7vsjtt65t9.fsf@alter.siamese.dyndns.org","subject":"Re: BUG? in --dirstat when rearranging lines in a file","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2011-04-07T22:59:33Z","receivedAt":"2011-04-07T22:59:33Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Thu, Apr 7, 2011 at 3:43 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Speaking of that logic, I've been wondering for about a year and a half if\n> this \"if (permille)\" exclusion was intentional:\n>\n> If the user sets dir->percent to zero, with an expectation that it will\n> disable all filtering, shouldn't we show everything?\n\nHmm. My gut feel is that you still don't want to see directories with\nno changes. In fact, doesn't the whole \"avoid even diffing identical\ndirectories with the same SHA1\" logic end up meaning that even if you\nwere to disable filtering, you'd _still_ not show the 0% case?\n\nBut hey, I dunno. If you want the semantics to be \"not identical, but\nnot damaged enough to be even 0.1%, so show it\", I don't think that\nwould be _wrong_ per se. I just don't think our current \"ignore 0.0%\nfiles\" is wrong either ;)\n\n                           Linus\n"},{"id":"165418","messageId":"201104081646.35750.johan@herland.net","threadId":"27015","inReplyTo":"BANLkTinEipewx2+Cx7Us0BSoSbjjU9uE6A@mail.gmail.com","subject":"Re: BUG? in --dirstat when rearranging lines in a file","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-08T14:46:35Z","receivedAt":"2011-04-08T14:46:35Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Thursday 07 April 2011, Linus Torvalds wrote:\n> On Thu, Apr 7, 2011 at 6:49 AM, Johan Herland wrote:\n> > Consider the following sequence of commands:\n> > [...]\n> > $ git diff --stat\n> >  dir/file |    2 +-\n> >  1 files changed, 1 insertions(+), 1 deletions(-)\n> > $ git diff --dirstat\n> > $ # WTF!?\n>\n> So the \"--dirstat\" thing really is different - it has never done a\n> full patch, it really only does a line hash count and then estimates\n> the amount of deleted/new code from that.\n>\n> [...]\n\nOk, so here are 3 patches to somewhat improve the situation without \nmaking --dirstat too ugly or expensive.\n\n#1: Simply document the current behavior.\n\n#2: Improve --dirstat-by-file. It doesn't really care about the per-file \nanalysis done by --dirstat, but only whether or not a file has changed \nat all. Since the diff queue does not contain unchanged files (<- this \nis an assumption that I hope someone with more diffcore knowledge can \nverify), we can unconditionally assign damage == 1 to each entry in the \ndiff queue, and bypass the entire --dirstat per-file analysis.\n\n#3. This is a quick/ugly hack that depends on the same assumption as #2: \nIf an entry is in the diff queue, we now that it is not unchanged. So \nif the per-file analysis yields damage == 0, we know that it must have \noverlooked something (rearranged lines), so we set damage = 1 instead. \nThe logic is that underrepresenting a change in --dirstat is better \nthan ignoring it...\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"165419","messageId":"201104081648.11842.johan@herland.net","threadId":"27015","inReplyTo":"201104081646.35750.johan@herland.net","subject":"[PATCH 1/3] --dirstat: Document shortcomings compared to --stat or regular diff","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-08T14:48:11Z","receivedAt":"2011-04-08T14:48:11Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Also add a testcase documenting the current behavior.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/diff-options.txt                     |    5 +++\n t/t4013-diff-various.sh                            |   27 +++++++++++++++----\n t/t4013/diff.diff_--dirstat_initial_rearrange      |    2 +\n ...tch_--stdout_--cover-letter_-n_initial..master^ |    2 +-\n t/t4013/diff.log_--decorate=full_--all             |    6 ++++\n t/t4013/diff.log_--decorate_--all                  |    6 ++++\n 6 files changed, 41 insertions(+), 7 deletions(-)\n create mode 100644 t/t4013/diff.diff_--dirstat_initial_rearrange\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex c93124b..25e48c4 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -72,6 +72,11 @@ endif::git-format-patch[]\n \ta cut-off percent (3% by default) are not shown. The cut-off percent\n \tcan be set with `--dirstat=<limit>`. Changes in a child directory are not\n \tcounted for the parent directory, unless `--cumulative` is used.\n++\n+Note that `--dirstat` does not use the regular diff machinery to calculate\n+the changes (rather it is based on the rename detection machinery). Therefore,\n+`--dirstat` may skip some changes that `--stat` does not skip. For example,\n+rearranging the lines in a file will not be detected by `--dirstat`.\n \n --dirstat-by-file[=<limit>]::\n \tSame as `--dirstat`, but counts changed files instead of lines.\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex 5daa0f2..8cc94ef 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -80,18 +80,31 @@ test_expect_success setup '\n \n \tgit config log.showroot false &&\n \tgit commit --amend &&\n+\n+\tGIT_AUTHOR_DATE=\"2006-06-26 00:06:00 +0000\" &&\n+\tGIT_COMMITTER_DATE=\"2006-06-26 00:06:00 +0000\" &&\n+\texport GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&\n+\tgit checkout -b rearrange initial &&\n+\tfor i in B A; do echo $i; done >dir/sub &&\n+\tgit add dir/sub &&\n+\tgit commit -m \"Rearranged lines in dir/sub\" &&\n+\tgit checkout master &&\n+\n \tgit show-branch\n '\n \n : <<\\EOF\n ! [initial] Initial\n  * [master] Merge branch 'side'\n-  ! [side] Side\n----\n- -  [master] Merge branch 'side'\n- *+ [side] Side\n- *  [master^] Second\n-+*+ [initial] Initial\n+  ! [rearrange] Rearranged lines in dir/sub\n+   ! [side] Side\n+----\n+  +  [rearrange] Rearranged lines in dir/sub\n+ -   [master] Merge branch 'side'\n+ * + [side] Side\n+ *   [master^] Third\n+ *   [master~2] Second\n++*++ [initial] Initial\n EOF\n \n V=`git version | sed -e 's/^git version //' -e 's/\\./\\\\./g'`\n@@ -287,6 +300,8 @@ diff --no-index --name-status -- dir2 dir\n diff --no-index dir dir3\n diff master master^ side\n diff --dirstat master~1 master~2\n+# --dirstat does NOT pick up changes that simply rearrange existing lines\n+diff --dirstat initial rearrange\n EOF\n \n test_expect_success 'log -S requires an argument' '\ndiff --git a/t/t4013/diff.diff_--dirstat_initial_rearrange b/t/t4013/diff.diff_--dirstat_initial_rearrange\nnew file mode 100644\nindex 0000000..fb2e17d\n--- /dev/null\n+++ b/t/t4013/diff.diff_--dirstat_initial_rearrange\n@@ -0,0 +1,2 @@\n+$ git diff --dirstat initial rearrange\n+$\ndiff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\nindex 1f0f9ad..3b4e113 100644\n--- a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\n+++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\n@@ -1,7 +1,7 @@\n $ git format-patch --stdout --cover-letter -n initial..master^\n From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001\n From: C O Mitter <committer@example.com>\n-Date: Mon, 26 Jun 2006 00:05:00 +0000\n+Date: Mon, 26 Jun 2006 00:06:00 +0000\n Subject: [DIFFERENT_PREFIX 0/2] *** SUBJECT HERE ***\n \n *** BLURB HERE ***\ndiff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all\nindex d155e0b..44d4525 100644\n--- a/t/t4013/diff.log_--decorate=full_--all\n+++ b/t/t4013/diff.log_--decorate=full_--all\n@@ -1,4 +1,10 @@\n $ git log --decorate=full --all\n+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (refs/heads/rearrange)\n+Author: A U Thor <author@example.com>\n+Date:   Mon Jun 26 00:06:00 2006 +0000\n+\n+    Rearranged lines in dir/sub\n+\n commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, refs/heads/master)\n Merge: 9a6d494 c7a2ab9\n Author: A U Thor <author@example.com>\ndiff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all\nindex fd7c3e6..27d3eab 100644\n--- a/t/t4013/diff.log_--decorate_--all\n+++ b/t/t4013/diff.log_--decorate_--all\n@@ -1,4 +1,10 @@\n $ git log --decorate --all\n+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (rearrange)\n+Author: A U Thor <author@example.com>\n+Date:   Mon Jun 26 00:06:00 2006 +0000\n+\n+    Rearranged lines in dir/sub\n+\n commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, master)\n Merge: 9a6d494 c7a2ab9\n Author: A U Thor <author@example.com>\n-- \n1.7.5.rc1\n"},{"id":"165420","messageId":"201104081650.49254.johan@herland.net","threadId":"27015","inReplyTo":"201104081646.35750.johan@herland.net","subject":"[PATCH 2/3] --dirstat-by-file: Make it faster and more correct","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-08T14:50:49Z","receivedAt":"2011-04-08T14:50:49Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Currently, when using --dirstat-by-file, it first does the full --dirstat\nanalysis (using diffcore_count_changes()), and then resets 'damage' to 1,\nif any damage was found by diffcore_count_changes().\n\nBut --dirstat-by-file is not interested in the file damage per se. It only\ncares if the file changed at all. In that sense it only cares if the blob\nSHA1 for a file has changed. Fortunately, determining which files have\nchanged is already done when we build the diff_queue, and by the time we\nget to show_dirstat(), we know that each entry in the queue correspond to\na changed file. Therefore we can skip the entire --dirstat analysis and\nsimply set 'damage' to 1 for each entry in the diff queue.\n\nThis makes --dirstat-by-file faster, and also bypasses --dirstat's issues\nwith detecting changes that merely rearrange lines.\n\nThe patch also contains an added testcase verifying that --dirstat-by-file\nnow detects changes that only rearrange lines.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n\nI hope someone with more intimate diffcore knowledge can verify that\nthe diff queue indeed never contains entries that should be considered\n\"unchanged\" by --dirstat-by-file.\n\n\n...Johan\n\n diff.c                                             |   21 +++++++++++++++----\n t/t4013-diff-various.sh                            |    2 +\n .../diff.diff_--dirstat-by-file_initial_rearrange  |    3 ++\n 3 files changed, 21 insertions(+), 5 deletions(-)\n create mode 100644 t/t4013/diff.diff_--dirstat-by-file_initial_rearrange\n\ndiff --git a/diff.c b/diff.c\nindex 9fa8410..28d9293 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1541,6 +1541,20 @@ static void show_dirstat(struct diff_options *options)\n \n \t\tname = p->one->path ? p->one->path : p->two->path;\n \n+\t\tif (DIFF_OPT_TST(options, DIRSTAT_BY_FILE)) {\n+\t\t\t/*\n+\t\t\t * In --dirstat-by-file mode, we're only interested in\n+\t\t\t * whether the file changed _at_all_.\n+\t\t\t * We don't need to look at the actual file contents.\n+\t\t\t * Assuming that the diff queue does not contain\n+\t\t\t * unchanged entries, we can unconditionally add this\n+\t\t\t * file to the list of results (with each file\n+\t\t\t * contributing equal damage).\n+\t\t\t */\n+\t\t\tdamage = 1;\n+\t\t\tgoto found_damage;\n+\t\t}\n+\n \t\tif (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {\n \t\t\tdiff_populate_filespec(p->one, 0);\n \t\t\tdiff_populate_filespec(p->two, 0);\n@@ -1563,14 +1577,11 @@ static void show_dirstat(struct diff_options *options)\n \t\t/*\n \t\t * Original minus copied is the removed material,\n \t\t * added is the new material.  They are both damages\n-\t\t * made to the preimage. In --dirstat-by-file mode, count\n-\t\t * damaged files, not damaged lines. This is done by\n-\t\t * counting only a single damaged line per file.\n+\t\t * made to the preimage.\n \t\t */\n \t\tdamage = (p->one->size - copied) + added;\n-\t\tif (DIFF_OPT_TST(options, DIRSTAT_BY_FILE) && damage > 0)\n-\t\t\tdamage = 1;\n \n+found_damage:\n \t\tALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);\n \t\tdir.files[dir.nr].name = name;\n \t\tdir.files[dir.nr].changed = damage;\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex 8cc94ef..e8240f2 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -302,6 +302,8 @@ diff master master^ side\n diff --dirstat master~1 master~2\n # --dirstat does NOT pick up changes that simply rearrange existing lines\n diff --dirstat initial rearrange\n+# ...but --dirstat-by-file DOES pick up rearranged lines\n+diff --dirstat-by-file initial rearrange\n EOF\n \n test_expect_success 'log -S requires an argument' '\ndiff --git a/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange b/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange\nnew file mode 100644\nindex 0000000..e48e33f\n--- /dev/null\n+++ b/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange\n@@ -0,0 +1,3 @@\n+$ git diff --dirstat-by-file initial rearrange\n+ 100.0% dir/\n+$\n-- \n1.7.5.rc1\n"},{"id":"165421","messageId":"201104081655.38075.johan@herland.net","threadId":"27015","inReplyTo":"201104081646.35750.johan@herland.net","subject":"[RFC/PATCH 3/3] Teach --dirstat to not completely ignore rearranged lines","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-08T14:55:37Z","receivedAt":"2011-04-08T14:55:37Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Currently, the --dirstat analysis fails to detect some kinds of changes.\nFor example, rearranging lines in a file causes the \"damage\" calculated\nby show_dirstat() to be 0. However, when we process the diff queue in\nshow_dirstat(), we already now that there should be at least _some_\ndamage assigned to each entry, because truly _unchanged_ entries are\nsimply not present in the diff queue.\n\nThis patch teaches show_dirstat() to assign a minimum amount of damage\n(== 1) to entries for which the analysis otherwise yields zero damage.\nObviously this is not a complete fix, but it's at least better to\nunderrepresent these changes, rather than simply pretending that they\ndon't exist.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n\nThis is a somewhat quick and ugly solution to make --dirstat at least\nshow _something_ for changes that consist solely of rearranging lines\nin a file. Sure, those changes would be thoroughly underrepresented by\n--dirstat (probably falling below the default 3% threshold in many\ncases), but I figure it's better to underrepresent them rather than\nignoring them completely.\n\nAs with 2/3, this patch also relies on the assumption that the diff\nqueue never contains entries that should be considered \"unchanged\" by\n--dirstat.\n\n Documentation/diff-options.txt                |    4 ++--\n diff.c                                        |    8 ++++++++\n t/t4013-diff-various.sh                       |    2 --\n t/t4013/diff.diff_--dirstat_initial_rearrange |    1 +\n 4 files changed, 11 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 25e48c4..61a8409 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -75,8 +75,8 @@ endif::git-format-patch[]\n +\n Note that `--dirstat` does not use the regular diff machinery to calculate\n the changes (rather it is based on the rename detection machinery). Therefore,\n-`--dirstat` may skip some changes that `--stat` does not skip. For example,\n-rearranging the lines in a file will not be detected by `--dirstat`.\n+`--dirstat` will count some changes differently than `--stat`. For example,\n+rearranged lines in a file will be underrepresented by `--dirstat`.\n \n --dirstat-by-file[=<limit>]::\n \tSame as `--dirstat`, but counts changed files instead of lines.\ndiff --git a/diff.c b/diff.c\nindex 28d9293..0d82082 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1578,8 +1578,16 @@ static void show_dirstat(struct diff_options *options)\n \t\t * Original minus copied is the removed material,\n \t\t * added is the new material.  They are both damages\n \t\t * made to the preimage.\n+\t\t * If the resulting damage is zero, we know that\n+\t\t * diffcore_count_changes() considers the two entries\n+\t\t * to be identical, but since they are in the diff\n+\t\t * queue at all, we now that there must have been\n+\t\t * _some_ kind of change, so we force all entries to\n+\t\t * have at least a minimum of damage.\n \t\t */\n \t\tdamage = (p->one->size - copied) + added;\n+\t\tif (!damage)\n+\t\t\tdamage = 1;\n \n found_damage:\n \t\tALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex e8240f2..93a6f20 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -300,9 +300,7 @@ diff --no-index --name-status -- dir2 dir\n diff --no-index dir dir3\n diff master master^ side\n diff --dirstat master~1 master~2\n-# --dirstat does NOT pick up changes that simply rearrange existing lines\n diff --dirstat initial rearrange\n-# ...but --dirstat-by-file DOES pick up rearranged lines\n diff --dirstat-by-file initial rearrange\n EOF\n \ndiff --git a/t/t4013/diff.diff_--dirstat_initial_rearrange b/t/t4013/diff.diff_--dirstat_initial_rearrange\nindex fb2e17d..5fb02c1 100644\n--- a/t/t4013/diff.diff_--dirstat_initial_rearrange\n+++ b/t/t4013/diff.diff_--dirstat_initial_rearrange\n@@ -1,2 +1,3 @@\n $ git diff --dirstat initial rearrange\n+ 100.0% dir/\n $\n-- \n1.7.5.rc1\n"},{"id":"165423","messageId":"BANLkTinv3SSYWn==F=ans4uHuHJhTrfjDQ@mail.gmail.com","threadId":"27015","inReplyTo":"201104081646.35750.johan@herland.net","subject":"Re: BUG? in --dirstat when rearranging lines in a file","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2011-04-08T15:04:52Z","receivedAt":"2011-04-08T15:04:52Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Fri, Apr 8, 2011 at 7:46 AM, Johan Herland <johan@herland.net> wrote:\n>\n> #2: Improve --dirstat-by-file. It doesn't really care about the per-file\n> analysis done by --dirstat, but only whether or not a file has changed\n> at all. Since the diff queue does not contain unchanged files (<- this\n> is an assumption that I hope someone with more diffcore knowledge can\n> verify),\n\nHmm.\n\nI think that with renames, the diff queue _can_ contain unchanged\nfiles (ie pure renames).\n\nAlso, I think -CC (aka --find-copies-harder), _every_ file ends up in\nthe diff queue because that's how it does the detection.\n\nBut I didn't actually check, and I may be full of sh*t.\n\n                              Linus\n"},{"id":"165446","messageId":"7v1v1c4j5j.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"201104081648.11842.johan@herland.net","subject":"Re: [PATCH 1/3] --dirstat: Document shortcomings compared to --stat or regular diff","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-08T19:50:32Z","receivedAt":"2011-04-08T19:50:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\n> index c93124b..25e48c4 100644\n> --- a/Documentation/diff-options.txt\n> +++ b/Documentation/diff-options.txt\n> @@ -72,6 +72,11 @@ endif::git-format-patch[]\n>  \ta cut-off percent (3% by default) are not shown. The cut-off percent\n>  \tcan be set with `--dirstat=<limit>`. Changes in a child directory are not\n>  \tcounted for the parent directory, unless `--cumulative` is used.\n> ++\n> +Note that `--dirstat` does not use the regular diff machinery to calculate\n> +the changes (rather it is based on the rename detection machinery). Therefore,\n> +`--dirstat` may skip some changes that `--stat` does not skip. For example,\n> +rearranging the lines in a file will not be detected by `--dirstat`.\n\nBe positive: s/will not be detected/is not considered to be a change/,\nperhaps.  Also \"it is based on the rename detection machinery\" is\ndescribing an implementation detail without helping the end users.\n\nTry to rephrase what Linus explained when he said \"it is very much on\npurpose\".  Perhaps like this?\n\n    Note that the `--dirstat` option computes the changes while ignoring\n    pure code movements within a file.  In other words, rearranging lines\n    in a file is not counted as a change.\n"},{"id":"165447","messageId":"7vtye834al.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"BANLkTinv3SSYWn==F=ans4uHuHJhTrfjDQ@mail.gmail.com","subject":"Re: BUG? in --dirstat when rearranging lines in a file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-08T19:56:50Z","receivedAt":"2011-04-08T19:56:50Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> On Fri, Apr 8, 2011 at 7:46 AM, Johan Herland <johan@herland.net> wrote:\n>>\n>> #2: Improve --dirstat-by-file. It doesn't really care about the per-file\n>> analysis done by --dirstat, but only whether or not a file has changed\n>> at all. Since the diff queue does not contain unchanged files (<- this\n>> is an assumption that I hope someone with more diffcore knowledge can\n>> verify),\n>\n> Hmm.\n>\n> I think that with renames, the diff queue _can_ contain unchanged\n> files (ie pure renames).\n>\n> Also, I think -CC (aka --find-copies-harder), _every_ file ends up in\n> the diff queue because that's how it does the detection.\n\nBoth are correct, but the output phase happens after diffcore_std() cleans\nup the unused and unchanged filepairs thrown into the queue for the\npurpose of find-copies-harder, so you shouldn't have to worry about them.\n\nWhen you rename a file without changing its contents, what do you want to\nsee in --dirstat-by-file output?  I assume that you do not want to show\nanything, so it would be sufficient to compare the two object names.\n"},{"id":"165586","messageId":"1302475732-741-1-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"7vtye834al.fsf@alter.siamese.dyndns.org","subject":"[PATCHv2 0/3] --dirstat fixes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-10T22:48:49Z","receivedAt":"2011-04-10T22:48:49Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Here's a reroll of the previous series. Changes since v1:\n\n- Adopt Junio's phrasing of the differences between --dirstat and\n  regular diff (--stat)\n\n- Detect and ignore pure renames in the diff queue. This is done by\n  comparing the SHA1s of each file pair, and if they are equal, we\n  know the files are identical, and should not show up in --dirstat.\n\n  As an extra bonus in this version, when the SHA1s do match, we can\n  bypass the usual --dirstat analysis, because we know it would find\n  no changes. Instead, we can directly set damage = 0 in that case.\n\nI've looked at the contents of the diff queue and resulting output in\na variety of cases:\n\n- files with no changes, rearranged lines, and other changes\n- files that are copied, moved, or not moved\n- unstaged changes, staged changes, committed changes\n- diff options: (none), --stat, --dirstat, and --dirstat-by-file\n- diff options: (none), -M, and -C -C\n\n(324 variations in total) and I'm fairly sure about the current patches\nand how they interact with the diff queue.\n\nA remaining question AFAICS is if there's a different (i.e. better) way\nto (cheaply) estimate the damage contributed by code movements within a\nfile. The current \"damage = 1\" approach is somewhat crude, but IMHO\nstill better that ignoring code movements altogether.\n\n\nHave fun! :)\n\n...Johan\n\n\nJohan Herland (3):\n  --dirstat: Describe non-obvious differences relative to --stat or regular diff\n  --dirstat-by-file: Make it faster and more correct\n  Teach --dirstat to not completely ignore rearranged lines within a file\n\n Documentation/diff-options.txt                     |    4 ++\n diff.c                                             |   40 ++++++++++++++++++--\n t/t4013-diff-various.sh                            |   27 ++++++++++---\n .../diff.diff_--dirstat-by-file_initial_rearrange  |    3 +\n t/t4013/diff.diff_--dirstat_initial_rearrange      |    3 +\n ...tch_--stdout_--cover-letter_-n_initial..master^ |    2 +-\n t/t4013/diff.log_--decorate=full_--all             |    6 +++\n t/t4013/diff.log_--decorate_--all                  |    6 +++\n 8 files changed, 80 insertions(+), 11 deletions(-)\n create mode 100644 t/t4013/diff.diff_--dirstat-by-file_initial_rearrange\n create mode 100644 t/t4013/diff.diff_--dirstat_initial_rearrange\n\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"165587","messageId":"1302475732-741-2-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1302475732-741-1-git-send-email-johan@herland.net","subject":"[PATCHv2 1/3] --dirstat: Describe non-obvious differences relative to --stat or regular diff","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-10T22:48:50Z","receivedAt":"2011-04-10T22:48:50Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Also add a testcase documenting the current behavior.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/diff-options.txt                     |    4 +++\n t/t4013-diff-various.sh                            |   27 +++++++++++++++----\n t/t4013/diff.diff_--dirstat_initial_rearrange      |    2 +\n ...tch_--stdout_--cover-letter_-n_initial..master^ |    2 +-\n t/t4013/diff.log_--decorate=full_--all             |    6 ++++\n t/t4013/diff.log_--decorate_--all                  |    6 ++++\n 6 files changed, 40 insertions(+), 7 deletions(-)\n create mode 100644 t/t4013/diff.diff_--dirstat_initial_rearrange\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex c93124b..23772d6 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -72,6 +72,10 @@ endif::git-format-patch[]\n \ta cut-off percent (3% by default) are not shown. The cut-off percent\n \tcan be set with `--dirstat=<limit>`. Changes in a child directory are not\n \tcounted for the parent directory, unless `--cumulative` is used.\n++\n+Note that the `--dirstat` option computes the changes while ignoring\n+pure code movements within a file.  In other words, rearranging lines\n+in a file is not counted as a change.\n \n --dirstat-by-file[=<limit>]::\n \tSame as `--dirstat`, but counts changed files instead of lines.\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex 5daa0f2..3b1b392 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -80,18 +80,31 @@ test_expect_success setup '\n \n \tgit config log.showroot false &&\n \tgit commit --amend &&\n+\n+\tGIT_AUTHOR_DATE=\"2006-06-26 00:06:00 +0000\" &&\n+\tGIT_COMMITTER_DATE=\"2006-06-26 00:06:00 +0000\" &&\n+\texport GIT_AUTHOR_DATE GIT_COMMITTER_DATE &&\n+\tgit checkout -b rearrange initial &&\n+\tfor i in B A; do echo $i; done >dir/sub &&\n+\tgit add dir/sub &&\n+\tgit commit -m \"Rearranged lines in dir/sub\" &&\n+\tgit checkout master &&\n+\n \tgit show-branch\n '\n \n : <<\\EOF\n ! [initial] Initial\n  * [master] Merge branch 'side'\n-  ! [side] Side\n----\n- -  [master] Merge branch 'side'\n- *+ [side] Side\n- *  [master^] Second\n-+*+ [initial] Initial\n+  ! [rearrange] Rearranged lines in dir/sub\n+   ! [side] Side\n+----\n+  +  [rearrange] Rearranged lines in dir/sub\n+ -   [master] Merge branch 'side'\n+ * + [side] Side\n+ *   [master^] Third\n+ *   [master~2] Second\n++*++ [initial] Initial\n EOF\n \n V=`git version | sed -e 's/^git version //' -e 's/\\./\\\\./g'`\n@@ -287,6 +300,8 @@ diff --no-index --name-status -- dir2 dir\n diff --no-index dir dir3\n diff master master^ side\n diff --dirstat master~1 master~2\n+# --dirstat doesn't notice changes that simply rearrange existing lines\n+diff --dirstat initial rearrange\n EOF\n \n test_expect_success 'log -S requires an argument' '\ndiff --git a/t/t4013/diff.diff_--dirstat_initial_rearrange b/t/t4013/diff.diff_--dirstat_initial_rearrange\nnew file mode 100644\nindex 0000000..fb2e17d\n--- /dev/null\n+++ b/t/t4013/diff.diff_--dirstat_initial_rearrange\n@@ -0,0 +1,2 @@\n+$ git diff --dirstat initial rearrange\n+$\ndiff --git a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\nindex 1f0f9ad..3b4e113 100644\n--- a/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\n+++ b/t/t4013/diff.format-patch_--stdout_--cover-letter_-n_initial..master^\n@@ -1,7 +1,7 @@\n $ git format-patch --stdout --cover-letter -n initial..master^\n From 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0 Mon Sep 17 00:00:00 2001\n From: C O Mitter <committer@example.com>\n-Date: Mon, 26 Jun 2006 00:05:00 +0000\n+Date: Mon, 26 Jun 2006 00:06:00 +0000\n Subject: [DIFFERENT_PREFIX 0/2] *** SUBJECT HERE ***\n \n *** BLURB HERE ***\ndiff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all\nindex d155e0b..44d4525 100644\n--- a/t/t4013/diff.log_--decorate=full_--all\n+++ b/t/t4013/diff.log_--decorate=full_--all\n@@ -1,4 +1,10 @@\n $ git log --decorate=full --all\n+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (refs/heads/rearrange)\n+Author: A U Thor <author@example.com>\n+Date:   Mon Jun 26 00:06:00 2006 +0000\n+\n+    Rearranged lines in dir/sub\n+\n commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, refs/heads/master)\n Merge: 9a6d494 c7a2ab9\n Author: A U Thor <author@example.com>\ndiff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all\nindex fd7c3e6..27d3eab 100644\n--- a/t/t4013/diff.log_--decorate_--all\n+++ b/t/t4013/diff.log_--decorate_--all\n@@ -1,4 +1,10 @@\n $ git log --decorate --all\n+commit cd4e72fd96faed3f0ba949dc42967430374e2290 (rearrange)\n+Author: A U Thor <author@example.com>\n+Date:   Mon Jun 26 00:06:00 2006 +0000\n+\n+    Rearranged lines in dir/sub\n+\n commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, master)\n Merge: 9a6d494 c7a2ab9\n Author: A U Thor <author@example.com>\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"165588","messageId":"1302475732-741-3-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1302475732-741-1-git-send-email-johan@herland.net","subject":"[PATCHv2 2/3] --dirstat-by-file: Make it faster and more correct","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-10T22:48:51Z","receivedAt":"2011-04-10T22:48:51Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Currently, when using --dirstat-by-file, it first does the full --dirstat\nanalysis (using diffcore_count_changes()), and then resets 'damage' to 1,\nif any damage was found by diffcore_count_changes().\n\nBut --dirstat-by-file is not interested in the file damage per se. It only\ncares if the file changed at all. In that sense it only cares if the blob\nSHA1 for a file has changed. We therefore only need to compare the SHA1s\nof each file pair in the diff queue. As a result, we can skip the entire\n--dirstat analysis and simply set 'damage' to 1 for each entry where the\nSHA1 has changed.\n\nThis makes --dirstat-by-file faster, and also bypasses --dirstat's practice\nof ignoring rearranged lines within a file.\n\nThe patch also contains an added testcase verifying that --dirstat-by-file\nnow detects changes that only rearrange lines within a file.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n diff.c                                             |   25 ++++++++++++++++----\n t/t4013-diff-various.sh                            |    2 +\n .../diff.diff_--dirstat-by-file_initial_rearrange  |    3 ++\n 3 files changed, 25 insertions(+), 5 deletions(-)\n create mode 100644 t/t4013/diff.diff_--dirstat-by-file_initial_rearrange\n\ndiff --git a/diff.c b/diff.c\nindex 9fa8410..a224048 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1538,9 +1538,27 @@ static void show_dirstat(struct diff_options *options)\n \t\tstruct diff_filepair *p = q->queue[i];\n \t\tconst char *name;\n \t\tunsigned long copied, added, damage;\n+\t\tint content_changed;\n \n \t\tname = p->one->path ? p->one->path : p->two->path;\n \n+\t\tif (p->one->sha1_valid && p->two->sha1_valid)\n+\t\t\tcontent_changed = hashcmp(p->one->sha1, p->two->sha1);\n+\t\telse\n+\t\t\tcontent_changed = 1;\n+\n+\t\tif (DIFF_OPT_TST(options, DIRSTAT_BY_FILE)) {\n+\t\t\t/*\n+\t\t\t * In --dirstat-by-file mode, we don't really need to\n+\t\t\t * look at the actual file contents at all.\n+\t\t\t * The fact that the SHA1 changed is enough for us to\n+\t\t\t * add this file to the list of results\n+\t\t\t * (with each file contributing equal damage).\n+\t\t\t */\n+\t\t\tdamage = content_changed ? 1 : 0;\n+\t\t\tgoto found_damage;\n+\t\t}\n+\n \t\tif (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {\n \t\t\tdiff_populate_filespec(p->one, 0);\n \t\t\tdiff_populate_filespec(p->two, 0);\n@@ -1563,14 +1581,11 @@ static void show_dirstat(struct diff_options *options)\n \t\t/*\n \t\t * Original minus copied is the removed material,\n \t\t * added is the new material.  They are both damages\n-\t\t * made to the preimage. In --dirstat-by-file mode, count\n-\t\t * damaged files, not damaged lines. This is done by\n-\t\t * counting only a single damaged line per file.\n+\t\t * made to the preimage.\n \t\t */\n \t\tdamage = (p->one->size - copied) + added;\n-\t\tif (DIFF_OPT_TST(options, DIRSTAT_BY_FILE) && damage > 0)\n-\t\t\tdamage = 1;\n \n+found_damage:\n \t\tALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);\n \t\tdir.files[dir.nr].name = name;\n \t\tdir.files[dir.nr].changed = damage;\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex 3b1b392..6428a90 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -302,6 +302,8 @@ diff master master^ side\n diff --dirstat master~1 master~2\n # --dirstat doesn't notice changes that simply rearrange existing lines\n diff --dirstat initial rearrange\n+# ...but --dirstat-by-file does notice changes that only rearrange lines\n+diff --dirstat-by-file initial rearrange\n EOF\n \n test_expect_success 'log -S requires an argument' '\ndiff --git a/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange b/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange\nnew file mode 100644\nindex 0000000..e48e33f\n--- /dev/null\n+++ b/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange\n@@ -0,0 +1,3 @@\n+$ git diff --dirstat-by-file initial rearrange\n+ 100.0% dir/\n+$\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"165589","messageId":"1302475732-741-4-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1302475732-741-1-git-send-email-johan@herland.net","subject":"[PATCHv2 3/3] Teach --dirstat to not completely ignore rearranged lines within a file","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-10T22:48:52Z","receivedAt":"2011-04-10T22:48:52Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Currently, the --dirstat analysis fails to detect when lines within a\nfile are rearranged, because the \"damage\" calculated by show_dirstat()\nis 0. However, if the SHA1 sum has changed, we already now that there\nshould be at least some minimum amount of damage.\n\nThis patch teaches show_dirstat() to assign a minimum amount of damage\n(== 1) to entries for which the analysis otherwise yields zero damage.\nObviously this is not a complete fix, but it's at least better to\nunderrepresent these changes, rather than simply pretending that they\ndon't exist.\n\nAlso, with the added SHA1 comparison, we can safely skip the --dirstat\nanalysis when the SHA1s do happen to match (e.g. for a pure file rename)\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/diff-options.txt                |    4 ++--\n diff.c                                        |   19 ++++++++++++++++++-\n t/t4013-diff-various.sh                       |    2 --\n t/t4013/diff.diff_--dirstat_initial_rearrange |    1 +\n 4 files changed, 21 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 23772d6..7e4bd42 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -74,8 +74,8 @@ endif::git-format-patch[]\n \tcounted for the parent directory, unless `--cumulative` is used.\n +\n Note that the `--dirstat` option computes the changes while ignoring\n-pure code movements within a file.  In other words, rearranging lines\n-in a file is not counted as a change.\n+the amount of pure code movements within a file.  In other words,\n+rearranging lines in a file is not counted as much as other changes.\n \n --dirstat-by-file[=<limit>]::\n \tSame as `--dirstat`, but counts changed files instead of lines.\ndiff --git a/diff.c b/diff.c\nindex a224048..3e0bc1f 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1547,6 +1547,16 @@ static void show_dirstat(struct diff_options *options)\n \t\telse\n \t\t\tcontent_changed = 1;\n \n+\t\tif (!content_changed) {\n+\t\t\t/*\n+\t\t\t * The SHA1 has not changed, so pre-/post-content is\n+\t\t\t * identical. We can therefore skip looking at the\n+\t\t\t * file contents altogether.\n+\t\t\t */\n+\t\t\tdamage = 0;\n+\t\t\tgoto found_damage;\n+\t\t}\n+\n \t\tif (DIFF_OPT_TST(options, DIRSTAT_BY_FILE)) {\n \t\t\t/*\n \t\t\t * In --dirstat-by-file mode, we don't really need to\n@@ -1555,7 +1565,7 @@ static void show_dirstat(struct diff_options *options)\n \t\t\t * add this file to the list of results\n \t\t\t * (with each file contributing equal damage).\n \t\t\t */\n-\t\t\tdamage = content_changed ? 1 : 0;\n+\t\t\tdamage = 1;\n \t\t\tgoto found_damage;\n \t\t}\n \n@@ -1582,8 +1592,15 @@ static void show_dirstat(struct diff_options *options)\n \t\t * Original minus copied is the removed material,\n \t\t * added is the new material.  They are both damages\n \t\t * made to the preimage.\n+\t\t * If the resulting damage is zero, we know that\n+\t\t * diffcore_count_changes() considers the two entries to\n+\t\t * be identical, but since content_changed is true, we\n+\t\t * know that there must have been _some_ kind of change,\n+\t\t * so we force all entries to have damage > 0.\n \t\t */\n \t\tdamage = (p->one->size - copied) + added;\n+\t\tif (!damage)\n+\t\t\tdamage = 1;\n \n found_damage:\n \t\tALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);\ndiff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh\nindex 6428a90..93a6f20 100755\n--- a/t/t4013-diff-various.sh\n+++ b/t/t4013-diff-various.sh\n@@ -300,9 +300,7 @@ diff --no-index --name-status -- dir2 dir\n diff --no-index dir dir3\n diff master master^ side\n diff --dirstat master~1 master~2\n-# --dirstat doesn't notice changes that simply rearrange existing lines\n diff --dirstat initial rearrange\n-# ...but --dirstat-by-file does notice changes that only rearrange lines\n diff --dirstat-by-file initial rearrange\n EOF\n \ndiff --git a/t/t4013/diff.diff_--dirstat_initial_rearrange b/t/t4013/diff.diff_--dirstat_initial_rearrange\nindex fb2e17d..5fb02c1 100644\n--- a/t/t4013/diff.diff_--dirstat_initial_rearrange\n+++ b/t/t4013/diff.diff_--dirstat_initial_rearrange\n@@ -1,2 +1,3 @@\n $ git diff --dirstat initial rearrange\n+ 100.0% dir/\n $\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"165591","messageId":"BANLkTinAO0ZOpsdYgdw5D7xAMzgKdjdwWA@mail.gmail.com","threadId":"27015","inReplyTo":"1302475732-741-1-git-send-email-johan@herland.net","subject":"Re: [PATCHv2 0/3] --dirstat fixes","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2011-04-10T23:17:28Z","receivedAt":"2011-04-10T23:17:28Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Sun, Apr 10, 2011 at 3:48 PM, Johan Herland <johan@herland.net> wrote:\n> Here's a reroll of the previous series. Changes since v1:\n\nThe series looks fine to me,\n\n       Linus\n"},{"id":"165636","messageId":"7vwrj0wt7i.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"1302475732-741-3-git-send-email-johan@herland.net","subject":"Re: [PATCHv2 2/3] --dirstat-by-file: Make it faster and more correct","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-11T18:14:57Z","receivedAt":"2011-04-11T18:14:57Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> Currently, when using --dirstat-by-file, it first does the full --dirstat\n> analysis (using diffcore_count_changes()), and then resets 'damage' to 1,\n> if any damage was found by diffcore_count_changes().\n>\n> But --dirstat-by-file is not interested in the file damage per se. It only\n> cares if the file changed at all. In that sense it only cares if the blob\n> SHA1 for a file has changed. We therefore only need to compare the SHA1s\n> of each file pair in the diff queue. As a result, we can skip the entire\n> --dirstat analysis and simply set 'damage' to 1 for each entry where the\n> SHA1 has changed.\n\nVery sensible.  Thanks.\n"},{"id":"165653","messageId":"7vtye4tqmk.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"1302475732-741-4-git-send-email-johan@herland.net","subject":"Re: [PATCHv2 3/3] Teach --dirstat to not completely ignore rearranged lines within a file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-11T21:38:59Z","receivedAt":"2011-04-11T21:38:59Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> Currently, the --dirstat analysis fails to detect when lines within a\n> file are rearranged, because the \"damage\" calculated by show_dirstat()\n> is 0. However, if the SHA1 sum has changed, we already now that there\n> should be at least some minimum amount of damage.\n\nThis logic is sensible, modulo that \"fails to detect\" is actually \"ignores\nmere line movements on purpose\".\n\nIn any case, if the object names are different, we already know that there\nis _some_ damage, and it is very unintiutive to claim that there is _no_\ndamage.\n\n> This patch teaches show_dirstat() to assign a minimum amount of damage\n> (== 1) to entries for which the analysis otherwise yields zero damage.\n\nSo it is perfectly in line with the above logic to give a minimum here.\nZero was simply just unintuitive, and this is a good fix to the problem.\n\n> Obviously this is not a complete fix, but it's at least better to\n\nI however do not understand what \"a complete fix\" means in this context.\nYou've fixed the unintuitiveness, and as far as the description in the\nintroductory paragraph of the problem goes, I think this already is a\ncomplete fix.\n"},{"id":"165657","messageId":"201104112356.09408.johan@herland.net","threadId":"27015","inReplyTo":"7vtye4tqmk.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv2 3/3] Teach --dirstat to not completely ignore rearranged lines within a file","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-11T21:56:07Z","receivedAt":"2011-04-11T21:56:07Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Monday 11 April 2011, Junio C Hamano wrote:\n> Johan Herland <johan@herland.net> writes:\n> > Currently, the --dirstat analysis fails to detect when lines within a\n> > file are rearranged, because the \"damage\" calculated by show_dirstat()\n> > is 0. However, if the SHA1 sum has changed, we already now that there\n> > should be at least some minimum amount of damage.\n> \n> This logic is sensible, modulo that \"fails to detect\" is actually\n> \"ignores mere line movements on purpose\".\n\nI apologize for my commit message not having caught up with discussion \naround this issue. I came into the discussion from the POV of \"--dirstat \ndoes not pick up what --stat picks up; there must be a bug in --dirstat\", \nand my original objective was therefore to \"fix\" --dirstat to be \"more like \n--stat\". Obviously, I now know exactly why --dirstat is different, and that \nwe don't want to fundamentally change it. My commit message should have been \nrephrased in a more positive light as a result. Feel free to fix before \napplying.\n\n> In any case, if the object names are different, we already know that\n> there is _some_ damage, and it is very unintiutive to claim that there\n> is _no_ damage.\n\nAgreed.\n\n> > This patch teaches show_dirstat() to assign a minimum amount of damage\n> > (== 1) to entries for which the analysis otherwise yields zero damage.\n> \n> So it is perfectly in line with the above logic to give a minimum here.\n> Zero was simply just unintuitive, and this is a good fix to the problem.\n> \n> > Obviously this is not a complete fix, but it's at least better to\n> \n> I however do not understand what \"a complete fix\" means in this context.\n> You've fixed the unintuitiveness, and as far as the description in the\n> introductory paragraph of the problem goes, I think this already is a\n> complete fix.\n\nI still feel that a file with 1000 rearranged lines should somehow count \n\"more\" than a file with only 1 rearranged line, but it's hard to get there \nwithout futzing with diffcore_count_changes(), probably making the whole \nthing considerably more expensive... So in that sense, I agree that the \ncurrent solution is probably as complete as we can get.\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"165659","messageId":"7vwrj0sap3.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"201104112356.09408.johan@herland.net","subject":"Re: [PATCHv2 3/3] Teach --dirstat to not completely ignore rearranged lines within a file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-11T22:08:24Z","receivedAt":"2011-04-11T22:08:24Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> I still feel that a file with 1000 rearranged lines should somehow count \n> \"more\" than a file with only 1 rearranged line,...\n\nI think that is just entirely a different mode of operation.  I do not\nthink it is wrong to have an alternative implementation of the dirstat\ndamage counter that is based on numstat code.\n\nIt may end up counting the damage slower than the current code, and more\nimportantly it will count a different kind of damage than the current code\ndoes, so we may probably want to make it an optional feature.\n"},{"id":"165677","messageId":"201104121122.56870.johan@herland.net","threadId":"27015","inReplyTo":"7vwrj0sap3.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv2 3/3] Teach --dirstat to not completely ignore rearranged lines within a file","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-12T09:22:56Z","receivedAt":"2011-04-12T09:22:56Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Tuesday 12 April 2011, Junio C Hamano wrote:\n> Johan Herland <johan@herland.net> writes:\n> > I still feel that a file with 1000 rearranged lines should somehow\n> > count \"more\" than a file with only 1 rearranged line,...\n> \n> I think that is just entirely a different mode of operation.  I do not\n> think it is wrong to have an alternative implementation of the dirstat\n> damage counter that is based on numstat code.\n> \n> It may end up counting the damage slower than the current code, and more\n> importantly it will count a different kind of damage than the current\n> code does, so we may probably want to make it an optional feature.\n\nI wrote it up just for fun, and here's the patch. I'll leave it up to you\nto decide if it's worth it.\n\nFirst, though, I've got another patch to --dirstat, which - in the case\nof renames, attributes the damage to the target filename instead of the\nsource filename. I found this more intuitive, especially in the case of\ncopies (-C -C) where damage would be attributed to the directory\ncontaining the (unchanged) source file, instead of the directory\ncontaining the (changed) target file.\n\n\nHave fun! :)\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"165678","messageId":"201104121124.35128.johan@herland.net","threadId":"27015","inReplyTo":"201104121122.56870.johan@herland.net","subject":"[PATCH 4/3] --dirstat: In case of renames, use target filename instead of source filename","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-12T09:24:34Z","receivedAt":"2011-04-12T09:24:34Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"This changes --dirstat analysis to count \"damage\" toward the target filename,\nrather than the source filename. For renames within a directory, this won't\nmatter to the final output, but when moving files between diretories, the\noutput now lists the target directory rather than the source directory.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n diff.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 3e0bc1f..5376d01 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1540,7 +1540,7 @@ static void show_dirstat(struct diff_options *options)\n \t\tunsigned long copied, added, damage;\n \t\tint content_changed;\n \n-\t\tname = p->one->path ? p->one->path : p->two->path;\n+\t\tname = p->two->path ? p->two->path : p->one->path;\n \n \t\tif (p->one->sha1_valid && p->two->sha1_valid)\n \t\t\tcontent_changed = hashcmp(p->one->sha1, p->two->sha1);\n-- \n1.7.5.rc1.3.g4d7b\n\n\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"165679","messageId":"201104121126.49881.johan@herland.net","threadId":"27015","inReplyTo":"201104121122.56870.johan@herland.net","subject":"[RFC/PATCH 5/3] Alternative --dirstat implementation, based on diffstat analysis","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-12T09:26:49Z","receivedAt":"2011-04-12T09:26:49Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"This patch adds an alternative implementation of show_dirstat(), called\nshow_dirstat_based_on_diffstat(), which uses the more expensive diffstat\nanalysis (as opposed to --dirstat's own (inexpensive) analysis) to derive\nthe numbers from which the --dirstat output is computed.\n\nThe alternative implementation is controlled by a new config variable called\ndiff.dirstatBasedOnDiffstat.\n\nIn linux-2.6.git, running\n\n  time git diff v2.6.20..v2.6.30 --dirstat=0 > /dev/null\n\nwith and without diff.dirstatBasedOnDiffstat enabled yields the following\naverage runtimes on my machine:\n\n- disabled: ~6.0 s\n- enabled:  ~9.7 s\n\nSo, as expected, there's a considerable performance hit (>60%) by going\nthrough the full diffstat analysis. As such, the new option is probably\nonly useful if you really need the --dirstat numbers to be consistent with\nthe numbers returned from the other --*stat options.\n\nIn --dirstat-by-file mode, the diffstat analysis is obviously a waste of time,\nso --dirstat-by-file automatically disabled diff.dirstatBasedOnDiffstat.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n\nThis might not be worth applying at all, but if it is, I can send a re-roll\nwith documentation and more user-friendlyness.\n\n\nHave fun! :)\n\n...Johan\n\n diff.c |   53 +++++++++++++++++++++++++++++++++++++++++++++++++++--\n 1 files changed, 51 insertions(+), 2 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 5376d01..a496ba6 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -31,6 +31,7 @@ static const char *external_diff_cmd_cfg;\n int diff_auto_refresh_index = 1;\n static int diff_mnemonic_prefix;\n static int diff_no_prefix;\n+static int dirstat_based_on_diffstat;\n static struct diff_options default_diff_options;\n \n static char diff_colors[][COLOR_MAXLEN] = {\n@@ -103,6 +104,10 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)\n \t\tdiff_no_prefix = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(var, \"diff.dirstatbasedondiffstat\")) {\n+\t\tdirstat_based_on_diffstat = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(var, \"diff.external\"))\n \t\treturn git_config_string(&external_diff_cmd_cfg, var, value);\n \tif (!strcmp(var, \"diff.wordregex\"))\n@@ -1619,6 +1624,43 @@ found_damage:\n \tgather_dirstat(options, &dir, changed, \"\", 0);\n }\n \n+static void show_dirstat_based_on_diffstat(struct diffstat_t *data, struct diff_options \n*options)\n+{\n+\tint i;\n+\tunsigned long changed;\n+\tstruct dirstat_dir dir;\n+\n+\tif (data->nr == 0)\n+\t\treturn;\n+\n+\tdir.files = NULL;\n+\tdir.alloc = 0;\n+\tdir.nr = 0;\n+\tdir.percent = options->dirstat_percent;\n+\tdir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);\n+\n+\tchanged = 0;\n+\tfor (i = 0; i < data->nr; i++) {\n+\t\tstruct diffstat_file *file = data->files[i];\n+\t\tunsigned long damage;\n+\n+\t\tdamage = file->added + file->deleted;\n+\t\tALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);\n+\t\tdir.files[dir.nr].name = file->name;\n+\t\tdir.files[dir.nr].changed = damage;\n+\t\tchanged += damage;\n+\t\tdir.nr++;\n+\t}\n+\n+\t/* This can happen even with many files, if everything was renames */\n+\tif (!changed)\n+\t\treturn;\n+\n+\t/* Show all directories with more than x% of the changes */\n+\tqsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);\n+\tgather_dirstat(options, &dir, changed, \"\", 0);\n+}\n+\n static void free_diffstat_info(struct diffstat_t *diffstat)\n {\n \tint i;\n@@ -4012,7 +4054,12 @@ void diff_flush(struct diff_options *options)\n \t\tseparator++;\n \t}\n \n-\tif (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {\n+\t// --dirstat-by-file REALLY don't need the full diffstat analysis\n+\tif (DIFF_OPT_TST(options, DIRSTAT_BY_FILE))\n+\t\tdirstat_based_on_diffstat = 0;\n+\n+\tif (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT) ||\n+\t    ((output_format & DIFF_FORMAT_DIRSTAT) && dirstat_based_on_diffstat)) {\n \t\tstruct diffstat_t diffstat;\n \n \t\tmemset(&diffstat, 0, sizeof(struct diffstat_t));\n@@ -4027,10 +4074,12 @@ void diff_flush(struct diff_options *options)\n \t\t\tshow_stats(&diffstat, options);\n \t\tif (output_format & DIFF_FORMAT_SHORTSTAT)\n \t\t\tshow_shortstats(&diffstat, options);\n+\t\tif (output_format & DIFF_FORMAT_DIRSTAT)\n+\t\t\tshow_dirstat_based_on_diffstat(&diffstat, options);\n \t\tfree_diffstat_info(&diffstat);\n \t\tseparator++;\n \t}\n-\tif (output_format & DIFF_FORMAT_DIRSTAT)\n+\tif ((output_format & DIFF_FORMAT_DIRSTAT) && !dirstat_based_on_diffstat)\n \t\tshow_dirstat(options);\n \n \tif (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"165687","messageId":"BANLkTim9U4cOnV+5=Mp-2g_M6+JOiM5e7A@mail.gmail.com","threadId":"27015","inReplyTo":"201104121126.49881.johan@herland.net","subject":"Re: [RFC/PATCH 5/3] Alternative --dirstat implementation, based on diffstat analysis","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2011-04-12T14:46:47Z","receivedAt":"2011-04-12T14:46:47Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Tue, Apr 12, 2011 at 2:26 AM, Johan Herland <johan@herland.net> wrote:\n> This patch adds an alternative implementation of show_dirstat(), called\n> show_dirstat_based_on_diffstat(), which uses the more expensive diffstat\n> analysis (as opposed to --dirstat's own (inexpensive) analysis) to derive\n> the numbers from which the --dirstat output is computed.\n>\n> The alternative implementation is controlled by a new config variable called\n> diff.dirstatBasedOnDiffstat.\n\nSo I don't hate the idea, but I do hate the \"use a config option\"\npart. Or rather, I hate the fact that it's the _only_ way to do it\n(and the particular config name you chose).\n\nI'd much rather have a command line option for the two cases, and then\nhave the config file part be a way to perhaps set the default value.\n\nSomething like \"--dirstat=exact\", and then without the explicit\nsetting you might fall back on the config file.\n\n(One reason I'd like that is that I think the \"--cumulative\" option\nwas a mistake. Again, it _should_ have been another option to\n\"--dirstat\", rather than a stand-alone option that makes no sense on\nits own)\n\nSo in a better world, I think we should be able to write\n\n  --dirstat=[non]exact,[non]cumulative,1\n\nto say exactly what kind of dirstat we actually want. And the config\noptions would also match, iow\n\n  [dirstat]\n     exact = true\n     cumulative = true\n     percentage = 1\n\nrather than the cumbersome name you chose that is based on an\nimplementation issue rather than a user interface issue (I think\nconfig options should talk about the user experience more than about\nhow it was implemented, so \"diff.dirstatbasedondiffstat: is not\nwonderful)\n\nWouldn't that be nicer? Can I sucker you into parsing something like that?\n\nIf you do this, another thing I've occasionally wanted to see was a\npercentage that allows fractional percentages. We show the results in\npermille, after all, it should be possible to ask for cut-offs at the\nsame precision, ie \"1.5%\".\n\n                 Linus \"can I find a sucker to implement this\" Torvalds\n"},{"id":"165688","messageId":"BANLkTinHcDug0A+n9N4hkApF_xBU+nEkBw@mail.gmail.com","threadId":"27015","inReplyTo":"201104121124.35128.johan@herland.net","subject":"Re: [PATCH 4/3] --dirstat: In case of renames, use target filename instead of source filename","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2011-04-12T14:59:21Z","receivedAt":"2011-04-12T14:59:21Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Tue, Apr 12, 2011 at 2:24 AM, Johan Herland <johan@herland.net> wrote:\n> This changes --dirstat analysis to count \"damage\" toward the target filename,\n> rather than the source filename. For renames within a directory, this won't\n> matter to the final output, but when moving files between diretories, the\n> output now lists the target directory rather than the source directory.\n\nAck. I think the use of the source filename was actually a bug.\n\nThe original dirstat code used the \"struct diffstat_file_t *\" pointer,\nand took the name from the ->name field of that. And that actually\ndefaults to the target name (see diffstat_add()). But then commit\nc04a7155a03e changed it to use \"struct dirstat_file\" and picked the\nname for that from the source.\n\n                        Linus\n"},{"id":"165689","messageId":"BANLkTi=yYU+v1Xx_YO6kpC8+1ukOdwb6SQ@mail.gmail.com","threadId":"27015","inReplyTo":"BANLkTim9U4cOnV+5=Mp-2g_M6+JOiM5e7A@mail.gmail.com","subject":"Re: [RFC/PATCH 5/3] Alternative --dirstat implementation, based on diffstat analysis","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2011-04-12T15:08:28Z","receivedAt":"2011-04-12T15:08:28Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Tue, Apr 12, 2011 at 7:46 AM, Linus Torvalds\n<torvalds@linux-foundation.org> wrote:\n>\n> So I don't hate the idea, but I do hate the \"use a config option\"\n> part. Or rather, I hate the fact that it's the _only_ way to do it\n> (and the particular config name you chose).\n\nOh, and one thing strikes me: I think the fast dirstat gave reasonable\nvalues when you had mixed text and binary (in the kernel tree, look\nfor the Documentation/logo.gif file, for example: it changed to the\nTasmanian devil in one release).\n\nHave you checked what happens to that when you use the diffstat one?\nBecause binary files are done very differently (byte-based counts).\n\nSo check out\n\n   git show --dirstat 3d4f16348b77efbf81b7fa186a18a0eb815b6b84\n\nwith and without your change. The old dirstat gives\n\n  44.0% Documentation/\n  55.9% drivers/video/logo/\n\nwhich is at least not completely insane.\n\nThe reason I bring this up is because I think this was an issue at one\npoint, and one of the statistics things (--stat or --numstat or\n--dirstat) gave absolutely horrid values (basically comparing \"bytes\nchanged\" for binaries with \"lines changed\" for text files). Resulting\nin totally skewed statistics.\n\n                       Linus\n"},{"id":"165703","messageId":"7voc4bpbcp.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"201104121126.49881.johan@herland.net","subject":"Re: [RFC/PATCH 5/3] Alternative --dirstat implementation, based on diffstat analysis","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-12T18:34:46Z","receivedAt":"2011-04-12T18:34:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> This patch adds an alternative implementation of show_dirstat(), called\n> show_dirstat_based_on_diffstat(), which uses the more expensive diffstat\n> analysis (as opposed to --dirstat's own (inexpensive) analysis) to derive\n> the numbers from which the --dirstat output is computed.\n> ...\n> diff --git a/diff.c b/diff.c\n> index 5376d01..a496ba6 100644\n> --- a/diff.c\n> +++ b/diff.c\n> @@ -31,6 +31,7 @@ static const char *external_diff_cmd_cfg;\n>  int diff_auto_refresh_index = 1;\n>  static int diff_mnemonic_prefix;\n>  static int diff_no_prefix;\n> +static int dirstat_based_on_diffstat;\n>  static struct diff_options default_diff_options;\n>  \n>  static char diff_colors[][COLOR_MAXLEN] = {\n> @@ -103,6 +104,10 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)\n>  \t\tdiff_no_prefix = git_config_bool(var, value);\n>  \t\treturn 0;\n>  \t}\n> +\tif (!strcmp(var, \"diff.dirstatbasedondiffstat\")) {\n> +\t\tdirstat_based_on_diffstat = git_config_bool(var, value);\n> +\t\treturn 0;\n> +\t}\n\nPeople may think of other damage calculator, so the variable shouldn't be\na boolean that says \"dirstat-based-on-diffstat\" but rather an enum.\n\nWe would need a command line interface for this.  How about something like\n\"--dirstat=lines\" vs \"--dirstat=changes\", and default \"--dirstat\" without\nan explicit type to traditional \"--dirstat=changes\"?  \n"},{"id":"165714","messageId":"201104130003.02869.johan@herland.net","threadId":"27015","inReplyTo":"BANLkTi=yYU+v1Xx_YO6kpC8+1ukOdwb6SQ@mail.gmail.com","subject":"Re: [RFC/PATCH 5/3] Alternative --dirstat implementation, based on diffstat analysis","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-12T22:03:02Z","receivedAt":"2011-04-12T22:03:02Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Tuesday 12 April 2011, Linus Torvalds wrote:\n> On Tue, Apr 12, 2011 at 7:46 AM, Linus Torvalds\n> \n> <torvalds@linux-foundation.org> wrote:\n> > So I don't hate the idea, but I do hate the \"use a config option\"\n> > part. Or rather, I hate the fact that it's the _only_ way to do it\n> > (and the particular config name you chose).\n> \n> Oh, and one thing strikes me: I think the fast dirstat gave reasonable\n> values when you had mixed text and binary (in the kernel tree, look\n> for the Documentation/logo.gif file, for example: it changed to the\n> Tasmanian devil in one release).\n> \n> Have you checked what happens to that when you use the diffstat one?\n> Because binary files are done very differently (byte-based counts).\n> \n> So check out\n> \n>    git show --dirstat 3d4f16348b77efbf81b7fa186a18a0eb815b6b84\n> \n> with and without your change. The old dirstat gives\n> \n>   44.0% Documentation/\n>   55.9% drivers/video/logo/\n> \n> which is at least not completely insane.\n\nMy change obviously makes a difference:\n\n  68.7% Documentation/\n  31.2% drivers/video/logo/\n\nTo make some more sense of the number, here they are with some extra\noutput from a debug printf:\n\n$ ../git/git show --dirstat 3d4f163\n  [...]\n\n        Documentation/logo.gif: +16335 -0 => damage = 16335\n        Documentation/logo.svg: +0 -310450 => damage = 310450\n        Documentation/logo.txt: +562 -200 => damage = 762\n        drivers/video/logo/logo_linux_clut224.ppm: +76628 -136093 => damage = 212721\n        drivers/video/logo/logo_linux_vga16.ppm: +76837 -126084 => damage = 202921\n  44.0% Documentation/\n  55.9% drivers/video/logo/\n\n\n$ ../git/git -c diff.dirstatbasedondiffstat=true show --dirstat 3d4f163\n  [...]\n\n        Documentation/logo.gif: +16335 -0 => damage = 16335\n        Documentation/logo.svg: +0 -2911 => damage = 2911\n        Documentation/logo.txt: +12 -3 => damage = 15\n        drivers/video/logo/logo_linux_clut224.ppm: +1602 -2826 => damage = 4428\n        drivers/video/logo/logo_linux_vga16.ppm: +1602 -2737 => damage = 4339\n  68.7% Documentation/\n  31.2% drivers/video/logo/\n\nIn the original dirstat numbers (computed by diffcore_count_changes())\nall the numbers (both from text and binary files) are on a byte scale.\n(making the binary logo.gif changes proportional in scale to the rest).\n\nIn the diffstat analysis, however, binary changes are reported in bytes,\nwhile text changes are reported in lines. This obviously makes binary\nchanges count disproportionately more than textual changes.\n\n> The reason I bring this up is because I think this was an issue at one\n> point, and one of the statistics things (--stat or --numstat or\n> --dirstat) gave absolutely horrid values (basically comparing \"bytes\n> changed\" for binaries with \"lines changed\" for text files). Resulting\n> in totally skewed statistics.\n\nIndeed, that's exactly what's going on here. Looking at the other\n--*stat options:\n\n--stat has a special output mode for binary files:\n\n        Documentation/logo.gif      | Bin 0 -> 16335 bytes\n\n--numstat refuses to show any meaningful output for binary files:\n\n        -       -       Documentation/logo.gif\n\n--shortstat skips binary (and unmerged) files altogether.\n\n\nSo, how should we count binary files in the diffstat version of\n--dirstat? Looking at the available data in struct diffstat_file,\nthere's not a lot of \"source material\" available. If I had easy\naccess to the file pre/post size, and the total number of lines,\nI could calculate the average number of bytes per line, and then\nmultiply that with the diffstat numbers to get an approximate\nbyte count. A crude fallback would be to use 64 bytes per line...\n\nA better solution might be to add a flag to struct diffstat_t\nindicating that we want byte counts (as opposed to line counts) for\ntext files, and then use that flag from within diffstat_consume() to\nadd \"len\" instead of \"1\" to x->added/deleted.\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"165715","messageId":"BANLkTi=6gV1S+6JtK0GNKW6jhA1RFbYXHw@mail.gmail.com","threadId":"27015","inReplyTo":"201104130003.02869.johan@herland.net","subject":"Re: [RFC/PATCH 5/3] Alternative --dirstat implementation, based on diffstat analysis","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2011-04-12T22:12:14Z","receivedAt":"2011-04-12T22:12:14Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Tue, Apr 12, 2011 at 3:03 PM, Johan Herland <johan@herland.net> wrote:\n>\n> --stat has a special output mode for binary files:\n>\n>        Documentation/logo.gif      | Bin 0 -> 16335 bytes\n\nYeah, I think that's the one we introduced exactly to not give crazy\nresults (ie really big bars of +++/---).\n\nOne option might be to just do something like\n\n    if (binary)\n       damage /= 52;\n\nand just say that \"52 bytes of binary diff counts as one line\".\n\nWhich is obviously totally crazy and idiotic, but it actually is\nroughly what happens when you print out the binary diff (that \"52\" is\nmade up, but I think it may be true. I forget what encoding rules we\nuse, it's in that kind of range).\n\nSo it's \"true\" in some insane made-up sense.\n\nFeel free to pick any number you like instead of \"52\" that makes some sense.\n\nBecause I don't think we have the option to just dismiss the binary\nchanges entirely, and I don't like the idea of comparing bytes against\nlines.\n\nThe other option would be to turn lines (of a non-binary diff) into\nbytes, and not count lines at all.\n\n                      Linus\n"},{"id":"165716","messageId":"7vr597nm86.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"201104130003.02869.johan@herland.net","subject":"Re: [RFC/PATCH 5/3] Alternative --dirstat implementation, based on diffstat analysis","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-12T22:22:49Z","receivedAt":"2011-04-12T22:22:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> So, how should we count binary files in the diffstat version of\n> --dirstat?\n\nIIRC, the reason Linus used the \"change\" (not \"lines\") damage computation\nin dirstat was exactly for this reason.\n\nComparing and combining the damage as number of lines and changed bytes\nsimply does not make much sense, so my gut answer to this question is \"we\nshouldn't\".  The --numstat mode punts exactly for this reason, to avoid\ntempting people to add numbers up without thinking and getting nonsense\nresults.\n\nI suspect that any heuristics is as good as your divide-by-64; you\nprobably could run count_lines(one->data, one->size) in the text diff\ncodepath in builtin_diffstat() to keep a running average of the line\nlengths of the files involved, but I do not think it is worth it.\n"},{"id":"166339","messageId":"1303776102-9085-1-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"BANLkTim9U4cOnV+5=Mp-2g_M6+JOiM5e7A@mail.gmail.com","subject":"[PATCH 0/6] --dirstat fixes, part 2","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-26T00:01:36Z","receivedAt":"2011-04-26T00:01:36Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Hi,\n\nI finally found the time to re-roll the remaining dirstat fixes,\nincorporating feedback from Linus and Junio in the surrounding thread.\n\nThe first patch adds a number of testcases for --dirstat, guarding\nagainst regressions.\n\nThe second patch fixes a small issue I found while playing around with\n--dirstat=0.\n\nThe next three patches revamps the dirstat-related command-line options\nand introduces a diff.dirstat config variable for controlling the\n--dirstat defaults. The third patch here (accepting floating-point\npercentage input) has some remaining questions mentioned in that email.\n\nFinally, the last patch is a re-roll of the previous \"RFC/PATCH 5/3\"\nthat introduces a new dirstat mode, based on the diffstat analysis.\n\n\nHave fun! :)\n\n...Johan\n\n\nJohan Herland (6):\n  Add several testcases for --dirstat and friends\n  Make --dirstat=0 output directories that contribute < 0.1% of changes\n  Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file\n  Add config variable for specifying default --dirstat behavior\n  Use floating point for --dirstat percentages\n  New --dirstat=lines mode, doing dirstat analysis based on diffstat\n\n Documentation/config.txt       |   43 ++\n Documentation/diff-options.txt |   52 ++-\n diff.c                         |  182 ++++++++-\n diff.h                         |    3 +-\n t/t4046-diff-dirstat.sh        |  873 ++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 1121 insertions(+), 32 deletions(-)\n create mode 100755 t/t4046-diff-dirstat.sh\n\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166340","messageId":"1303776102-9085-2-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303776102-9085-1-git-send-email-johan@herland.net","subject":"[PATCH 1/6] Add several testcases for --dirstat and friends","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-26T00:01:37Z","receivedAt":"2011-04-26T00:01:37Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Currently, t4013 is the only selftest that exercises the --dirstat machinery,\nbut it only does a superficial verification of --dirstat's output.\n\nThis patch adds a new selftest - t4046-diff-dirstat.sh - which prepares a\ncommit containing:\n- unchanged files, changed files and files with rearranged lines\n- copied files, moved files, and unmoved files\n\nIt then verifies the correct dirstat output for that commit in the following\ndirstat modes:\n- --dirstat\n- --dirstat=0\n- --cumulative\n- --dirstat-by-file\n- (plus combinations of the above)\n\nEach of the above tests are also run with:\n- no rename detection\n- rename detection (-M)\n- expensive copy detection (-C -C)\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n t/t4046-diff-dirstat.sh |  562 +++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 562 insertions(+), 0 deletions(-)\n create mode 100755 t/t4046-diff-dirstat.sh\n\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nnew file mode 100755\nindex 0000000..1690468\n--- /dev/null\n+++ b/t/t4046-diff-dirstat.sh\n@@ -0,0 +1,562 @@\n+#!/bin/sh\n+\n+test_description='diff --dirstat tests'\n+. ./test-lib.sh\n+\n+# set up two commits where the second commit has these files\n+# (10 lines in each file):\n+#\n+#   unchanged/text           (unchanged from 1st commit)\n+#   changed/text             (changed 1st line)\n+#   rearranged/text          (swapped 1st and 2nd line)\n+#   dst/copy/unchanged/text  (copied from src/copy/unchanged/text, unchanged)\n+#   dst/copy/changed/text    (copied from src/copy/changed/text, changed)\n+#   dst/copy/rearranged/text (copied from src/copy/rearranged/text, rearranged)\n+#   dst/move/unchanged/text  (moved from src/move/unchanged/text, unchanged)\n+#   dst/move/changed/text    (moved from src/move/changed/text, changed)\n+#   dst/move/rearranged/text (moved from src/move/rearranged/text, rearranged)\n+\n+test_expect_success 'setup' '\n+\tmkdir unchanged &&\n+\tmkdir changed &&\n+\tmkdir rearranged &&\n+\tmkdir src &&\n+\tmkdir src/copy &&\n+\tmkdir src/copy/unchanged &&\n+\tmkdir src/copy/changed &&\n+\tmkdir src/copy/rearranged &&\n+\tmkdir src/move &&\n+\tmkdir src/move/unchanged &&\n+\tmkdir src/move/changed &&\n+\tmkdir src/move/rearranged &&\n+\tcat <<EOF >unchanged/text &&\n+unchanged       line #0\n+unchanged       line #1\n+unchanged       line #2\n+unchanged       line #3\n+unchanged       line #4\n+unchanged       line #5\n+unchanged       line #6\n+unchanged       line #7\n+unchanged       line #8\n+unchanged       line #9\n+EOF\n+\tcat <<EOF >changed/text &&\n+changed         line #0\n+changed         line #1\n+changed         line #2\n+changed         line #3\n+changed         line #4\n+changed         line #5\n+changed         line #6\n+changed         line #7\n+changed         line #8\n+changed         line #9\n+EOF\n+\tcat <<EOF >rearranged/text &&\n+rearranged      line #0\n+rearranged      line #1\n+rearranged      line #2\n+rearranged      line #3\n+rearranged      line #4\n+rearranged      line #5\n+rearranged      line #6\n+rearranged      line #7\n+rearranged      line #8\n+rearranged      line #9\n+EOF\n+\tcat <<EOF >src/copy/unchanged/text &&\n+copy  unchanged line #0\n+copy  unchanged line #1\n+copy  unchanged line #2\n+copy  unchanged line #3\n+copy  unchanged line #4\n+copy  unchanged line #5\n+copy  unchanged line #6\n+copy  unchanged line #7\n+copy  unchanged line #8\n+copy  unchanged line #9\n+EOF\n+\tcat <<EOF >src/copy/changed/text &&\n+copy    changed line #0\n+copy    changed line #1\n+copy    changed line #2\n+copy    changed line #3\n+copy    changed line #4\n+copy    changed line #5\n+copy    changed line #6\n+copy    changed line #7\n+copy    changed line #8\n+copy    changed line #9\n+EOF\n+\tcat <<EOF >src/copy/rearranged/text &&\n+copy rearranged line #0\n+copy rearranged line #1\n+copy rearranged line #2\n+copy rearranged line #3\n+copy rearranged line #4\n+copy rearranged line #5\n+copy rearranged line #6\n+copy rearranged line #7\n+copy rearranged line #8\n+copy rearranged line #9\n+EOF\n+\tcat <<EOF >src/move/unchanged/text &&\n+move  unchanged line #0\n+move  unchanged line #1\n+move  unchanged line #2\n+move  unchanged line #3\n+move  unchanged line #4\n+move  unchanged line #5\n+move  unchanged line #6\n+move  unchanged line #7\n+move  unchanged line #8\n+move  unchanged line #9\n+EOF\n+\tcat <<EOF >src/move/changed/text &&\n+move    changed line #0\n+move    changed line #1\n+move    changed line #2\n+move    changed line #3\n+move    changed line #4\n+move    changed line #5\n+move    changed line #6\n+move    changed line #7\n+move    changed line #8\n+move    changed line #9\n+EOF\n+\tcat <<EOF >src/move/rearranged/text &&\n+move rearranged line #0\n+move rearranged line #1\n+move rearranged line #2\n+move rearranged line #3\n+move rearranged line #4\n+move rearranged line #5\n+move rearranged line #6\n+move rearranged line #7\n+move rearranged line #8\n+move rearranged line #9\n+EOF\n+\tgit add . &&\n+\tgit commit -m \"initial\" &&\n+\tmkdir dst &&\n+\tmkdir dst/copy &&\n+\tmkdir dst/copy/unchanged &&\n+\tmkdir dst/copy/changed &&\n+\tmkdir dst/copy/rearranged &&\n+\tmkdir dst/move &&\n+\tmkdir dst/move/unchanged &&\n+\tmkdir dst/move/changed &&\n+\tmkdir dst/move/rearranged &&\n+\tcat <<EOF >changed/text &&\n+CHANGED XXXXXXX line #0\n+changed         line #1\n+changed         line #2\n+changed         line #3\n+changed         line #4\n+changed         line #5\n+changed         line #6\n+changed         line #7\n+changed         line #8\n+changed         line #9\n+EOF\n+\tcat <<EOF >rearranged/text &&\n+rearranged      line #1\n+rearranged      line #0\n+rearranged      line #2\n+rearranged      line #3\n+rearranged      line #4\n+rearranged      line #5\n+rearranged      line #6\n+rearranged      line #7\n+rearranged      line #8\n+rearranged      line #9\n+EOF\n+\tcat <<EOF >dst/copy/unchanged/text &&\n+copy  unchanged line #0\n+copy  unchanged line #1\n+copy  unchanged line #2\n+copy  unchanged line #3\n+copy  unchanged line #4\n+copy  unchanged line #5\n+copy  unchanged line #6\n+copy  unchanged line #7\n+copy  unchanged line #8\n+copy  unchanged line #9\n+EOF\n+\tcat <<EOF >dst/copy/changed/text &&\n+copy XXXCHANGED line #0\n+copy    changed line #1\n+copy    changed line #2\n+copy    changed line #3\n+copy    changed line #4\n+copy    changed line #5\n+copy    changed line #6\n+copy    changed line #7\n+copy    changed line #8\n+copy    changed line #9\n+EOF\n+\tcat <<EOF >dst/copy/rearranged/text &&\n+copy rearranged line #1\n+copy rearranged line #0\n+copy rearranged line #2\n+copy rearranged line #3\n+copy rearranged line #4\n+copy rearranged line #5\n+copy rearranged line #6\n+copy rearranged line #7\n+copy rearranged line #8\n+copy rearranged line #9\n+EOF\n+\tcat <<EOF >dst/move/unchanged/text &&\n+move  unchanged line #0\n+move  unchanged line #1\n+move  unchanged line #2\n+move  unchanged line #3\n+move  unchanged line #4\n+move  unchanged line #5\n+move  unchanged line #6\n+move  unchanged line #7\n+move  unchanged line #8\n+move  unchanged line #9\n+EOF\n+\tcat <<EOF >dst/move/changed/text &&\n+move XXXCHANGED line #0\n+move    changed line #1\n+move    changed line #2\n+move    changed line #3\n+move    changed line #4\n+move    changed line #5\n+move    changed line #6\n+move    changed line #7\n+move    changed line #8\n+move    changed line #9\n+EOF\n+\tcat <<EOF >dst/move/rearranged/text &&\n+move rearranged line #1\n+move rearranged line #0\n+move rearranged line #2\n+move rearranged line #3\n+move rearranged line #4\n+move rearranged line #5\n+move rearranged line #6\n+move rearranged line #7\n+move rearranged line #8\n+move rearranged line #9\n+EOF\n+\tgit add . &&\n+\tgit rm -r src/move/unchanged &&\n+\tgit rm -r src/move/changed &&\n+\tgit rm -r src/move/rearranged &&\n+\tgit commit -m \"changes\"\n+'\n+\n+cat <<EOF >expect_diff_stat\n+ changed/text             |    2 +-\n+ dst/copy/changed/text    |   10 ++++++++++\n+ dst/copy/rearranged/text |   10 ++++++++++\n+ dst/copy/unchanged/text  |   10 ++++++++++\n+ dst/move/changed/text    |   10 ++++++++++\n+ dst/move/rearranged/text |   10 ++++++++++\n+ dst/move/unchanged/text  |   10 ++++++++++\n+ rearranged/text          |    2 +-\n+ src/move/changed/text    |   10 ----------\n+ src/move/rearranged/text |   10 ----------\n+ src/move/unchanged/text  |   10 ----------\n+ 11 files changed, 62 insertions(+), 32 deletions(-)\n+EOF\n+\n+cat <<EOF >expect_diff_stat_M\n+ changed/text                      |    2 +-\n+ dst/copy/changed/text             |   10 ++++++++++\n+ dst/copy/rearranged/text          |   10 ++++++++++\n+ dst/copy/unchanged/text           |   10 ++++++++++\n+ {src => dst}/move/changed/text    |    2 +-\n+ {src => dst}/move/rearranged/text |    2 +-\n+ {src => dst}/move/unchanged/text  |    0\n+ rearranged/text                   |    2 +-\n+ 8 files changed, 34 insertions(+), 4 deletions(-)\n+EOF\n+\n+cat <<EOF >expect_diff_stat_CC\n+ changed/text                      |    2 +-\n+ {src => dst}/copy/changed/text    |    2 +-\n+ {src => dst}/copy/rearranged/text |    2 +-\n+ {src => dst}/copy/unchanged/text  |    0\n+ {src => dst}/move/changed/text    |    2 +-\n+ {src => dst}/move/rearranged/text |    2 +-\n+ {src => dst}/move/unchanged/text  |    0\n+ rearranged/text                   |    2 +-\n+ 8 files changed, 6 insertions(+), 6 deletions(-)\n+EOF\n+\n+test_expect_success 'sanity check setup (--stat)' '\n+\tgit diff --stat HEAD^..HEAD >actual_diff_stat &&\n+\ttest_cmp expect_diff_stat actual_diff_stat &&\n+\tgit diff --stat -M HEAD^..HEAD >actual_diff_stat_M &&\n+\ttest_cmp expect_diff_stat_M actual_diff_stat_M &&\n+\tgit diff --stat -C -C HEAD^..HEAD >actual_diff_stat_CC &&\n+\ttest_cmp expect_diff_stat_CC actual_diff_stat_CC\n+'\n+\n+# changed/text and rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+EOF\n+\n+# rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+   5.8% dst/move/changed/\n+EOF\n+\n+# rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+  32.6% dst/move/changed/\n+EOF\n+\n+test_expect_success 'vanilla --dirstat' '\n+\tgit diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+   5.8% dst/move/changed/\n+   0.1% dst/move/rearranged/\n+   0.1% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+   0.6% dst/copy/rearranged/\n+  32.6% dst/move/changed/\n+   0.6% dst/move/rearranged/\n+   0.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=0' '\n+\tgit diff --dirstat=0 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  32.5% dst/copy/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  32.5% dst/move/\n+  65.1% dst/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+  32.5% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+  88.0% dst/copy/\n+   5.8% dst/move/changed/\n+   0.1% dst/move/rearranged/\n+   5.9% dst/move/\n+  94.0% dst/\n+   0.1% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+   0.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  32.6% dst/move/changed/\n+   0.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+   0.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=0 --cumulative' '\n+\tgit diff --dirstat=0 --cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0 --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0 --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   9.0% changed/\n+   9.0% dst/copy/changed/\n+   9.0% dst/copy/rearranged/\n+   9.0% dst/copy/unchanged/\n+   9.0% dst/move/changed/\n+   9.0% dst/move/rearranged/\n+   9.0% dst/move/unchanged/\n+   9.0% rearranged/\n+   9.0% src/move/changed/\n+   9.0% src/move/rearranged/\n+   9.0% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file' '\n+\tgit diff --dirstat-by-file HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+  27.2% dst/copy/\n+  27.2% dst/move/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file=10' '\n+\tgit diff --dirstat-by-file=10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file=10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file=10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   9.0% changed/\n+   9.0% dst/copy/changed/\n+   9.0% dst/copy/rearranged/\n+   9.0% dst/copy/unchanged/\n+  27.2% dst/copy/\n+   9.0% dst/move/changed/\n+   9.0% dst/move/rearranged/\n+   9.0% dst/move/unchanged/\n+  27.2% dst/move/\n+  54.5% dst/\n+   9.0% rearranged/\n+   9.0% src/move/changed/\n+   9.0% src/move/rearranged/\n+   9.0% src/move/unchanged/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  42.8% dst/copy/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  28.5% dst/move/\n+  71.4% dst/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file --cumulative' '\n+\tgit diff --dirstat-by-file --cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166343","messageId":"1303776102-9085-3-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303776102-9085-1-git-send-email-johan@herland.net","subject":"[PATCH 2/6] Make --dirstat=0 output directories that contribute < 0.1% of changes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-26T00:01:38Z","receivedAt":"2011-04-26T00:01:38Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"The expected output from --dirstat=0, is to include any directory with\nchanges, even if those changes contribute a minuscule portion of the total\nchanges. However, currently, directories that contribute less than 0.1% are\nnot included, since their 'permille' value is 0, and there is an\n'if (permille)' check in gather_dirstat() that causes them to be ignored.\n\nThis test is obviously intended to exclude directories that contribute no\nchanges whatsoever, but in this case, it hits too broadly. The correct\ncheck is against 'this_dir' from which the permille is calculated. Only if\nthis value is 0 does the directory truly contribute no changes, and should\nbe skipped from the output.\n\nThis patches fixes this issue, and updates corresponding testcases to\nexpect the new behvaior.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n diff.c                  |    4 ++--\n t/t4046-diff-dirstat.sh |    4 ++--\n 2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex abd9cd5..cfbfa92 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1500,8 +1500,8 @@ static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n \t *    under this directory (sources == 1).\n \t */\n \tif (baselen && sources != 1) {\n-\t\tint permille = this_dir * 1000 / changed;\n-\t\tif (permille) {\n+\t\tif (this_dir) {\n+\t\t\tint permille = this_dir * 1000 / changed;\n \t\t\tint percent = permille / 10;\n \t\t\tif (percent >= dir->percent) {\n \t\t\t\tfprintf(opt->file, \"%s%4d.%01d%% %.*s\\n\", line_prefix,\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex 1690468..694a950 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -337,7 +337,6 @@ test_expect_success 'vanilla --dirstat' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n-# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -346,6 +345,7 @@ cat <<EOF >expect_diff_dirstat\n   10.8% dst/move/changed/\n   10.8% dst/move/rearranged/\n   10.8% dst/move/unchanged/\n+   0.0% rearranged/\n   10.8% src/move/changed/\n   10.8% src/move/rearranged/\n   10.8% src/move/unchanged/\n@@ -379,7 +379,6 @@ test_expect_success '--dirstat=0' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n-# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -391,6 +390,7 @@ cat <<EOF >expect_diff_dirstat\n   10.8% dst/move/unchanged/\n   32.5% dst/move/\n   65.1% dst/\n+   0.0% rearranged/\n   10.8% src/move/changed/\n   10.8% src/move/rearranged/\n   10.8% src/move/unchanged/\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166344","messageId":"1303776102-9085-4-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303776102-9085-1-git-send-email-johan@herland.net","subject":"[PATCH 3/6] Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-26T00:01:39Z","receivedAt":"2011-04-26T00:01:39Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Instead of having multiple interconnected dirstat-related options, teach\nthe --dirstat option itself to accept all behavior modifiers as arguments.\n\n- Preserve the current --dirstat=<limit> (where <limit> is an integer\n  specifying a cut-off percentage)\n- Add --dirstat=cumulative, replacing --cumulative\n- Add --dirstat=files, replacing --dirstat-by-file\n- Also add --dirstat=changes and --dirstat=noncumulative for specifying the\n  current default behavior. These allow the user to reset other --dirstat\n  arguments (e.g. 'cumulative' and 'files') occuring earlier on the command\n  line.\n\nAllow multiple arguments to be separated by commas, e.g.:\n  --dirstat=files,10,cumulative\n\nUpdate the documentation accordingly, and add testcases verifying the\nbehavior of the new syntax.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/diff-options.txt |   45 +++++++++++++-----\n diff.c                         |   80 +++++++++++++++++++++++++++----\n t/t4046-diff-dirstat.sh        |  102 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 205 insertions(+), 22 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 7e4bd42..b6b1448 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -66,19 +66,40 @@ endif::git-format-patch[]\n \tnumber of modified files, as well as number of added and deleted\n \tlines.\n \n---dirstat[=<limit>]::\n-\tOutput the distribution of relative amount of changes (number of lines added or\n-\tremoved) for each sub-directory. Directories with changes below\n-\ta cut-off percent (3% by default) are not shown. The cut-off percent\n-\tcan be set with `--dirstat=<limit>`. Changes in a child directory are not\n-\tcounted for the parent directory, unless `--cumulative` is used.\n+--dirstat[=<arg1,arg2,...>]::\n+\tOutput the distribution of relative amount of changes for each\n+\tsub-directory. The behavior of `--dirstat` can be customized by\n+\tpassing it a comma separated list of arguments. The defaults\n+\tare controlled by the `diff.dirstat` configuration variable (see\n+\tlinkgit:git-config[1]). The following arguments are available:\n +\n-Note that the `--dirstat` option computes the changes while ignoring\n-the amount of pure code movements within a file.  In other words,\n-rearranging lines in a file is not counted as much as other changes.\n-\n---dirstat-by-file[=<limit>]::\n-\tSame as `--dirstat`, but counts changed files instead of lines.\n+--\n+`changes`;;\n+\tCompute the dirstat numbers by counting the lines that have been\n+\tremoved from the source, or added to the destination. This ignores\n+\tthe amount of pure code movements within a file.  In other words,\n+\trearranging lines in a file is not counted as much as other changes.\n+\tThis is the default `--dirstat` behavior.\n+`files`;;\n+\tCompute the dirstat numbers by counting the number of files changed.\n+\tEach changed file counts equally in the dirstat analysis. This is\n+\tthe computationally cheapest `--dirstat` behavior, since it does\n+\tnot look at the file contents at all.\n+`cumulative`;;\n+\tCount changes in a child directory for the parent directory as well.\n+\tNote that when using `cumulative`, the sum of the percentages\n+\treported may exceed 100%. The default (non-cumulative) behavior can\n+\tbe specified with the `noncumulative` argument.\n+<limit>;;\n+\tAn integer argument specifies a cut-off percent (3% by default).\n+\tDirectories contributing less than this percentage of the changes\n+\tare not shown in the output.\n+--\n++\n+Example: The following will count changed files, while ignoring\n+directories with less than 10% of the total amount of changed files,\n+and accumulating child directory counts in the parent directories:\n+`--dirstat=files,10,cumulative`.\n \n --summary::\n \tOutput a condensed summary of extended header information\ndiff --git a/diff.c b/diff.c\nindex cfbfa92..08aaa47 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -3144,6 +3144,72 @@ static int stat_opt(struct diff_options *options, const char **av)\n \treturn argcount;\n }\n \n+static int dirstat_opt(struct diff_options *options, const char **av)\n+{\n+\tconst char *p, *arg = av[0];\n+\tchar *mangled = NULL;\n+\tchar sep = '=';\n+\n+\tif (!strcmp(arg, \"--cumulative\")) /* deprecated */\n+\t\t/* handle '--cumulative' like '--dirstat=cumulative' */\n+\t\tp = \"=cumulative\";\n+\telse if (!strcmp(arg, \"--dirstat-by-file\") ||\n+\t\t !prefixcmp(arg, \"--dirstat-by-file=\")) { /* deprecated */\n+\t\t/* handle '--dirstat-by-file=*' like '--dirstat=files,*' */\n+\t\tmangled = xstrdup(arg + 2);\n+\t\tmemcpy(mangled, \"--dirstat=files\", 15);\n+\t\tif (mangled[15]) {\n+\t\t\tassert(mangled[15] == '=');\n+\t\t\tmangled[15] = ',';\n+\t\t}\n+\t\targ = mangled;\n+\t\tp = mangled + 9;\n+\t}\n+\telse if (!prefixcmp(arg, \"-X\"))\n+\t\tp = arg + 2;\n+\telse if (!prefixcmp(arg, \"--dirstat\"))\n+\t\tp = arg + 9;\n+\telse\n+\t\treturn 0;\n+\n+\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n+\n+\twhile (*p) {\n+\t\tif (*p != sep)\n+\t\t\tdie(\"Missing argument separator ('%c'), at index %lu of '%s'\",\n+\t\t\t    sep, p - arg, arg);\n+\t\tsep = ',';\n+\t\t++p;\n+\t\tif (!prefixcmp(p, \"changes\")) {\n+\t\t\tp += 7;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"files\")) {\n+\t\t\tp += 5;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"noncumulative\")) {\n+\t\t\tp += 13;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"cumulative\")) {\n+\t\t\tp += 10;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n+\t\t}\n+\t\telse if (isdigit(*p)) {\n+\t\t\tchar *end;\n+\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n+\t\t\tassert(end > p);\n+\t\t\tp = end;\n+\t\t}\n+\t\telse\n+\t\t\tdie(\"Unknown --dirstat argument '%s'\", p);\n+\t}\n+\n+\tfree(mangled);\n+\treturn 1;\n+}\n+\n int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n {\n \tconst char *arg = av[0];\n@@ -3163,16 +3229,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \t\toptions->output_format |= DIFF_FORMAT_NUMSTAT;\n \telse if (!strcmp(arg, \"--shortstat\"))\n \t\toptions->output_format |= DIFF_FORMAT_SHORTSTAT;\n-\telse if (opt_arg(arg, 'X', \"dirstat\", &options->dirstat_percent))\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\telse if (!strcmp(arg, \"--cumulative\")) {\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n-\t} else if (opt_arg(arg, 0, \"dirstat-by-file\",\n-\t\t\t   &options->dirstat_percent)) {\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n-\t}\n+\telse if (!prefixcmp(arg, \"-X\") || !prefixcmp(arg, \"--dirstat\") ||\n+\t\t !strcmp(arg, \"--cumulative\"))\n+\t\t/* -X, --dirstat[=<args>], --dirstat-by-file, or --cumulative */\n+\t\treturn dirstat_opt(options, av);\n \telse if (!strcmp(arg, \"--check\"))\n \t\toptions->output_format |= DIFF_FORMAT_CHECKDIFF;\n \telse if (!strcmp(arg, \"--summary\"))\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex 694a950..bd1494c 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -337,6 +337,31 @@ test_expect_success 'vanilla --dirstat' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'explicit defaults: --dirstat=changes,noncumulative,3' '\n+\tgit diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+\n+test_expect_success 'later options override earlier options:' '\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative --dirstat=3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative --dirstat=3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative --dirstat=3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -431,6 +456,15 @@ test_expect_success '--dirstat=0 --cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=0,cumulative' '\n+\tgit diff --dirstat=0,cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -473,6 +507,15 @@ test_expect_success '--dirstat-by-file' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files' '\n+\tgit diff --dirstat=files HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -507,6 +550,15 @@ test_expect_success '--dirstat-by-file=10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files,10' '\n+\tgit diff --dirstat=files,10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -559,4 +611,54 @@ test_expect_success '--dirstat-by-file --cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files,cumulative' '\n+\tgit diff --dirstat=files,cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+  27.2% dst/copy/\n+  27.2% dst/move/\n+  54.5% dst/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  42.8% dst/copy/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  28.5% dst/move/\n+  71.4% dst/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=files,cumulative,10' '\n+\tgit diff --dirstat=files,cumulative,10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166341","messageId":"1303776102-9085-5-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303776102-9085-1-git-send-email-johan@herland.net","subject":"[PATCH 4/6] Add config variable for specifying default --dirstat behavior","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-26T00:01:40Z","receivedAt":"2011-04-26T00:01:40Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"The new diff.dirstat config variable takes the same arguments as\n'--dirstat=<args>', and specifies the default arguments for --dirstat.\nThe config is obviously overridden by --dirstat arguments passed on the\ncommand line.\n\nWhen not specified, the --dirstat defaults are 'changes,noncumulative,3'.\n\nThe parsing of the config variable is done by the new function -\ndirstat_opt_args() - which has been refactored out of dirstat_opt().\n\nThe patch also adds several tests verifying the interaction between the\ndiff.dirstat config variable, and the --dirstat command line option.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/config.txt |   36 +++++++++++++++\n diff.c                   |  110 ++++++++++++++++++++++++++++-----------------\n t/t4046-diff-dirstat.sh  |   72 ++++++++++++++++++++++++++++++\n 3 files changed, 176 insertions(+), 42 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6babbc7..10fa89a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -822,6 +822,42 @@ diff.autorefreshindex::\n \taffects only 'git diff' Porcelain, and not lower level\n \t'diff' commands such as 'git diff-files'.\n \n+diff.dirstat::\n+\tA comma separated list of `--dirstat` arguments specifying the\n+\tdefault behavior of the `--dirstat` option to linkgit:git-diff[1]`\n+\tand friends. The defaults can be overridden on the command line\n+\t(using `--dirstat=<arg1,arg2,...>`). The fallback defaults (when\n+\tnot changed by `diff.dirstat`) are `changes,noncumulative,3`.\n+\tThe following arguments are available:\n++\n+--\n+`changes`;;\n+\tCompute the dirstat numbers by counting the lines that have been\n+\tremoved from the source, or added to the destination. This ignores\n+\tthe amount of pure code movements within a file.  In other words,\n+\trearranging lines in a file is not counted as much as other changes.\n+\tThis is the default `--dirstat` behavior.\n+`files`;;\n+\tCompute the dirstat numbers by counting the number of files changed.\n+\tEach changed file counts equally in the dirstat analysis. This is\n+\tthe computationally cheapest `--dirstat` behavior, since it does\n+\tnot look at the file contents at all.\n+`cumulative`;;\n+\tCount changes in a child directory for the parent directory as well.\n+\tNote that when using `cumulative`, the sum of the percentages\n+\treported may exceed 100%. The default (non-cumulative) behavior can\n+\tbe specified with the `noncumulative` argument.\n+<limit>;;\n+\tAn integer argument specifies a cut-off percent (3% by default).\n+\tDirectories contributing less than this percentage of the changes\n+\tare not shown in the output.\n+--\n++\n+Example: The following will count changed files, while ignoring\n+directories with less than 10% of the total amount of changed files,\n+and accumulating child directory counts in the parent directories:\n+`files,10,cumulative`.\n+\n diff.external::\n \tIf this config variable is set, diff generation is not\n \tperformed using the internal diff machinery, but using the\ndiff --git a/diff.c b/diff.c\nindex 08aaa47..20fe02c 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -45,6 +45,17 @@ static char diff_colors[][COLOR_MAXLEN] = {\n \tGIT_COLOR_NORMAL,\t/* FUNCINFO */\n };\n \n+static void init_default_diff_options()\n+{\n+\tstatic int initialized = 0;\n+\tif (initialized)\n+\t\treturn;\n+\n+\tdefault_diff_options.dirstat_percent = 3;\n+\n+\tinitialized = 1;\n+}\n+\n static int parse_diff_color_slot(const char *var, int ofs)\n {\n \tif (!strcasecmp(var+ofs, \"plain\"))\n@@ -114,6 +125,44 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)\n \treturn git_diff_basic_config(var, value, cb);\n }\n \n+static void dirstat_opt_args(struct diff_options *options, const char *args)\n+{\n+\tconst char *p = args;\n+\twhile (*p) {\n+\t\tif (!prefixcmp(p, \"changes\")) {\n+\t\t\tp += 7;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"files\")) {\n+\t\t\tp += 5;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"noncumulative\")) {\n+\t\t\tp += 13;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"cumulative\")) {\n+\t\t\tp += 10;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n+\t\t}\n+\t\telse if (isdigit(*p)) {\n+\t\t\tchar *end;\n+\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n+\t\t\tassert(end > p);\n+\t\t\tp = end;\n+\t\t}\n+\t\telse\n+\t\t\tdie(\"Unknown --dirstat argument '%s'\", p);\n+\n+\t\tif (*p) { /* more arguments, swallow separator */\n+\t\t\tif (*p != ',')\n+\t\t\t\tdie(\"Missing comma separator, at index %lu of '%s'\",\n+\t\t\t\t    p - args, args);\n+\t\t\t++p;\n+\t\t}\n+\t}\n+}\n+\n int git_diff_basic_config(const char *var, const char *value, void *cb)\n {\n \tif (!strcmp(var, \"diff.renamelimit\")) {\n@@ -145,6 +194,12 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"diff.dirstat\")) {\n+\t\tinit_default_diff_options();\n+\t\tdirstat_opt_args(&default_diff_options, value);\n+\t\treturn 0;\n+\t}\n+\n \tif (!prefixcmp(var, \"submodule.\"))\n \t\treturn parse_submodule_config_option(var, value);\n \n@@ -2879,6 +2934,8 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)\n \n void diff_setup(struct diff_options *options)\n {\n+\tinit_default_diff_options();\n+\n \tmemcpy(options, &default_diff_options, sizeof(*options));\n \n \toptions->file = stdout;\n@@ -2886,7 +2943,6 @@ void diff_setup(struct diff_options *options)\n \toptions->line_termination = '\\n';\n \toptions->break_opt = -1;\n \toptions->rename_limit = -1;\n-\toptions->dirstat_percent = 3;\n \toptions->context = 3;\n \n \toptions->change = diff_change;\n@@ -3148,7 +3204,6 @@ static int dirstat_opt(struct diff_options *options, const char **av)\n {\n \tconst char *p, *arg = av[0];\n \tchar *mangled = NULL;\n-\tchar sep = '=';\n \n \tif (!strcmp(arg, \"--cumulative\")) /* deprecated */\n \t\t/* handle '--cumulative' like '--dirstat=cumulative' */\n@@ -3156,14 +3211,13 @@ static int dirstat_opt(struct diff_options *options, const char **av)\n \telse if (!strcmp(arg, \"--dirstat-by-file\") ||\n \t\t !prefixcmp(arg, \"--dirstat-by-file=\")) { /* deprecated */\n \t\t/* handle '--dirstat-by-file=*' like '--dirstat=files,*' */\n-\t\tmangled = xstrdup(arg + 2);\n-\t\tmemcpy(mangled, \"--dirstat=files\", 15);\n-\t\tif (mangled[15]) {\n-\t\t\tassert(mangled[15] == '=');\n-\t\t\tmangled[15] = ',';\n+\t\tmangled = xstrdup(arg + 11);\n+\t\tmemcpy(mangled, \"=files\", 6);\n+\t\tif (mangled[6]) {\n+\t\t\tassert(mangled[6] == '=');\n+\t\t\tmangled[6] = ',';\n \t\t}\n-\t\targ = mangled;\n-\t\tp = mangled + 9;\n+\t\tp = mangled;\n \t}\n \telse if (!prefixcmp(arg, \"-X\"))\n \t\tp = arg + 2;\n@@ -3172,40 +3226,12 @@ static int dirstat_opt(struct diff_options *options, const char **av)\n \telse\n \t\treturn 0;\n \n-\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\n-\twhile (*p) {\n-\t\tif (*p != sep)\n-\t\t\tdie(\"Missing argument separator ('%c'), at index %lu of '%s'\",\n-\t\t\t    sep, p - arg, arg);\n-\t\tsep = ',';\n-\t\t++p;\n-\t\tif (!prefixcmp(p, \"changes\")) {\n-\t\t\tp += 7;\n-\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n-\t\t}\n-\t\telse if (!prefixcmp(p, \"files\")) {\n-\t\t\tp += 5;\n-\t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n-\t\t}\n-\t\telse if (!prefixcmp(p, \"noncumulative\")) {\n-\t\t\tp += 13;\n-\t\t\tDIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);\n-\t\t}\n-\t\telse if (!prefixcmp(p, \"cumulative\")) {\n-\t\t\tp += 10;\n-\t\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n-\t\t}\n-\t\telse if (isdigit(*p)) {\n-\t\t\tchar *end;\n-\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n-\t\t\tassert(end > p);\n-\t\t\tp = end;\n-\t\t}\n-\t\telse\n-\t\t\tdie(\"Unknown --dirstat argument '%s'\", p);\n-\t}\n+\tif (*p == '=')\n+\t\tdirstat_opt_args(options, ++p);\n+\telse if (*p)\n+\t\treturn 0;\n \n+\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n \tfree(mangled);\n \treturn 1;\n }\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex bd1494c..021c9c4 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -362,6 +362,15 @@ test_expect_success 'later options override earlier options:' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'non-defaults in config overridden by explicit defaults on command line' '\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -404,6 +413,15 @@ test_expect_success '--dirstat=0' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=0' '\n+\tgit -c diff.dirstat=0 diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0 diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0 diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -465,6 +483,24 @@ test_expect_success '--dirstat=0,cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=0,cumulative' '\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=0 & --dirstat=cumulative' '\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -516,6 +552,15 @@ test_expect_success '--dirstat=files' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=files' '\n+\tgit -c diff.dirstat=files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -559,6 +604,15 @@ test_expect_success '--dirstat=files,10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=10,files' '\n+\tgit -c diff.dirstat=10,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=10,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=10,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -620,6 +674,15 @@ test_expect_success '--dirstat=files,cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=cumulative,files' '\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -661,4 +724,13 @@ test_expect_success '--dirstat=files,cumulative,10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=10,cumulative,files' '\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166342","messageId":"1303776102-9085-6-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303776102-9085-1-git-send-email-johan@herland.net","subject":"[PATCH 5/6] Use floating point for --dirstat percentages","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-26T00:01:41Z","receivedAt":"2011-04-26T00:01:41Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Allow specifying --dirstat cut-off percentage as a floating point number.\n\nWhen printing the dirstat output, floating point numbers are presented in\nrounded form (as opposed to truncated). Therefore, this patch includes a\nsignificant churn in the expected output of the dirstat selftests.\n\nA selftest verifying floating-point percentage input has been added.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n\nRemaining questions:\n- Locale issues with strod(), e.g. decimal separator is a comma in certain\n  locales.\n- Is it really worth the extensive churn in the dirstat output?\n\n\nHave fun! :)\n\n...Johan\n\n diff.c                  |   22 ++--\n diff.h                  |    2 +-\n t/t4046-diff-dirstat.sh |  327 ++++++++++++++++++++++++++---------------------\n 3 files changed, 195 insertions(+), 156 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 20fe02c..4da3b68 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -51,7 +51,7 @@ static void init_default_diff_options()\n \tif (initialized)\n \t\treturn;\n \n-\tdefault_diff_options.dirstat_percent = 3;\n+\tdefault_diff_options.dirstat_percent = 3.0;\n \n \tinitialized = 1;\n }\n@@ -146,10 +146,12 @@ static void dirstat_opt_args(struct diff_options *options, const char *args)\n \t\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n \t\t}\n \t\telse if (isdigit(*p)) {\n-\t\t\tchar *end;\n-\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n-\t\t\tassert(end > p);\n-\t\t\tp = end;\n+\t\t\tchar *end = (char *) p;\n+\t\t\toptions->dirstat_percent = strtod(p, &end);\n+\t\t\tif (end > p && (*end == ',' || !*end))\n+\t\t\t\tp = end;\n+\t\t\telse\n+\t\t\t\tdie(\"Failed to parse percent threshold '%s'\", p);\n \t\t}\n \t\telse\n \t\t\tdie(\"Unknown --dirstat argument '%s'\", p);\n@@ -1508,7 +1510,8 @@ struct dirstat_file {\n \n struct dirstat_dir {\n \tstruct dirstat_file *files;\n-\tint alloc, nr, percent, cumulative;\n+\tdouble percent;\n+\tint alloc, nr, cumulative;\n };\n \n static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n@@ -1556,11 +1559,10 @@ static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n \t */\n \tif (baselen && sources != 1) {\n \t\tif (this_dir) {\n-\t\t\tint permille = this_dir * 1000 / changed;\n-\t\t\tint percent = permille / 10;\n+\t\t\tdouble percent = this_dir * 100.0 / changed;\n \t\t\tif (percent >= dir->percent) {\n-\t\t\t\tfprintf(opt->file, \"%s%4d.%01d%% %.*s\\n\", line_prefix,\n-\t\t\t\t\tpercent, permille % 10, baselen, base);\n+\t\t\t\tfprintf(opt->file, \"%s%6.1f%% %.*s\\n\", line_prefix,\n+\t\t\t\t\tpercent, baselen, base);\n \t\t\t\tif (!dir->cumulative)\n \t\t\t\t\treturn 0;\n \t\t\t}\ndiff --git a/diff.h b/diff.h\nindex 0083d92..781c620 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -111,13 +111,13 @@ struct diff_options {\n \tint rename_score;\n \tint rename_limit;\n \tint warn_on_too_large_rename;\n-\tint dirstat_percent;\n \tint setup;\n \tint abbrev;\n \tconst char *prefix;\n \tint prefix_length;\n \tconst char *stat_sep;\n \tlong xdl_opts;\n+\tdouble dirstat_percent;\n \n \tint stat_width;\n \tint stat_name_width;\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex 021c9c4..da4484c 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -301,31 +301,31 @@ test_expect_success 'sanity check setup (--stat)' '\n \n # changed/text and rearranged/text falls below default 3% threshold\n cat <<EOF >expect_diff_dirstat\n-  10.8% dst/copy/changed/\n-  10.8% dst/copy/rearranged/\n-  10.8% dst/copy/unchanged/\n-  10.8% dst/move/changed/\n-  10.8% dst/move/rearranged/\n-  10.8% dst/move/unchanged/\n-  10.8% src/move/changed/\n-  10.8% src/move/rearranged/\n-  10.8% src/move/unchanged/\n+  10.9% dst/copy/changed/\n+  10.9% dst/copy/rearranged/\n+  10.9% dst/copy/unchanged/\n+  10.9% dst/move/changed/\n+  10.9% dst/move/rearranged/\n+  10.9% dst/move/unchanged/\n+  10.9% src/move/changed/\n+  10.9% src/move/rearranged/\n+  10.9% src/move/unchanged/\n EOF\n \n # rearranged/text falls below default 3% threshold\n cat <<EOF >expect_diff_dirstat_M\n-   5.8% changed/\n+   5.9% changed/\n   29.3% dst/copy/changed/\n   29.3% dst/copy/rearranged/\n   29.3% dst/copy/unchanged/\n-   5.8% dst/move/changed/\n+   5.9% dst/move/changed/\n EOF\n \n # rearranged/text falls below default 3% threshold\n cat <<EOF >expect_diff_dirstat_CC\n-  32.6% changed/\n-  32.6% dst/copy/changed/\n-  32.6% dst/move/changed/\n+  32.7% changed/\n+  32.7% dst/copy/changed/\n+  32.7% dst/move/changed/\n EOF\n \n test_expect_success 'vanilla --dirstat' '\n@@ -372,36 +372,36 @@ test_expect_success 'non-defaults in config overridden by explicit defaults on c\n '\n \n cat <<EOF >expect_diff_dirstat\n-   2.1% changed/\n-  10.8% dst/copy/changed/\n-  10.8% dst/copy/rearranged/\n-  10.8% dst/copy/unchanged/\n-  10.8% dst/move/changed/\n-  10.8% dst/move/rearranged/\n-  10.8% dst/move/unchanged/\n+   2.2% changed/\n+  10.9% dst/copy/changed/\n+  10.9% dst/copy/rearranged/\n+  10.9% dst/copy/unchanged/\n+  10.9% dst/move/changed/\n+  10.9% dst/move/rearranged/\n+  10.9% dst/move/unchanged/\n    0.0% rearranged/\n-  10.8% src/move/changed/\n-  10.8% src/move/rearranged/\n-  10.8% src/move/unchanged/\n+  10.9% src/move/changed/\n+  10.9% src/move/rearranged/\n+  10.9% src/move/unchanged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_M\n-   5.8% changed/\n+   5.9% changed/\n   29.3% dst/copy/changed/\n   29.3% dst/copy/rearranged/\n   29.3% dst/copy/unchanged/\n-   5.8% dst/move/changed/\n+   5.9% dst/move/changed/\n    0.1% dst/move/rearranged/\n    0.1% rearranged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_CC\n-  32.6% changed/\n-  32.6% dst/copy/changed/\n-   0.6% dst/copy/rearranged/\n-  32.6% dst/move/changed/\n-   0.6% dst/move/rearranged/\n-   0.6% rearranged/\n+  32.7% changed/\n+  32.7% dst/copy/changed/\n+   0.7% dst/copy/rearranged/\n+  32.7% dst/move/changed/\n+   0.7% dst/move/rearranged/\n+   0.7% rearranged/\n EOF\n \n test_expect_success '--dirstat=0' '\n@@ -423,46 +423,46 @@ test_expect_success 'diff.dirstat=0' '\n '\n \n cat <<EOF >expect_diff_dirstat\n-   2.1% changed/\n-  10.8% dst/copy/changed/\n-  10.8% dst/copy/rearranged/\n-  10.8% dst/copy/unchanged/\n-  32.5% dst/copy/\n-  10.8% dst/move/changed/\n-  10.8% dst/move/rearranged/\n-  10.8% dst/move/unchanged/\n-  32.5% dst/move/\n-  65.1% dst/\n+   2.2% changed/\n+  10.9% dst/copy/changed/\n+  10.9% dst/copy/rearranged/\n+  10.9% dst/copy/unchanged/\n+  32.6% dst/copy/\n+  10.9% dst/move/changed/\n+  10.9% dst/move/rearranged/\n+  10.9% dst/move/unchanged/\n+  32.6% dst/move/\n+  65.2% dst/\n    0.0% rearranged/\n-  10.8% src/move/changed/\n-  10.8% src/move/rearranged/\n-  10.8% src/move/unchanged/\n-  32.5% src/move/\n+  10.9% src/move/changed/\n+  10.9% src/move/rearranged/\n+  10.9% src/move/unchanged/\n+  32.6% src/move/\n EOF\n \n cat <<EOF >expect_diff_dirstat_M\n-   5.8% changed/\n+   5.9% changed/\n   29.3% dst/copy/changed/\n   29.3% dst/copy/rearranged/\n   29.3% dst/copy/unchanged/\n   88.0% dst/copy/\n-   5.8% dst/move/changed/\n+   5.9% dst/move/changed/\n    0.1% dst/move/rearranged/\n-   5.9% dst/move/\n+   6.0% dst/move/\n   94.0% dst/\n    0.1% rearranged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_CC\n-  32.6% changed/\n-  32.6% dst/copy/changed/\n-   0.6% dst/copy/rearranged/\n+  32.7% changed/\n+  32.7% dst/copy/changed/\n+   0.7% dst/copy/rearranged/\n   33.3% dst/copy/\n-  32.6% dst/move/changed/\n-   0.6% dst/move/rearranged/\n+  32.7% dst/move/changed/\n+   0.7% dst/move/rearranged/\n   33.3% dst/move/\n-  66.6% dst/\n-   0.6% rearranged/\n+  66.7% dst/\n+   0.7% rearranged/\n EOF\n \n test_expect_success '--dirstat=0 --cumulative' '\n@@ -502,36 +502,36 @@ test_expect_success 'diff.dirstat=0 & --dirstat=cumulative' '\n '\n \n cat <<EOF >expect_diff_dirstat\n-   9.0% changed/\n-   9.0% dst/copy/changed/\n-   9.0% dst/copy/rearranged/\n-   9.0% dst/copy/unchanged/\n-   9.0% dst/move/changed/\n-   9.0% dst/move/rearranged/\n-   9.0% dst/move/unchanged/\n-   9.0% rearranged/\n-   9.0% src/move/changed/\n-   9.0% src/move/rearranged/\n-   9.0% src/move/unchanged/\n+   9.1% changed/\n+   9.1% dst/copy/changed/\n+   9.1% dst/copy/rearranged/\n+   9.1% dst/copy/unchanged/\n+   9.1% dst/move/changed/\n+   9.1% dst/move/rearranged/\n+   9.1% dst/move/unchanged/\n+   9.1% rearranged/\n+   9.1% src/move/changed/\n+   9.1% src/move/rearranged/\n+   9.1% src/move/unchanged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_M\n-  14.2% changed/\n-  14.2% dst/copy/changed/\n-  14.2% dst/copy/rearranged/\n-  14.2% dst/copy/unchanged/\n-  14.2% dst/move/changed/\n-  14.2% dst/move/rearranged/\n-  14.2% rearranged/\n+  14.3% changed/\n+  14.3% dst/copy/changed/\n+  14.3% dst/copy/rearranged/\n+  14.3% dst/copy/unchanged/\n+  14.3% dst/move/changed/\n+  14.3% dst/move/rearranged/\n+  14.3% rearranged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_CC\n-  16.6% changed/\n-  16.6% dst/copy/changed/\n-  16.6% dst/copy/rearranged/\n-  16.6% dst/move/changed/\n-  16.6% dst/move/rearranged/\n-  16.6% rearranged/\n+  16.7% changed/\n+  16.7% dst/copy/changed/\n+  16.7% dst/copy/rearranged/\n+  16.7% dst/move/changed/\n+  16.7% dst/move/rearranged/\n+  16.7% rearranged/\n EOF\n \n test_expect_success '--dirstat-by-file' '\n@@ -562,28 +562,28 @@ test_expect_success 'diff.dirstat=files' '\n '\n \n cat <<EOF >expect_diff_dirstat\n-  27.2% dst/copy/\n-  27.2% dst/move/\n-  27.2% src/move/\n+  27.3% dst/copy/\n+  27.3% dst/move/\n+  27.3% src/move/\n EOF\n \n cat <<EOF >expect_diff_dirstat_M\n-  14.2% changed/\n-  14.2% dst/copy/changed/\n-  14.2% dst/copy/rearranged/\n-  14.2% dst/copy/unchanged/\n-  14.2% dst/move/changed/\n-  14.2% dst/move/rearranged/\n-  14.2% rearranged/\n+  14.3% changed/\n+  14.3% dst/copy/changed/\n+  14.3% dst/copy/rearranged/\n+  14.3% dst/copy/unchanged/\n+  14.3% dst/move/changed/\n+  14.3% dst/move/rearranged/\n+  14.3% rearranged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_CC\n-  16.6% changed/\n-  16.6% dst/copy/changed/\n-  16.6% dst/copy/rearranged/\n-  16.6% dst/move/changed/\n-  16.6% dst/move/rearranged/\n-  16.6% rearranged/\n+  16.7% changed/\n+  16.7% dst/copy/changed/\n+  16.7% dst/copy/rearranged/\n+  16.7% dst/move/changed/\n+  16.7% dst/move/rearranged/\n+  16.7% rearranged/\n EOF\n \n test_expect_success '--dirstat-by-file=10' '\n@@ -614,46 +614,46 @@ test_expect_success 'diff.dirstat=10,files' '\n '\n \n cat <<EOF >expect_diff_dirstat\n-   9.0% changed/\n-   9.0% dst/copy/changed/\n-   9.0% dst/copy/rearranged/\n-   9.0% dst/copy/unchanged/\n-  27.2% dst/copy/\n-   9.0% dst/move/changed/\n-   9.0% dst/move/rearranged/\n-   9.0% dst/move/unchanged/\n-  27.2% dst/move/\n+   9.1% changed/\n+   9.1% dst/copy/changed/\n+   9.1% dst/copy/rearranged/\n+   9.1% dst/copy/unchanged/\n+  27.3% dst/copy/\n+   9.1% dst/move/changed/\n+   9.1% dst/move/rearranged/\n+   9.1% dst/move/unchanged/\n+  27.3% dst/move/\n   54.5% dst/\n-   9.0% rearranged/\n-   9.0% src/move/changed/\n-   9.0% src/move/rearranged/\n-   9.0% src/move/unchanged/\n-  27.2% src/move/\n+   9.1% rearranged/\n+   9.1% src/move/changed/\n+   9.1% src/move/rearranged/\n+   9.1% src/move/unchanged/\n+  27.3% src/move/\n EOF\n \n cat <<EOF >expect_diff_dirstat_M\n-  14.2% changed/\n-  14.2% dst/copy/changed/\n-  14.2% dst/copy/rearranged/\n-  14.2% dst/copy/unchanged/\n-  42.8% dst/copy/\n-  14.2% dst/move/changed/\n-  14.2% dst/move/rearranged/\n-  28.5% dst/move/\n+  14.3% changed/\n+  14.3% dst/copy/changed/\n+  14.3% dst/copy/rearranged/\n+  14.3% dst/copy/unchanged/\n+  42.9% dst/copy/\n+  14.3% dst/move/changed/\n+  14.3% dst/move/rearranged/\n+  28.6% dst/move/\n   71.4% dst/\n-  14.2% rearranged/\n+  14.3% rearranged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_CC\n-  16.6% changed/\n-  16.6% dst/copy/changed/\n-  16.6% dst/copy/rearranged/\n+  16.7% changed/\n+  16.7% dst/copy/changed/\n+  16.7% dst/copy/rearranged/\n   33.3% dst/copy/\n-  16.6% dst/move/changed/\n-  16.6% dst/move/rearranged/\n+  16.7% dst/move/changed/\n+  16.7% dst/move/rearranged/\n   33.3% dst/move/\n-  66.6% dst/\n-  16.6% rearranged/\n+  66.7% dst/\n+  16.7% rearranged/\n EOF\n \n test_expect_success '--dirstat-by-file --cumulative' '\n@@ -684,35 +684,35 @@ test_expect_success 'diff.dirstat=cumulative,files' '\n '\n \n cat <<EOF >expect_diff_dirstat\n-  27.2% dst/copy/\n-  27.2% dst/move/\n+  27.3% dst/copy/\n+  27.3% dst/move/\n   54.5% dst/\n-  27.2% src/move/\n+  27.3% src/move/\n EOF\n \n cat <<EOF >expect_diff_dirstat_M\n-  14.2% changed/\n-  14.2% dst/copy/changed/\n-  14.2% dst/copy/rearranged/\n-  14.2% dst/copy/unchanged/\n-  42.8% dst/copy/\n-  14.2% dst/move/changed/\n-  14.2% dst/move/rearranged/\n-  28.5% dst/move/\n+  14.3% changed/\n+  14.3% dst/copy/changed/\n+  14.3% dst/copy/rearranged/\n+  14.3% dst/copy/unchanged/\n+  42.9% dst/copy/\n+  14.3% dst/move/changed/\n+  14.3% dst/move/rearranged/\n+  28.6% dst/move/\n   71.4% dst/\n-  14.2% rearranged/\n+  14.3% rearranged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_CC\n-  16.6% changed/\n-  16.6% dst/copy/changed/\n-  16.6% dst/copy/rearranged/\n+  16.7% changed/\n+  16.7% dst/copy/changed/\n+  16.7% dst/copy/rearranged/\n   33.3% dst/copy/\n-  16.6% dst/move/changed/\n-  16.6% dst/move/rearranged/\n+  16.7% dst/move/changed/\n+  16.7% dst/move/rearranged/\n   33.3% dst/move/\n-  66.6% dst/\n-  16.6% rearranged/\n+  66.7% dst/\n+  16.7% rearranged/\n EOF\n \n test_expect_success '--dirstat=files,cumulative,10' '\n@@ -733,4 +733,41 @@ test_expect_success 'diff.dirstat=10,cumulative,files' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+cat <<EOF >expect_diff_dirstat\n+  27.3% dst/copy/\n+  27.3% dst/move/\n+  54.5% dst/\n+  27.3% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  42.9% dst/copy/\n+  28.6% dst/move/\n+  71.4% dst/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  33.3% dst/copy/\n+  33.3% dst/move/\n+  66.7% dst/\n+EOF\n+\n+test_expect_success '--dirstat=files,cumulative,16.7' '\n+\tgit diff --dirstat=files,cumulative,16.7 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,16.7 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,16.7 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=16.7,cumulative,files' '\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166345","messageId":"1303776102-9085-7-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303776102-9085-1-git-send-email-johan@herland.net","subject":"[PATCH 6/6] New --dirstat=lines mode, doing dirstat analysis based on diffstat","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-26T00:01:42Z","receivedAt":"2011-04-26T00:01:42Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"This patch adds an alternative implementation of show_dirstat(), called\nshow_dirstat_by_line(), which uses the more expensive diffstat analysis\n(as opposed to show_dirstat()'s own (relatively inexpensive) analysis)\nto derive the numbers from which the --dirstat output is computed.\n\nThe alternative implementation is controlled by the new \"lines\" argument\nto the --dirstat option (or the diff.dirstat config variable).\n\nIn linux-2.6.git, running the three different --dirstat modes:\n\n  time git diff v2.6.20..v2.6.30 --dirstat=changes > /dev/null\nvs.\n  time git diff v2.6.20..v2.6.30 --dirstat=lines > /dev/null\nvs.\n  time git diff v2.6.20..v2.6.30 --dirstat=files > /dev/null\n\nyields the following average runtimes on my machine:\n\n- \"changes\" (default): ~6.0 s\n- \"lines\":             ~9.6 s\n- \"files\":             ~0.1 s\n\nSo, as expected, there's a considerable performance hit (~60%) by going\nthrough the full diffstat analysis as compared to the default \"changes\"\nanalysis (obviously, \"files\" is much faster than both). As such, the\n\"lines\" mode is probably only useful if you really need the --dirstat\nnumbers to be consistent with the numbers returned from the other\n--*stat options.\n\nThe patch also includes documentation and tests for the new dirstat mode.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/config.txt       |    7 +++\n Documentation/diff-options.txt |    7 +++\n diff.c                         |   60 +++++++++++++++++++++++-\n diff.h                         |    1 +\n t/t4046-diff-dirstat.sh        |  100 ++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 173 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 10fa89a..47b2423 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -837,6 +837,13 @@ diff.dirstat::\n \tthe amount of pure code movements within a file.  In other words,\n \trearranging lines in a file is not counted as much as other changes.\n \tThis is the default `--dirstat` behavior.\n+`lines`;;\n+\tCompute the dirstat numbers by doing the regular line-based diff\n+\tanalysis, and summing the removed/added line counts. This is a more\n+\texpensive `--dirstat` behavior than the `changes` behavior, but it\n+\tdoes count rearranged lines within a file as much as other changes.\n+\tThe resulting output is consistent with what you get from the other\n+\t`--*stat` options.\n `files`;;\n \tCompute the dirstat numbers by counting the number of files changed.\n \tEach changed file counts equally in the dirstat analysis. This is\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex b6b1448..0b7417b 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -80,6 +80,13 @@ endif::git-format-patch[]\n \tthe amount of pure code movements within a file.  In other words,\n \trearranging lines in a file is not counted as much as other changes.\n \tThis is the default `--dirstat` behavior.\n+`lines`;;\n+\tCompute the dirstat numbers by doing the regular line-based diff\n+\tanalysis, and summing the removed/added line counts. This is a more\n+\texpensive `--dirstat` behavior than the `changes` behavior, but it\n+\tdoes count rearranged lines within a file as much as other changes.\n+\tThe resulting output is consistent with what you get from the other\n+\t`--*stat` options.\n `files`;;\n \tCompute the dirstat numbers by counting the number of files changed.\n \tEach changed file counts equally in the dirstat analysis. This is\ndiff --git a/diff.c b/diff.c\nindex 4da3b68..c00984f 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -131,10 +131,17 @@ static void dirstat_opt_args(struct diff_options *options, const char *args)\n \twhile (*p) {\n \t\tif (!prefixcmp(p, \"changes\")) {\n \t\t\tp += 7;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_LINE);\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"lines\")) {\n+\t\t\tp += 5;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n \t\t}\n \t\telse if (!prefixcmp(p, \"files\")) {\n \t\t\tp += 5;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n \t\t}\n \t\telse if (!prefixcmp(p, \"noncumulative\")) {\n@@ -1677,6 +1684,48 @@ found_damage:\n \tgather_dirstat(options, &dir, changed, \"\", 0);\n }\n \n+static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *options)\n+{\n+\tint i;\n+\tunsigned long changed;\n+\tstruct dirstat_dir dir;\n+\n+\tif (data->nr == 0)\n+\t\treturn;\n+\n+\tdir.files = NULL;\n+\tdir.alloc = 0;\n+\tdir.nr = 0;\n+\tdir.percent = options->dirstat_percent;\n+\tdir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);\n+\n+\tchanged = 0;\n+\tfor (i = 0; i < data->nr; i++) {\n+\t\tstruct diffstat_file *file = data->files[i];\n+\t\tunsigned long damage = file->added + file->deleted;\n+\t\tif (damage && file->is_binary)\n+\t\t\t/*\n+\t\t\t * binary files counts bytes, not lines. Must find some\n+\t\t\t * way to normalize binary bytes vs. textual lines.\n+\t\t\t * The following heuristic is cheap, but beyond ugly...\n+\t\t\t */\n+\t\t\tdamage = damage < 52 ? 1 : damage / 52;\n+\t\tALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);\n+\t\tdir.files[dir.nr].name = file->name;\n+\t\tdir.files[dir.nr].changed = damage;\n+\t\tchanged += damage;\n+\t\tdir.nr++;\n+\t}\n+\n+\t/* This can happen even with many files, if everything was renames */\n+\tif (!changed)\n+\t\treturn;\n+\n+\t/* Show all directories with more than x% of the changes */\n+\tqsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);\n+\tgather_dirstat(options, &dir, changed, \"\", 0);\n+}\n+\n static void free_diffstat_info(struct diffstat_t *diffstat)\n {\n \tint i;\n@@ -4081,6 +4130,7 @@ void diff_flush(struct diff_options *options)\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tint i, output_format = options->output_format;\n \tint separator = 0;\n+\tint dirstat_by_line = 0;\n \n \t/*\n \t * Order: raw, stat, summary, patch\n@@ -4101,7 +4151,11 @@ void diff_flush(struct diff_options *options)\n \t\tseparator++;\n \t}\n \n-\tif (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {\n+\tif (output_format & DIFF_FORMAT_DIRSTAT && DIFF_OPT_TST(options, DIRSTAT_BY_LINE))\n+\t\tdirstat_by_line = 1;\n+\n+\tif (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT) ||\n+\t    dirstat_by_line) {\n \t\tstruct diffstat_t diffstat;\n \n \t\tmemset(&diffstat, 0, sizeof(struct diffstat_t));\n@@ -4116,10 +4170,12 @@ void diff_flush(struct diff_options *options)\n \t\t\tshow_stats(&diffstat, options);\n \t\tif (output_format & DIFF_FORMAT_SHORTSTAT)\n \t\t\tshow_shortstats(&diffstat, options);\n+\t\tif (output_format & DIFF_FORMAT_DIRSTAT)\n+\t\t\tshow_dirstat_by_line(&diffstat, options);\n \t\tfree_diffstat_info(&diffstat);\n \t\tseparator++;\n \t}\n-\tif (output_format & DIFF_FORMAT_DIRSTAT)\n+\tif ((output_format & DIFF_FORMAT_DIRSTAT) && !dirstat_by_line)\n \t\tshow_dirstat(options);\n \n \tif (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {\ndiff --git a/diff.h b/diff.h\nindex 781c620..5f12049 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -78,6 +78,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)\n #define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)\n #define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)\n #define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)\n+#define DIFF_OPT_DIRSTAT_BY_LINE     (1 << 28)\n \n #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)\n #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex da4484c..ef9326a 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -770,4 +770,104 @@ test_expect_success 'diff.dirstat=16.7,cumulative,files' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+cat <<EOF >expect_diff_dirstat\n+  10.6% dst/copy/changed/\n+  10.6% dst/copy/rearranged/\n+  10.6% dst/copy/unchanged/\n+  10.6% dst/move/changed/\n+  10.6% dst/move/rearranged/\n+  10.6% dst/move/unchanged/\n+  10.6% src/move/changed/\n+  10.6% src/move/rearranged/\n+  10.6% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.3% changed/\n+  26.3% dst/copy/changed/\n+  26.3% dst/copy/rearranged/\n+  26.3% dst/copy/unchanged/\n+   5.3% dst/move/changed/\n+   5.3% dst/move/rearranged/\n+   5.3% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.7% changed/\n+  16.7% dst/copy/changed/\n+  16.7% dst/copy/rearranged/\n+  16.7% dst/move/changed/\n+  16.7% dst/move/rearranged/\n+  16.7% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=lines' '\n+\tgit diff --dirstat=lines HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=lines -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=lines -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=lines' '\n+\tgit -c diff.dirstat=lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.6% dst/copy/changed/\n+  10.6% dst/copy/rearranged/\n+  10.6% dst/copy/unchanged/\n+  10.6% dst/move/changed/\n+  10.6% dst/move/rearranged/\n+  10.6% dst/move/unchanged/\n+   2.1% rearranged/\n+  10.6% src/move/changed/\n+  10.6% src/move/rearranged/\n+  10.6% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.3% changed/\n+  26.3% dst/copy/changed/\n+  26.3% dst/copy/rearranged/\n+  26.3% dst/copy/unchanged/\n+   5.3% dst/move/changed/\n+   5.3% dst/move/rearranged/\n+   5.3% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.7% changed/\n+  16.7% dst/copy/changed/\n+  16.7% dst/copy/rearranged/\n+  16.7% dst/move/changed/\n+  16.7% dst/move/rearranged/\n+  16.7% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=lines,0' '\n+\tgit diff --dirstat=lines,0 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=lines,0 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=lines,0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=0,lines' '\n+\tgit -c diff.dirstat=0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166346","messageId":"BANLkTi=sP3zu45R_s_3w-oMuMh1fGGv=vQ@mail.gmail.com","threadId":"27015","inReplyTo":"1303776102-9085-1-git-send-email-johan@herland.net","subject":"Re: [PATCH 0/6] --dirstat fixes, part 2","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2011-04-26T00:15:41Z","receivedAt":"2011-04-26T00:15:41Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Mon, Apr 25, 2011 at 5:01 PM, Johan Herland <johan@herland.net> wrote:\n>\n> I finally found the time to re-roll the remaining dirstat fixes,\n> incorporating feedback from Linus and Junio in the surrounding thread.\n\nAfter a _very_ superficial walk-through of the patches, I have no real\nissues. Looks ok by me,\n\n                 Linus\n"},{"id":"166390","messageId":"7vy62xezqy.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"1303776102-9085-4-git-send-email-johan@herland.net","subject":"Re: [PATCH 3/6] Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-26T16:36:21Z","receivedAt":"2011-04-26T16:36:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> Instead of having multiple interconnected dirstat-related options, teach\n> the --dirstat option itself to accept all behavior modifiers as arguments.\n>\n> - Preserve the current --dirstat=<limit> (where <limit> is an integer\n>   specifying a cut-off percentage)\n> - Add --dirstat=cumulative, replacing --cumulative\n> - Add --dirstat=files, replacing --dirstat-by-file\n> - Also add --dirstat=changes and --dirstat=noncumulative for specifying the\n>   current default behavior. These allow the user to reset other --dirstat\n>   arguments (e.g. 'cumulative' and 'files') occuring earlier on the command\n>   line.\n>\n> Allow multiple arguments to be separated by commas, e.g.:\n>   --dirstat=files,10,cumulative\n>\n> Update the documentation accordingly, and add testcases verifying the\n> behavior of the new syntax.\n\nThe above description is unclear if the version of git will error out when\ngiven --cumulative or --dirstat-by-file.  I can sort of guess by lack of\nremoved lines from the documentation, but please do not make readers guess.\n\nAlso a miniscule style nitpick: could you indent your bulletted-list just\na bit (one space indent is just fine)?\n\n> diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\n> index 7e4bd42..b6b1448 100644\n> --- a/Documentation/diff-options.txt\n> +++ b/Documentation/diff-options.txt\n> @@ -66,19 +66,40 @@ endif::git-format-patch[]\n>  \tnumber of modified files, as well as number of added and deleted\n>  \tlines.\n>  \n> ---dirstat[=<limit>]::\n> -\tOutput the distribution of relative amount of changes (number of lines added or\n> -\tremoved) for each sub-directory. Directories with changes below\n> -\ta cut-off percent (3% by default) are not shown. The cut-off percent\n> -\tcan be set with `--dirstat=<limit>`. Changes in a child directory are not\n> -\tcounted for the parent directory, unless `--cumulative` is used.\n> +--dirstat[=<arg1,arg2,...>]::\n> +\tOutput the distribution of relative amount of changes for each\n> +\tsub-directory. The behavior of `--dirstat` can be customized by\n> +\tpassing it a comma separated list of arguments. The defaults\n> +\tare controlled by the `diff.dirstat` configuration variable (see\n> +\tlinkgit:git-config[1]). The following arguments are available:\n\nThese \"arguments\" feel more like \"options\" (or \"parameters\"), no?  Your\ncode in diff.c also calls it \"opt\".  The second line of the proposed log\nmessage has the same issue.\n\n> +--\n> +`changes`;;\n> +\tCompute the dirstat numbers by counting the lines that have been\n> +\tremoved from the source, or added to the destination. This ignores\n> +\tthe amount of pure code movements within a file.  In other words,\n> +\trearranging lines in a file is not counted as much as other changes.\n> +\tThis is the default `--dirstat` behavior.\n\n\"default behavior when no option is given\"?\n\n> +`files`;;\n> +\tCompute the dirstat numbers by counting the number of files changed.\n> +\tEach changed file counts equally in the dirstat analysis. This is\n> +\tthe computationally cheapest `--dirstat` behavior, since it does\n> +\tnot look at the file contents at all.\n\ns/not look/not have to look/?\n\n> +`cumulative`;;\n> +\tCount changes in a child directory for the parent directory as well.\n> +\tNote that when using `cumulative`, the sum of the percentages\n> +\treported may exceed 100%. The default (non-cumulative) behavior can\n> +\tbe specified with the `noncumulative` argument.\n\nSo the later one wins?  I.e. --dirstat=cumulative,noncumulative from the\ncommand line (which seems silly), or more importantly with\n\n    [alias]\n    \tdstat = diff --dirstat=cumulative\n\nand you can say \"git dstat --dirstat=noncumulative A..B\"?\n\n> diff --git a/diff.c b/diff.c\n> index cfbfa92..08aaa47 100644\n> --- a/diff.c\n> +++ b/diff.c\n> @@ -3144,6 +3144,72 @@ static int stat_opt(struct diff_options *options, const char **av)\n>  \treturn argcount;\n>  }\n>  \n\n/*\n * Document what the return value from this function means here.\n */\n> +static int dirstat_opt(struct diff_options *options, const char **av)\n\nDo you have to pass \"const char **av\", or just \"const char *arg\"?\n\n> +{\n> +\tconst char *p, *arg = av[0];\n> +\tchar *mangled = NULL;\n> +\tchar sep = '=';\n> +\n> +\tif (!strcmp(arg, \"--cumulative\")) /* deprecated */\n> +\t\t/* handle '--cumulative' like '--dirstat=cumulative' */\n> +\t\tp = \"=cumulative\";\n> +\telse if (!strcmp(arg, \"--dirstat-by-file\") ||\n> +\t\t !prefixcmp(arg, \"--dirstat-by-file=\")) { /* deprecated */\n> +\t\t/* handle '--dirstat-by-file=*' like '--dirstat=files,*' */\n> +\t\tmangled = xstrdup(arg + 2);\n> +\t\tmemcpy(mangled, \"--dirstat=files\", 15);\n> +\t\tif (mangled[15]) {\n> +\t\t\tassert(mangled[15] == '=');\n> +\t\t\tmangled[15] = ',';\n> +\t\t}\n> +\t\targ = mangled;\n> +\t\tp = mangled + 9;\n\nI understand you wanted to reuse the while() loop below, but I do not\nthink it is worth it.  Isn't it easier to read if you handled the above\ncases in their if/else body and return?\n\n\tif (--cumulative) {\n\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n        \tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n                return 1;\n\t}\n        if (--dirstat-by-file) {\n\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n\t\treturn 1;\n\t}\n\t...\n\nEven better, probably they can be left to diff_opt_parse() without calling\nthis function, as you are deprecating them and do not have to allow them\nto take the opt1,opt2,... form of parameter.\n\n> +\t}\n> +\telse if (!prefixcmp(arg, \"-X\"))\n> +\t\tp = arg + 2;\n> +\telse if (!prefixcmp(arg, \"--dirstat\"))\n> +\t\tp = arg + 9;\n> +\telse\n> +\t\treturn 0;\n> +\n> +\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n> +\n> +\twhile (*p) {\n> +\t\tif (*p != sep)\n\nWhat happens to \"diff -X3 A..B\"?\n\n> +\t\t\tdie(\"Missing argument separator ('%c'), at index %lu of '%s'\",\n> +\t\t\t    sep, p - arg, arg);\n\nDon't you need to cast (p-arg) for %lu from ptrdiff type here?  It\nprobably is more common to say s/index/char/;\n\n> +\t\tsep = ',';\n> +\t\t++p;\n\nWe tend to write postincrement when there is no strong reason to do\notherwise.\n\n> +\t\tif (!prefixcmp(p, \"changes\")) {\n> +\t\t\tp += 7;\n> +\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n> +\t\t}\n> +\t\telse if (!prefixcmp(p, \"files\")) {\n> +\t\t\tp += 5;\n> +\t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n> +\t\t}\n> +\t\telse if (!prefixcmp(p, \"noncumulative\")) {\n> +\t\t\tp += 13;\n> +\t\t\tDIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);\n> +\t\t}\n> +\t\telse if (!prefixcmp(p, \"cumulative\")) {\n> +\t\t\tp += 10;\n> +\t\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n> +\t\t}\n> +\t\telse if (isdigit(*p)) {\n> +\t\t\tchar *end;\n> +\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n> +\t\t\tassert(end > p);\n> +\t\t\tp = end;\n> +\t\t}\n\nThat's a senseless assert(), isn't it?\n\nYou already know the first letter is a digit, so assert(p < end) will\nalways be true.  You may want to check that this particular option is all\ndigit by checking (*end == '\\0' || *end == ',') but that is done at the\nbeginning of this loop anyway, so I don't think there is anything to check\nhere.\n\n> +\t\telse\n> +\t\t\tdie(\"Unknown --dirstat argument '%s'\", p);\n\nThe function parses dirstat_OPT, but this says argument?\n"},{"id":"166391","messageId":"7vtydlezfj.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"1303776102-9085-5-git-send-email-johan@herland.net","subject":"Re: [PATCH 4/6] Add config variable for specifying default --dirstat behavior","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-26T16:43:12Z","receivedAt":"2011-04-26T16:43:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> diff --git a/diff.c b/diff.c\n> index 08aaa47..20fe02c 100644\n> --- a/diff.c\n> +++ b/diff.c\n> @@ -45,6 +45,17 @@ static char diff_colors[][COLOR_MAXLEN] = {\n>  \tGIT_COLOR_NORMAL,\t/* FUNCINFO */\n>  };\n>  \n> +static void init_default_diff_options()\n> +{\n> +\tstatic int initialized = 0;\n> +\tif (initialized)\n> +\t\treturn;\n> +\n> +\tdefault_diff_options.dirstat_percent = 3;\n> +\n> +\tinitialized = 1;\n> +}\n\nThis smells fishy on two counts.\n\n . The rest of the diff machinery is designed to be callable multiple\n   times by calling diff_setup(), and there should be no place for any\n   call-once function like this one.\n\n . Why is dirstat-percent _so_ special that it is the only one that has to\n   be initialized this way, when the function name implies that this is\n   the central place to control the initialization of all diff related\n   options?\n\n> @@ -114,6 +125,44 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)\n>  \treturn git_diff_basic_config(var, value, cb);\n>  }\n>  \n> +static void dirstat_opt_args(struct diff_options *options, const char *args)\n> +{\n> +\tconst char *p = args;\n> +\twhile (*p) {\n> +\t\tif (!prefixcmp(p, \"changes\")) {\n> + ...\n> +\t\t}\n> +\t}\n> +}\n\nPlease move this part to the previous patch in your reroll.  This helper\nis what the previous patch should have been written with.\n"},{"id":"166392","messageId":"7vpqo9ez03.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"1303776102-9085-6-git-send-email-johan@herland.net","subject":"Re: [PATCH 5/6] Use floating point for --dirstat percentages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-26T16:52:28Z","receivedAt":"2011-04-26T16:52:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> Allow specifying --dirstat cut-off percentage as a floating point number.\n>\n> When printing the dirstat output, floating point numbers are presented in\n> rounded form (as opposed to truncated).\n\nWhy isn't it sufficient to change\n\n\tpermille = this_dir * 1000 / changed\n\nto\n\n\tpermille = (this_dir * 2000 + changed) / (changed * 2)\n\nor something?  If rounding is the only issue that bothers you (I admit\nthat it does bother me, now that you brought it up), that is.\n"},{"id":"166394","messageId":"7vliyxeynz.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"1303776102-9085-7-git-send-email-johan@herland.net","subject":"Re: [PATCH 6/6] New --dirstat=lines mode, doing dirstat analysis based on diffstat","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-26T16:59:44Z","receivedAt":"2011-04-26T16:59:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> This patch adds an alternative implementation of show_dirstat(), called\n> show_dirstat_by_line(), which uses the more expensive diffstat analysis\n> (as opposed to show_dirstat()'s own (relatively inexpensive) analysis)\n> to derive the numbers from which the --dirstat output is computed.\n>\n> The alternative implementation is controlled by the new \"lines\" argument\n> to the --dirstat option (or the diff.dirstat config variable).\n>\n> In linux-2.6.git, running the three different --dirstat modes:\n>\n>   time git diff v2.6.20..v2.6.30 --dirstat=changes > /dev/null\n> vs.\n>   time git diff v2.6.20..v2.6.30 --dirstat=lines > /dev/null\n> vs.\n>   time git diff v2.6.20..v2.6.30 --dirstat=files > /dev/null\n>\n> yields the following average runtimes on my machine:\n>\n> - \"changes\" (default): ~6.0 s\n> - \"lines\":             ~9.6 s\n> - \"files\":             ~0.1 s\n>\n> So, as expected, there's a considerable performance hit (~60%) by going\n> through the full diffstat analysis as compared to the default \"changes\"\n> analysis (obviously, \"files\" is much faster than both). As such, the\n> \"lines\" mode is probably only useful if you really need the --dirstat\n> numbers to be consistent with the numbers returned from the other\n> --*stat options.\n>\n> The patch also includes documentation and tests for the new dirstat mode.\n\nIt needs to document and also mention in the proposed commit log message\nhow binary files are accounted for.\n\n> @@ -1677,6 +1684,48 @@ found_damage:\n>  \tgather_dirstat(options, &dir, changed, \"\", 0);\n>  }\n>  \n> +static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *options)\n> +{\n> +\tint i;\n> +\tunsigned long changed;\n> +\tstruct dirstat_dir dir;\n> +\n> +\tif (data->nr == 0)\n> +\t\treturn;\n> +\n> +\tdir.files = NULL;\n> +\tdir.alloc = 0;\n> +\tdir.nr = 0;\n> +\tdir.percent = options->dirstat_percent;\n> +\tdir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);\n> +\n> +\tchanged = 0;\n> +\tfor (i = 0; i < data->nr; i++) {\n> +\t\tstruct diffstat_file *file = data->files[i];\n> +\t\tunsigned long damage = file->added + file->deleted;\n> +\t\tif (damage && file->is_binary)\n> +\t\t\t/*\n> +\t\t\t * binary files counts bytes, not lines. Must find some\n> +\t\t\t * way to normalize binary bytes vs. textual lines.\n> +\t\t\t * The following heuristic is cheap, but beyond ugly...\n> +\t\t\t */\n> +\t\t\tdamage = damage < 52 ? 1 : damage / 52;\n\nIf 52 is just as good as any number around 50-70 range, I would prefer to\nsee 64, just because I am superstitious and dividing by a power of two\nfeels nicer.\n\n> +cat <<EOF >expect_diff_dirstat_CC\n> +  16.7% changed/\n> +  16.7% dst/copy/changed/\n> +  16.7% dst/copy/rearranged/\n> +  16.7% dst/move/changed/\n> +  16.7% dst/move/rearranged/\n> +  16.7% rearranged/\n> +EOF\n\nI really wish you can come up with a way to express expected results in\nmuch less strict way in the test vector (not limited to the test vectors\nfor this patch but for the entire series).  The underlying count-damages\n(for the purpose of rename detection) implementation may improve over time\nand the textual diff generation may too.  Here what we want to preserve is\nthat these six entries show more-or-less the same amount of contribution,\nnot precisely 16.666666% each.\n"},{"id":"166429","messageId":"201104270402.10658.johan@herland.net","threadId":"27015","inReplyTo":"7vy62xezqy.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 3/6] Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T02:02:10Z","receivedAt":"2011-04-27T02:02:10Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Tuesday 26 April 2011, Junio C Hamano wrote:\n> Johan Herland <johan@herland.net> writes:\n> > +--dirstat[=<arg1,arg2,...>]::\n> > +\tOutput the distribution of relative amount of changes for each\n> > +\tsub-directory. The behavior of `--dirstat` can be customized by\n> > +\tpassing it a comma separated list of arguments. The defaults\n> > +\tare controlled by the `diff.dirstat` configuration variable (see\n> > +\tlinkgit:git-config[1]). The following arguments are available:\n>\n> These \"arguments\" feel more like \"options\" (or \"parameters\"), no?  Your\n> code in diff.c also calls it \"opt\".  The second line of the proposed log\n> message has the same issue.\n\nI have tried to consistently use \"option\" for referring to the entire\n\"--dirstat=whatever\" entity, and then use \"argument\" for referring to\neach comma-separated token following \"--dirstat=\". I based this on the\nfunction naming in diff.c, which uses \"diff_opt_parse()\" to parse diff\noptions, \"stat_opt()\" to parse the '--stat*' options, and \"opt_arg()\"\nto parse arguments to options (i.e. \"--option=argument\").\n\nTo me, \"argument\" and \"parameter\" are synonyms, but English is not my\nfirst language. I'll replace \"argument\" with \"parameter\" in the re-roll.\nI.e. \"option\" refers to the option name AND the option parameters, while\n\"parameters\" refers to the option parameters only.\n\n> > +--\n> > +`changes`;;\n> > +\tCompute the dirstat numbers by counting the lines that have been\n> > +\tremoved from the source, or added to the destination. This ignores\n> > +\tthe amount of pure code movements within a file.  In other words,\n> > +\trearranging lines in a file is not counted as much as other changes.\n> > +\tThis is the default `--dirstat` behavior.\n> \n> \"default behavior when no option is given\"?\n\n\"default behavior when no parameter is given\"?\n\n> > +`cumulative`;;\n> > +\tCount changes in a child directory for the parent directory as well.\n> > +\tNote that when using `cumulative`, the sum of the percentages\n> > +\treported may exceed 100%. The default (non-cumulative) behavior can\n> > +\tbe specified with the `noncumulative` argument.\n> \n> So the later one wins?  I.e. --dirstat=cumulative,noncumulative from the\n> command line (which seems silly), or more importantly with\n> \n>     [alias]\n>     \tdstat = diff --dirstat=cumulative\n> \n> and you can say \"git dstat --dirstat=noncumulative A..B\"?\n\nIndeed. The intention is that dirstat parameters are parsed in order\n(first from config, then from command line), and the later parameters\noverride earlier (conflicting) parameters.\n\n> > diff --git a/diff.c b/diff.c\n> > index cfbfa92..08aaa47 100644\n> > --- a/diff.c\n> > +++ b/diff.c\n> > @@ -3144,6 +3144,72 @@ static int stat_opt(struct diff_options\n> > *options, const char **av)\n> > \n> >  \treturn argcount;\n> >  \n> >  }\n> \n> /*\n>  * Document what the return value from this function means here.\n>  */\n> > +static int dirstat_opt(struct diff_options *options, const char **av)\n> \n> Do you have to pass \"const char **av\", or just \"const char *arg\"?\n\ndirstat_opt() was modeled on stat_opt(). dirstat_opt() obviously needs\njust \"const char *arg\". Will fix.\n\n> > +{\n> > +\tconst char *p, *arg = av[0];\n> > +\tchar *mangled = NULL;\n> > +\tchar sep = '=';\n> > +\n> > +\tif (!strcmp(arg, \"--cumulative\")) /* deprecated */\n> > +\t\t/* handle '--cumulative' like '--dirstat=cumulative' */\n> > +\t\tp = \"=cumulative\";\n> > +\telse if (!strcmp(arg, \"--dirstat-by-file\") ||\n> > +\t\t !prefixcmp(arg, \"--dirstat-by-file=\")) { /* deprecated */\n> > +\t\t/* handle '--dirstat-by-file=*' like '--dirstat=files,*' */\n> > +\t\tmangled = xstrdup(arg + 2);\n> > +\t\tmemcpy(mangled, \"--dirstat=files\", 15);\n> > +\t\tif (mangled[15]) {\n> > +\t\t\tassert(mangled[15] == '=');\n> > +\t\t\tmangled[15] = ',';\n> > +\t\t}\n> > +\t\targ = mangled;\n> > +\t\tp = mangled + 9;\n> \n> I understand you wanted to reuse the while() loop below, but I do not\n> think it is worth it.  Isn't it easier to read if you handled the above\n> cases in their if/else body and return?\n> \n> \tif (--cumulative) {\n> \t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n>         \tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n>                 return 1;\n> \t}\n>         if (--dirstat-by-file) {\n> \t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n> \t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n> \t\treturn 1;\n> \t}\n> \t...\n> \n> Even better, probably they can be left to diff_opt_parse() without\n> calling this function, as you are deprecating them and do not have to\n> allow them to take the opt1,opt2,... form of parameter.\n\nI understand, but politely disagree: Patch 6/6 complicates the logic\nthat DIFF_OPT_SET()/CLR() various bits in the diff options. I'd rather\nkeep that logic in one place, than duplicate it into diff_opt_parse().\n\n> > +\t}\n> > +\telse if (!prefixcmp(arg, \"-X\"))\n> > +\t\tp = arg + 2;\n> > +\telse if (!prefixcmp(arg, \"--dirstat\"))\n> > +\t\tp = arg + 9;\n> > +\telse\n> > +\t\treturn 0;\n> > +\n> > +\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n> > +\n> > +\twhile (*p) {\n> > +\t\tif (*p != sep)\n> \n> What happens to \"diff -X3 A..B\"?\n\nOops. Will fix, and add testcases verifying the fix.\n\n> > +\t\t\tdie(\"Missing argument separator ('%c'), at index %lu of '%s'\",\n> > +\t\t\t    sep, p - arg, arg);\n> \n> Don't you need to cast (p-arg) for %lu from ptrdiff type here?\n\nCopied PD_FMT from builtin/mktag.c instead.\n\n> It probably is more common to say s/index/char/;\n\nIndeed.\n\n> > +\t\tif (!prefixcmp(p, \"changes\")) {\n> > +\t\t\tp += 7;\n> > +\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n> > +\t\t}\n> > +\t\telse if (!prefixcmp(p, \"files\")) {\n> > +\t\t\tp += 5;\n> > +\t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n> > +\t\t}\n> > +\t\telse if (!prefixcmp(p, \"noncumulative\")) {\n> > +\t\t\tp += 13;\n> > +\t\t\tDIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);\n> > +\t\t}\n> > +\t\telse if (!prefixcmp(p, \"cumulative\")) {\n> > +\t\t\tp += 10;\n> > +\t\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n> > +\t\t}\n> > +\t\telse if (isdigit(*p)) {\n> > +\t\t\tchar *end;\n> > +\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n> > +\t\t\tassert(end > p);\n> > +\t\t\tp = end;\n> > +\t\t}\n> \n> That's a senseless assert(), isn't it?\n> \n> You already know the first letter is a digit, so assert(p < end) will\n> always be true.  You may want to check that this particular option is all\n> digit by checking (*end == '\\0' || *end == ',') but that is done at the\n> beginning of this loop anyway, so I don't think there is anything to\n> check here.\n\nTrue. I guess I just wanted a sanity check that aborts, rather than\nentering an infinite loop in case I got my logic wrong somewhere...\nRemoved in the re-roll.\n\n> > +\t\telse\n> > +\t\t\tdie(\"Unknown --dirstat argument '%s'\", p);\n> \n> The function parses dirstat_OPT, but this says argument?\n\nAgain, the \"option\" refers to the option name (\"--dirstat\") AND its\ns/arguments/parameters/ (\"changes,noncumulative,3\")\n\n\nYour other comments (that I felt no need to comment on) will also be\nincorporated in the re-roll.\n\n\nThanks for the feedback!\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"166430","messageId":"201104270402.17685.johan@herland.net","threadId":"27015","inReplyTo":"7vtydlezfj.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 4/6] Add config variable for specifying default --dirstat behavior","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T02:02:17Z","receivedAt":"2011-04-27T02:02:17Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Tuesday 26 April 2011, Junio C Hamano wrote:\n> Johan Herland <johan@herland.net> writes:\n> > +static void init_default_diff_options()\n> > +{\n> > +\tstatic int initialized = 0;\n> > +\tif (initialized)\n> > +\t\treturn;\n> > +\n> > +\tdefault_diff_options.dirstat_percent = 3;\n> > +\n> > +\tinitialized = 1;\n> > +}\n> \n> This smells fishy on two counts.\n> \n>  . The rest of the diff machinery is designed to be callable multiple\n>    times by calling diff_setup(), and there should be no place for any\n>    call-once function like this one.\n\nTrue. I needed it because the hardcoded \"options->dirstat_percent = 3\" in \ndiff_setup() would overwrite the \"diff.dirstat=10\" stored in \ndefault_diff_options.dirstat_percent. Instead, I needed the fallback \"3\" to \nbe stored in default_diff_options.dirstat_percent before \"diff.dirstat\" was \nparsed.\n\n>  . Why is dirstat-percent _so_ special that it is the only one that has\n>    to be initialized this way, when the function name implies that this\n>    is the central place to control the initialization of all diff\n>    related options?\n\nOnce I added init_default_diff_options(), I did in fact try to move the \nother hardcoded diff options from diff_setup(). However, I ended up with a \nlot of test failures, so I quickly gave up on that.\n\nIn the upcoming re-roll, I have solved the problem in a different way (using \na static variable to store the default dirstat percentage).\n\n> > +static void dirstat_opt_args(struct diff_options *options, const char\n> > *args) +{\n> > +\tconst char *p = args;\n> > +\twhile (*p) {\n> > +\t\tif (!prefixcmp(p, \"changes\")) {\n> > + ...\n> > +\t\t}\n> > +\t}\n> > +}\n> \n> Please move this part to the previous patch in your reroll.  This helper\n> is what the previous patch should have been written with.\n\nWill do.\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"166431","messageId":"201104270402.29085.johan@herland.net","threadId":"27015","inReplyTo":"7vpqo9ez03.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 5/6] Use floating point for --dirstat percentages","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T02:02:28Z","receivedAt":"2011-04-27T02:02:28Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Tuesday 26 April 2011, Junio C Hamano wrote:\n> Johan Herland <johan@herland.net> writes:\n> > Allow specifying --dirstat cut-off percentage as a floating point\n> > number.\n> > \n> > When printing the dirstat output, floating point numbers are presented\n> > in rounded form (as opposed to truncated).\n> \n> Why isn't it sufficient to change\n> \n> \tpermille = this_dir * 1000 / changed\n> \n> to\n> \n> \tpermille = (this_dir * 2000 + changed) / (changed * 2)\n> \n> or something?  If rounding is the only issue that bothers you (I admit\n> that it does bother me, now that you brought it up), that is.\n\nActually, rounding doesn't bother me at all (or rather, I don't really care \nif we round or truncate, as long as we're consistent).\n\nIt's just that once I s/strtoul/strtod/, and started propagating the \n\"double\"s through the code, I found that doing the final calculation and \noutput with \"double\"s was more natural than the (somewhat hackish, IMHO) \npermille/percent thing. And that's when I finally came across the fact that \n\"%6.1f\" rounds whereas the earlier version truncated.\n\nI thought about it for a second, and figured that rounding was probably what \nmost users expected.\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"166432","messageId":"201104270402.47106.johan@herland.net","threadId":"27015","inReplyTo":"7vliyxeynz.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 6/6] New --dirstat=lines mode, doing dirstat analysis based on diffstat","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T02:02:47Z","receivedAt":"2011-04-27T02:02:47Z","isPatch":true,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Tuesday 26 April 2011, Junio C Hamano wrote:\n> Johan Herland <johan@herland.net> writes:\n>\n> [...]\n> It needs to document and also mention in the proposed commit log message\n> how binary files are accounted for.\n\nWill do.\n\n> [...]\n> > +\t\tif (damage && file->is_binary)\n> > +\t\t\t/*\n> > +\t\t\t * binary files counts bytes, not lines. Must find some\n> > +\t\t\t * way to normalize binary bytes vs. textual lines.\n> > +\t\t\t * The following heuristic is cheap, but beyond ugly...\n> > +\t\t\t */\n> > +\t\t\tdamage = damage < 52 ? 1 : damage / 52;\n> \n> If 52 is just as good as any number around 50-70 range, I would prefer to\n> see 64, just because I am superstitious and dividing by a power of two\n> feels nicer.\n\nWill do.\n\n> > +cat <<EOF >expect_diff_dirstat_CC\n> > +  16.7% changed/\n> > +  16.7% dst/copy/changed/\n> > +  16.7% dst/copy/rearranged/\n> > +  16.7% dst/move/changed/\n> > +  16.7% dst/move/rearranged/\n> > +  16.7% rearranged/\n> > +EOF\n> \n> I really wish you can come up with a way to express expected results in\n> much less strict way in the test vector (not limited to the test vectors\n> for this patch but for the entire series).  The underlying count-damages\n> (for the purpose of rename detection) implementation may improve over\n> time and the textual diff generation may too.  Here what we want to\n> preserve is that these six entries show more-or-less the same amount of\n> contribution, not precisely 16.666666% each.\n\nYeah, that does make sense, although I haven't yet thought of a good way to \ndo this without losing important details. I thought of assigning letters to \neach percentage value, so that instead of\n\n\tcat <<EOF >expect_diff_dirstat_M\n\t   5.3% changed/\n\t  26.3% dst/copy/changed/\n\t  26.3% dst/copy/rearranged/\n\t  26.3% dst/copy/unchanged/\n\t   5.3% dst/move/changed/\n\t   5.3% dst/move/rearranged/\n\t   5.3% rearranged/\n\tEOF\n\n\tcat <<EOF >expect_diff_dirstat_CC\n\t  16.7% changed/\n\t  16.7% dst/copy/changed/\n\t  16.7% dst/copy/rearranged/\n\t  16.7% dst/move/changed/\n\t  16.7% dst/move/rearranged/\n\t  16.7% rearranged/\n\tEOF\n\nyou'd have\n\n\tcat <<EOF >expect_diff_dirstat_M\n\t     A% changed/\n\t     B% dst/copy/changed/\n\t     B% dst/copy/rearranged/\n\t     B% dst/copy/unchanged/\n\t     A% dst/move/changed/\n\t     A% dst/move/rearranged/\n\t     A% rearranged/\n\tEOF\n\n\tcat <<EOF >expect_diff_dirstat_CC\n\t     A% changed/\n\t     A% dst/copy/changed/\n\t     A% dst/copy/rearranged/\n\t     A% dst/move/changed/\n\t     A% dst/move/rearranged/\n\t     A% rearranged/\n\tEOF\n\nbut that would lose too much detail in many cases...\n\nAlso, I partly like the fact that we test the output strictly. That way, \nwhen someone _does_ change the underlying details, they get an immediate \ntest failure telling them exactly _how_ the numbers changed. They can then \nuse that to verify that their changes produce the expected results, before \nfinally updating the test in accordance with their new numbers.\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"166433","messageId":"1303870359-26083-1-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303776102-9085-1-git-send-email-johan@herland.net","subject":"[PATCHv2 0/6] --dirstat fixes, part 2","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T02:12:33Z","receivedAt":"2011-04-27T02:12:33Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Hi,\n\nHere's version 2 with a lot of improvements suggested by Junio.\nEspecially patches #3, #4 and #6 have been updated. The other\npatches are unchanged, or have only received minor/trivial updates.\n\n\nHave fun! :)\n\n...Johan\n\nJohan Herland (6):\n  Add several testcases for --dirstat and friends\n  Make --dirstat=0 output directories that contribute < 0.1% of changes\n  Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file\n  Add config variable for specifying default --dirstat behavior\n  Use floating point for --dirstat percentages\n  New --dirstat=lines mode, doing dirstat analysis based on diffstat\n\n Documentation/config.txt       |   44 ++\n Documentation/diff-options.txt |   54 ++-\n diff.c                         |  183 ++++++++-\n diff.h                         |    3 +-\n t/t4046-diff-dirstat.sh        |  908 ++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 1160 insertions(+), 32 deletions(-)\n create mode 100755 t/t4046-diff-dirstat.sh\n\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166436","messageId":"1303870359-26083-2-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303870359-26083-1-git-send-email-johan@herland.net","subject":"[PATCHv2 1/6] Add several testcases for --dirstat and friends","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T02:12:34Z","receivedAt":"2011-04-27T02:12:34Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Currently, t4013 is the only selftest that exercises the --dirstat machinery,\nbut it only does a superficial verification of --dirstat's output.\n\nThis patch adds a new selftest - t4046-diff-dirstat.sh - which prepares a\ncommit containing:\n - unchanged files, changed files and files with rearranged lines\n - copied files, moved files, and unmoved files\n\nIt then verifies the correct dirstat output for that commit in the following\ndirstat modes:\n - --dirstat\n - -X\n - --dirstat=0\n - -X0\n - --cumulative\n - --dirstat-by-file\n - (plus combinations of the above)\n\nEach of the above tests are also run with:\n - no rename detection\n - rename detection (-M)\n - expensive copy detection (-C -C)\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n t/t4046-diff-dirstat.sh |  580 +++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 580 insertions(+), 0 deletions(-)\n create mode 100755 t/t4046-diff-dirstat.sh\n\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nnew file mode 100755\nindex 0000000..eb6bf47\n--- /dev/null\n+++ b/t/t4046-diff-dirstat.sh\n@@ -0,0 +1,580 @@\n+#!/bin/sh\n+\n+test_description='diff --dirstat tests'\n+. ./test-lib.sh\n+\n+# set up two commits where the second commit has these files\n+# (10 lines in each file):\n+#\n+#   unchanged/text           (unchanged from 1st commit)\n+#   changed/text             (changed 1st line)\n+#   rearranged/text          (swapped 1st and 2nd line)\n+#   dst/copy/unchanged/text  (copied from src/copy/unchanged/text, unchanged)\n+#   dst/copy/changed/text    (copied from src/copy/changed/text, changed)\n+#   dst/copy/rearranged/text (copied from src/copy/rearranged/text, rearranged)\n+#   dst/move/unchanged/text  (moved from src/move/unchanged/text, unchanged)\n+#   dst/move/changed/text    (moved from src/move/changed/text, changed)\n+#   dst/move/rearranged/text (moved from src/move/rearranged/text, rearranged)\n+\n+test_expect_success 'setup' '\n+\tmkdir unchanged &&\n+\tmkdir changed &&\n+\tmkdir rearranged &&\n+\tmkdir src &&\n+\tmkdir src/copy &&\n+\tmkdir src/copy/unchanged &&\n+\tmkdir src/copy/changed &&\n+\tmkdir src/copy/rearranged &&\n+\tmkdir src/move &&\n+\tmkdir src/move/unchanged &&\n+\tmkdir src/move/changed &&\n+\tmkdir src/move/rearranged &&\n+\tcat <<EOF >unchanged/text &&\n+unchanged       line #0\n+unchanged       line #1\n+unchanged       line #2\n+unchanged       line #3\n+unchanged       line #4\n+unchanged       line #5\n+unchanged       line #6\n+unchanged       line #7\n+unchanged       line #8\n+unchanged       line #9\n+EOF\n+\tcat <<EOF >changed/text &&\n+changed         line #0\n+changed         line #1\n+changed         line #2\n+changed         line #3\n+changed         line #4\n+changed         line #5\n+changed         line #6\n+changed         line #7\n+changed         line #8\n+changed         line #9\n+EOF\n+\tcat <<EOF >rearranged/text &&\n+rearranged      line #0\n+rearranged      line #1\n+rearranged      line #2\n+rearranged      line #3\n+rearranged      line #4\n+rearranged      line #5\n+rearranged      line #6\n+rearranged      line #7\n+rearranged      line #8\n+rearranged      line #9\n+EOF\n+\tcat <<EOF >src/copy/unchanged/text &&\n+copy  unchanged line #0\n+copy  unchanged line #1\n+copy  unchanged line #2\n+copy  unchanged line #3\n+copy  unchanged line #4\n+copy  unchanged line #5\n+copy  unchanged line #6\n+copy  unchanged line #7\n+copy  unchanged line #8\n+copy  unchanged line #9\n+EOF\n+\tcat <<EOF >src/copy/changed/text &&\n+copy    changed line #0\n+copy    changed line #1\n+copy    changed line #2\n+copy    changed line #3\n+copy    changed line #4\n+copy    changed line #5\n+copy    changed line #6\n+copy    changed line #7\n+copy    changed line #8\n+copy    changed line #9\n+EOF\n+\tcat <<EOF >src/copy/rearranged/text &&\n+copy rearranged line #0\n+copy rearranged line #1\n+copy rearranged line #2\n+copy rearranged line #3\n+copy rearranged line #4\n+copy rearranged line #5\n+copy rearranged line #6\n+copy rearranged line #7\n+copy rearranged line #8\n+copy rearranged line #9\n+EOF\n+\tcat <<EOF >src/move/unchanged/text &&\n+move  unchanged line #0\n+move  unchanged line #1\n+move  unchanged line #2\n+move  unchanged line #3\n+move  unchanged line #4\n+move  unchanged line #5\n+move  unchanged line #6\n+move  unchanged line #7\n+move  unchanged line #8\n+move  unchanged line #9\n+EOF\n+\tcat <<EOF >src/move/changed/text &&\n+move    changed line #0\n+move    changed line #1\n+move    changed line #2\n+move    changed line #3\n+move    changed line #4\n+move    changed line #5\n+move    changed line #6\n+move    changed line #7\n+move    changed line #8\n+move    changed line #9\n+EOF\n+\tcat <<EOF >src/move/rearranged/text &&\n+move rearranged line #0\n+move rearranged line #1\n+move rearranged line #2\n+move rearranged line #3\n+move rearranged line #4\n+move rearranged line #5\n+move rearranged line #6\n+move rearranged line #7\n+move rearranged line #8\n+move rearranged line #9\n+EOF\n+\tgit add . &&\n+\tgit commit -m \"initial\" &&\n+\tmkdir dst &&\n+\tmkdir dst/copy &&\n+\tmkdir dst/copy/unchanged &&\n+\tmkdir dst/copy/changed &&\n+\tmkdir dst/copy/rearranged &&\n+\tmkdir dst/move &&\n+\tmkdir dst/move/unchanged &&\n+\tmkdir dst/move/changed &&\n+\tmkdir dst/move/rearranged &&\n+\tcat <<EOF >changed/text &&\n+CHANGED XXXXXXX line #0\n+changed         line #1\n+changed         line #2\n+changed         line #3\n+changed         line #4\n+changed         line #5\n+changed         line #6\n+changed         line #7\n+changed         line #8\n+changed         line #9\n+EOF\n+\tcat <<EOF >rearranged/text &&\n+rearranged      line #1\n+rearranged      line #0\n+rearranged      line #2\n+rearranged      line #3\n+rearranged      line #4\n+rearranged      line #5\n+rearranged      line #6\n+rearranged      line #7\n+rearranged      line #8\n+rearranged      line #9\n+EOF\n+\tcat <<EOF >dst/copy/unchanged/text &&\n+copy  unchanged line #0\n+copy  unchanged line #1\n+copy  unchanged line #2\n+copy  unchanged line #3\n+copy  unchanged line #4\n+copy  unchanged line #5\n+copy  unchanged line #6\n+copy  unchanged line #7\n+copy  unchanged line #8\n+copy  unchanged line #9\n+EOF\n+\tcat <<EOF >dst/copy/changed/text &&\n+copy XXXCHANGED line #0\n+copy    changed line #1\n+copy    changed line #2\n+copy    changed line #3\n+copy    changed line #4\n+copy    changed line #5\n+copy    changed line #6\n+copy    changed line #7\n+copy    changed line #8\n+copy    changed line #9\n+EOF\n+\tcat <<EOF >dst/copy/rearranged/text &&\n+copy rearranged line #1\n+copy rearranged line #0\n+copy rearranged line #2\n+copy rearranged line #3\n+copy rearranged line #4\n+copy rearranged line #5\n+copy rearranged line #6\n+copy rearranged line #7\n+copy rearranged line #8\n+copy rearranged line #9\n+EOF\n+\tcat <<EOF >dst/move/unchanged/text &&\n+move  unchanged line #0\n+move  unchanged line #1\n+move  unchanged line #2\n+move  unchanged line #3\n+move  unchanged line #4\n+move  unchanged line #5\n+move  unchanged line #6\n+move  unchanged line #7\n+move  unchanged line #8\n+move  unchanged line #9\n+EOF\n+\tcat <<EOF >dst/move/changed/text &&\n+move XXXCHANGED line #0\n+move    changed line #1\n+move    changed line #2\n+move    changed line #3\n+move    changed line #4\n+move    changed line #5\n+move    changed line #6\n+move    changed line #7\n+move    changed line #8\n+move    changed line #9\n+EOF\n+\tcat <<EOF >dst/move/rearranged/text &&\n+move rearranged line #1\n+move rearranged line #0\n+move rearranged line #2\n+move rearranged line #3\n+move rearranged line #4\n+move rearranged line #5\n+move rearranged line #6\n+move rearranged line #7\n+move rearranged line #8\n+move rearranged line #9\n+EOF\n+\tgit add . &&\n+\tgit rm -r src/move/unchanged &&\n+\tgit rm -r src/move/changed &&\n+\tgit rm -r src/move/rearranged &&\n+\tgit commit -m \"changes\"\n+'\n+\n+cat <<EOF >expect_diff_stat\n+ changed/text             |    2 +-\n+ dst/copy/changed/text    |   10 ++++++++++\n+ dst/copy/rearranged/text |   10 ++++++++++\n+ dst/copy/unchanged/text  |   10 ++++++++++\n+ dst/move/changed/text    |   10 ++++++++++\n+ dst/move/rearranged/text |   10 ++++++++++\n+ dst/move/unchanged/text  |   10 ++++++++++\n+ rearranged/text          |    2 +-\n+ src/move/changed/text    |   10 ----------\n+ src/move/rearranged/text |   10 ----------\n+ src/move/unchanged/text  |   10 ----------\n+ 11 files changed, 62 insertions(+), 32 deletions(-)\n+EOF\n+\n+cat <<EOF >expect_diff_stat_M\n+ changed/text                      |    2 +-\n+ dst/copy/changed/text             |   10 ++++++++++\n+ dst/copy/rearranged/text          |   10 ++++++++++\n+ dst/copy/unchanged/text           |   10 ++++++++++\n+ {src => dst}/move/changed/text    |    2 +-\n+ {src => dst}/move/rearranged/text |    2 +-\n+ {src => dst}/move/unchanged/text  |    0\n+ rearranged/text                   |    2 +-\n+ 8 files changed, 34 insertions(+), 4 deletions(-)\n+EOF\n+\n+cat <<EOF >expect_diff_stat_CC\n+ changed/text                      |    2 +-\n+ {src => dst}/copy/changed/text    |    2 +-\n+ {src => dst}/copy/rearranged/text |    2 +-\n+ {src => dst}/copy/unchanged/text  |    0\n+ {src => dst}/move/changed/text    |    2 +-\n+ {src => dst}/move/rearranged/text |    2 +-\n+ {src => dst}/move/unchanged/text  |    0\n+ rearranged/text                   |    2 +-\n+ 8 files changed, 6 insertions(+), 6 deletions(-)\n+EOF\n+\n+test_expect_success 'sanity check setup (--stat)' '\n+\tgit diff --stat HEAD^..HEAD >actual_diff_stat &&\n+\ttest_cmp expect_diff_stat actual_diff_stat &&\n+\tgit diff --stat -M HEAD^..HEAD >actual_diff_stat_M &&\n+\ttest_cmp expect_diff_stat_M actual_diff_stat_M &&\n+\tgit diff --stat -C -C HEAD^..HEAD >actual_diff_stat_CC &&\n+\ttest_cmp expect_diff_stat_CC actual_diff_stat_CC\n+'\n+\n+# changed/text and rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+EOF\n+\n+# rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+   5.8% dst/move/changed/\n+EOF\n+\n+# rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+  32.6% dst/move/changed/\n+EOF\n+\n+test_expect_success 'vanilla --dirstat' '\n+\tgit diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'vanilla -X' '\n+\tgit diff -X HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -X -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -X -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+   5.8% dst/move/changed/\n+   0.1% dst/move/rearranged/\n+   0.1% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+   0.6% dst/copy/rearranged/\n+  32.6% dst/move/changed/\n+   0.6% dst/move/rearranged/\n+   0.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=0' '\n+\tgit diff --dirstat=0 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success '-X0' '\n+\tgit diff -X0 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -X0 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -X0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  32.5% dst/copy/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  32.5% dst/move/\n+  65.1% dst/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+  32.5% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+  88.0% dst/copy/\n+   5.8% dst/move/changed/\n+   0.1% dst/move/rearranged/\n+   5.9% dst/move/\n+  94.0% dst/\n+   0.1% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+   0.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  32.6% dst/move/changed/\n+   0.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+   0.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=0 --cumulative' '\n+\tgit diff --dirstat=0 --cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0 --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0 --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   9.0% changed/\n+   9.0% dst/copy/changed/\n+   9.0% dst/copy/rearranged/\n+   9.0% dst/copy/unchanged/\n+   9.0% dst/move/changed/\n+   9.0% dst/move/rearranged/\n+   9.0% dst/move/unchanged/\n+   9.0% rearranged/\n+   9.0% src/move/changed/\n+   9.0% src/move/rearranged/\n+   9.0% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file' '\n+\tgit diff --dirstat-by-file HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+  27.2% dst/copy/\n+  27.2% dst/move/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file=10' '\n+\tgit diff --dirstat-by-file=10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file=10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file=10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   9.0% changed/\n+   9.0% dst/copy/changed/\n+   9.0% dst/copy/rearranged/\n+   9.0% dst/copy/unchanged/\n+  27.2% dst/copy/\n+   9.0% dst/move/changed/\n+   9.0% dst/move/rearranged/\n+   9.0% dst/move/unchanged/\n+  27.2% dst/move/\n+  54.5% dst/\n+   9.0% rearranged/\n+   9.0% src/move/changed/\n+   9.0% src/move/rearranged/\n+   9.0% src/move/unchanged/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  42.8% dst/copy/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  28.5% dst/move/\n+  71.4% dst/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file --cumulative' '\n+\tgit diff --dirstat-by-file --cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166435","messageId":"1303870359-26083-3-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303870359-26083-1-git-send-email-johan@herland.net","subject":"[PATCHv2 2/6] Make --dirstat=0 output directories that contribute < 0.1% of changes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T02:12:35Z","receivedAt":"2011-04-27T02:12:35Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"The expected output from --dirstat=0, is to include any directory with\nchanges, even if those changes contribute a minuscule portion of the total\nchanges. However, currently, directories that contribute less than 0.1% are\nnot included, since their 'permille' value is 0, and there is an\n'if (permille)' check in gather_dirstat() that causes them to be ignored.\n\nThis test is obviously intended to exclude directories that contribute no\nchanges whatsoever, but in this case, it hits too broadly. The correct\ncheck is against 'this_dir' from which the permille is calculated. Only if\nthis value is 0 does the directory truly contribute no changes, and should\nbe skipped from the output.\n\nThis patches fixes this issue, and updates corresponding testcases to\nexpect the new behvaior.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n diff.c                  |    4 ++--\n t/t4046-diff-dirstat.sh |    4 ++--\n 2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex abd9cd5..cfbfa92 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1500,8 +1500,8 @@ static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n \t *    under this directory (sources == 1).\n \t */\n \tif (baselen && sources != 1) {\n-\t\tint permille = this_dir * 1000 / changed;\n-\t\tif (permille) {\n+\t\tif (this_dir) {\n+\t\t\tint permille = this_dir * 1000 / changed;\n \t\t\tint percent = permille / 10;\n \t\t\tif (percent >= dir->percent) {\n \t\t\t\tfprintf(opt->file, \"%s%4d.%01d%% %.*s\\n\", line_prefix,\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex eb6bf47..6ff7f9f 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -346,7 +346,6 @@ test_expect_success 'vanilla -X' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n-# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -355,6 +354,7 @@ cat <<EOF >expect_diff_dirstat\n   10.8% dst/move/changed/\n   10.8% dst/move/rearranged/\n   10.8% dst/move/unchanged/\n+   0.0% rearranged/\n   10.8% src/move/changed/\n   10.8% src/move/rearranged/\n   10.8% src/move/unchanged/\n@@ -397,7 +397,6 @@ test_expect_success '-X0' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n-# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -409,6 +408,7 @@ cat <<EOF >expect_diff_dirstat\n   10.8% dst/move/unchanged/\n   32.5% dst/move/\n   65.1% dst/\n+   0.0% rearranged/\n   10.8% src/move/changed/\n   10.8% src/move/rearranged/\n   10.8% src/move/unchanged/\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166434","messageId":"1303870359-26083-4-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303870359-26083-1-git-send-email-johan@herland.net","subject":"[PATCHv2 3/6] Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T02:12:36Z","receivedAt":"2011-04-27T02:12:36Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Instead of having multiple interconnected dirstat-related options, teach\nthe --dirstat option itself to accept all behavior modifiers as parameters.\n\n - Preserve the current --dirstat=<limit> (where <limit> is an integer\n   specifying a cut-off percentage)\n - Add --dirstat=cumulative, replacing --cumulative\n - Add --dirstat=files, replacing --dirstat-by-file\n - Also add --dirstat=changes and --dirstat=noncumulative for specifying the\n   current default behavior. These allow the user to reset other --dirstat\n   parameters (e.g. 'cumulative' and 'files') occuring earlier on the\n   command line.\n\nThe deprecated options (--cumulative and --dirstat-by-file) are still\nfunctional, although they have been removed from the documentation.\n\nAllow multiple parameters to be separated by commas, e.g.:\n  --dirstat=files,10,cumulative\n\nUpdate the documentation accordingly, and add testcases verifying the\nbehavior of the new syntax.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/diff-options.txt |   44 +++++++++++----\n diff.c                         |   99 ++++++++++++++++++++++++++++++---\n t/t4046-diff-dirstat.sh        |  119 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 240 insertions(+), 22 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 7e4bd42..6a3a9c1 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -66,19 +66,39 @@ endif::git-format-patch[]\n \tnumber of modified files, as well as number of added and deleted\n \tlines.\n \n---dirstat[=<limit>]::\n-\tOutput the distribution of relative amount of changes (number of lines added or\n-\tremoved) for each sub-directory. Directories with changes below\n-\ta cut-off percent (3% by default) are not shown. The cut-off percent\n-\tcan be set with `--dirstat=<limit>`. Changes in a child directory are not\n-\tcounted for the parent directory, unless `--cumulative` is used.\n+--dirstat[=<param1,param2,...>]::\n+\tOutput the distribution of relative amount of changes for each\n+\tsub-directory. The behavior of `--dirstat` can be customized by\n+\tpassing it a comma separated list of parameters.\n+\tThe following parameters are available:\n +\n-Note that the `--dirstat` option computes the changes while ignoring\n-the amount of pure code movements within a file.  In other words,\n-rearranging lines in a file is not counted as much as other changes.\n-\n---dirstat-by-file[=<limit>]::\n-\tSame as `--dirstat`, but counts changed files instead of lines.\n+--\n+`changes`;;\n+\tCompute the dirstat numbers by counting the lines that have been\n+\tremoved from the source, or added to the destination. This ignores\n+\tthe amount of pure code movements within a file.  In other words,\n+\trearranging lines in a file is not counted as much as other changes.\n+\tThis is the default behavior when no parameter is given.\n+`files`;;\n+\tCompute the dirstat numbers by counting the number of files changed.\n+\tEach changed file counts equally in the dirstat analysis. This is\n+\tthe computationally cheapest `--dirstat` behavior, since it does\n+\tnot have to look at the file contents at all.\n+`cumulative`;;\n+\tCount changes in a child directory for the parent directory as well.\n+\tNote that when using `cumulative`, the sum of the percentages\n+\treported may exceed 100%. The default (non-cumulative) behavior can\n+\tbe specified with the `noncumulative` parameter.\n+<limit>;;\n+\tAn integer parameter specifies a cut-off percent (3% by default).\n+\tDirectories contributing less than this percentage of the changes\n+\tare not shown in the output.\n+--\n++\n+Example: The following will count changed files, while ignoring\n+directories with less than 10% of the total amount of changed files,\n+and accumulating child directory counts in the parent directories:\n+`--dirstat=files,10,cumulative`.\n \n --summary::\n \tOutput a condensed summary of extended header information\ndiff --git a/diff.c b/diff.c\nindex cfbfa92..0387e4f 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -66,6 +66,49 @@ static int parse_diff_color_slot(const char *var, int ofs)\n \treturn -1;\n }\n \n+#ifdef NO_C99_FORMAT\n+#define PD_FMT \"%d\"\n+#else\n+#define PD_FMT \"%td\"\n+#endif\n+\n+static void parse_dirstat_params(struct diff_options *options, const char *params)\n+{\n+\tconst char *p = params;\n+\twhile (*p) {\n+\t\tif (!prefixcmp(p, \"changes\")) {\n+\t\t\tp += 7;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"files\")) {\n+\t\t\tp += 5;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"noncumulative\")) {\n+\t\t\tp += 13;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"cumulative\")) {\n+\t\t\tp += 10;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n+\t\t}\n+\t\telse if (isdigit(*p)) {\n+\t\t\tchar *end;\n+\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n+\t\t\tp = end;\n+\t\t}\n+\t\telse\n+\t\t\tdie(\"Unknown --dirstat parameter '%s'\", p);\n+\n+\t\tif (*p) { /* more parameters, swallow separator */\n+\t\t\tif (*p != ',')\n+\t\t\t\tdie(\"Missing comma separator at char \" PD_FMT\n+\t\t\t\t    \" of '%s'\", p - params, params);\n+\t\t\tp++;\n+\t\t}\n+\t}\n+}\n+\n static int git_config_rename(const char *var, const char *value)\n {\n \tif (!value)\n@@ -3144,6 +3187,48 @@ static int stat_opt(struct diff_options *options, const char **av)\n \treturn argcount;\n }\n \n+/*\n+ * Parse dirstat-related options and any parameters given to those options.\n+ * Returns 1 if the option in 'arg' is a recognized dirstat-related option;\n+ * otherwise returns 0.\n+ */\n+static int dirstat_opt(struct diff_options *options, const char *arg)\n+{\n+\tconst char *p;\n+\tchar *mangled = NULL;\n+\n+\tif (!strcmp(arg, \"--cumulative\")) /* deprecated */\n+\t\t/* handle '--cumulative' like '--dirstat=cumulative' */\n+\t\tp = \"cumulative\";\n+\telse if (!strcmp(arg, \"--dirstat-by-file\") ||\n+\t\t !prefixcmp(arg, \"--dirstat-by-file=\")) { /* deprecated */\n+\t\t/* handle '--dirstat-by-file=*' like '--dirstat=files,*' */\n+\t\tmangled = xstrdup(arg + 12);\n+\t\tmemcpy(mangled, \"files\", 5);\n+\t\tif (mangled[5]) {\n+\t\t\tassert(mangled[5] == '=');\n+\t\t\tmangled[5] = ',';\n+\t\t}\n+\t\tp = mangled;\n+\t}\n+\telse if (!prefixcmp(arg, \"-X\"))\n+\t\tp = arg + 2;\n+\telse if (!prefixcmp(arg, \"--dirstat\"))\n+\t\tp = arg + 9;\n+\telse\n+\t\treturn 0;\n+\n+\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n+\n+\tif (*p)\n+\t\tif (*p == '=')\n+\t\t\tp++;\n+\t\tparse_dirstat_params(options, p);\n+\n+\tfree(mangled);\n+\treturn 1;\n+}\n+\n int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n {\n \tconst char *arg = av[0];\n@@ -3163,16 +3248,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \t\toptions->output_format |= DIFF_FORMAT_NUMSTAT;\n \telse if (!strcmp(arg, \"--shortstat\"))\n \t\toptions->output_format |= DIFF_FORMAT_SHORTSTAT;\n-\telse if (opt_arg(arg, 'X', \"dirstat\", &options->dirstat_percent))\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\telse if (!strcmp(arg, \"--cumulative\")) {\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n-\t} else if (opt_arg(arg, 0, \"dirstat-by-file\",\n-\t\t\t   &options->dirstat_percent)) {\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n-\t}\n+\telse if (!prefixcmp(arg, \"-X\") || !prefixcmp(arg, \"--dirstat\") ||\n+\t\t !strcmp(arg, \"--cumulative\"))\n+\t\t/* -X, --dirstat[=<args>], --dirstat-by-file, or --cumulative */\n+\t\treturn dirstat_opt(options, arg);\n \telse if (!strcmp(arg, \"--check\"))\n \t\toptions->output_format |= DIFF_FORMAT_CHECKDIFF;\n \telse if (!strcmp(arg, \"--summary\"))\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex 6ff7f9f..0ede619 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -346,6 +346,39 @@ test_expect_success 'vanilla -X' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'explicit defaults: --dirstat=changes,noncumulative,3' '\n+\tgit diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'explicit defaults: -Xchanges,noncumulative,3' '\n+\tgit diff -Xchanges,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -Xchanges,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -Xchanges,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'later options override earlier options:' '\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -449,6 +482,24 @@ test_expect_success '--dirstat=0 --cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=0,cumulative' '\n+\tgit diff --dirstat=0,cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success '-X0,cumulative' '\n+\tgit diff -X0,cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -X0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -X0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -491,6 +542,15 @@ test_expect_success '--dirstat-by-file' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files' '\n+\tgit diff --dirstat=files HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -525,6 +585,15 @@ test_expect_success '--dirstat-by-file=10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files,10' '\n+\tgit diff --dirstat=files,10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -577,4 +646,54 @@ test_expect_success '--dirstat-by-file --cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files,cumulative' '\n+\tgit diff --dirstat=files,cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+  27.2% dst/copy/\n+  27.2% dst/move/\n+  54.5% dst/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  42.8% dst/copy/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  28.5% dst/move/\n+  71.4% dst/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=files,cumulative,10' '\n+\tgit diff --dirstat=files,cumulative,10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166438","messageId":"1303870359-26083-5-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303870359-26083-1-git-send-email-johan@herland.net","subject":"[PATCHv2 4/6] Add config variable for specifying default --dirstat behavior","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T02:12:37Z","receivedAt":"2011-04-27T02:12:37Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"The new diff.dirstat config variable takes the same arguments as\n'--dirstat=<args>', and specifies the default arguments for --dirstat.\nThe config is obviously overridden by --dirstat arguments passed on the\ncommand line.\n\nWhen not specified, the --dirstat defaults are 'changes,noncumulative,3'.\n\nThe patch also adds several tests verifying the interaction between the\ndiff.dirstat config variable, and the --dirstat command line option.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/config.txt       |   36 ++++++++++++++++++++\n Documentation/diff-options.txt |    2 +\n diff.c                         |   10 +++++-\n t/t4046-diff-dirstat.sh        |   72 ++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 119 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6babbc7..c18dd5a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -822,6 +822,42 @@ diff.autorefreshindex::\n \taffects only 'git diff' Porcelain, and not lower level\n \t'diff' commands such as 'git diff-files'.\n \n+diff.dirstat::\n+\tA comma separated list of `--dirstat` parameters specifying the\n+\tdefault behavior of the `--dirstat` option to linkgit:git-diff[1]`\n+\tand friends. The defaults can be overridden on the command line\n+\t(using `--dirstat=<param1,param2,...>`). The fallback defaults\n+\t(when not changed by `diff.dirstat`) are `changes,noncumulative,3`.\n+\tThe following parameters are available:\n++\n+--\n+`changes`;;\n+\tCompute the dirstat numbers by counting the lines that have been\n+\tremoved from the source, or added to the destination. This ignores\n+\tthe amount of pure code movements within a file.  In other words,\n+\trearranging lines in a file is not counted as much as other changes.\n+\tThis is the default behavior when no parameter is given.\n+`files`;;\n+\tCompute the dirstat numbers by counting the number of files changed.\n+\tEach changed file counts equally in the dirstat analysis. This is\n+\tthe computationally cheapest `--dirstat` behavior, since it does\n+\tnot have to look at the file contents at all.\n+`cumulative`;;\n+\tCount changes in a child directory for the parent directory as well.\n+\tNote that when using `cumulative`, the sum of the percentages\n+\treported may exceed 100%. The default (non-cumulative) behavior can\n+\tbe specified with the `noncumulative` parameter.\n+<limit>;;\n+\tAn integer parameter specifies a cut-off percent (3% by default).\n+\tDirectories contributing less than this percentage of the changes\n+\tare not shown in the output.\n+--\n++\n+Example: The following will count changed files, while ignoring\n+directories with less than 10% of the total amount of changed files,\n+and accumulating child directory counts in the parent directories:\n+`files,10,cumulative`.\n+\n diff.external::\n \tIf this config variable is set, diff generation is not\n \tperformed using the internal diff machinery, but using the\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 6a3a9c1..4ad50b9 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -70,6 +70,8 @@ endif::git-format-patch[]\n \tOutput the distribution of relative amount of changes for each\n \tsub-directory. The behavior of `--dirstat` can be customized by\n \tpassing it a comma separated list of parameters.\n+\tThe defaults are controlled by the `diff.dirstat` configuration\n+\tvariable (see linkgit:git-config[1]).\n \tThe following parameters are available:\n +\n --\ndiff --git a/diff.c b/diff.c\nindex 0387e4f..1b6e8c0 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -31,6 +31,7 @@ static const char *external_diff_cmd_cfg;\n int diff_auto_refresh_index = 1;\n static int diff_mnemonic_prefix;\n static int diff_no_prefix;\n+static int diff_dirstat_percent_default = 3;\n static struct diff_options default_diff_options;\n \n static char diff_colors[][COLOR_MAXLEN] = {\n@@ -188,6 +189,13 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"diff.dirstat\")) {\n+\t\tdefault_diff_options.dirstat_percent = diff_dirstat_percent_default;\n+\t\tparse_dirstat_params(&default_diff_options, value);\n+\t\tdiff_dirstat_percent_default = default_diff_options.dirstat_percent;\n+\t\treturn 0;\n+\t}\n+\n \tif (!prefixcmp(var, \"submodule.\"))\n \t\treturn parse_submodule_config_option(var, value);\n \n@@ -2929,7 +2937,7 @@ void diff_setup(struct diff_options *options)\n \toptions->line_termination = '\\n';\n \toptions->break_opt = -1;\n \toptions->rename_limit = -1;\n-\toptions->dirstat_percent = 3;\n+\toptions->dirstat_percent = diff_dirstat_percent_default;\n \toptions->context = 3;\n \n \toptions->change = diff_change;\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex 0ede619..fa1885c 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -379,6 +379,15 @@ test_expect_success 'later options override earlier options:' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'non-defaults in config overridden by explicit defaults on command line' '\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -430,6 +439,15 @@ test_expect_success '-X0' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=0' '\n+\tgit -c diff.dirstat=0 diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0 diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0 diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -500,6 +518,24 @@ test_expect_success '-X0,cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=0,cumulative' '\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=0 & --dirstat=cumulative' '\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -551,6 +587,15 @@ test_expect_success '--dirstat=files' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=files' '\n+\tgit -c diff.dirstat=files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -594,6 +639,15 @@ test_expect_success '--dirstat=files,10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=10,files' '\n+\tgit -c diff.dirstat=10,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=10,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=10,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -655,6 +709,15 @@ test_expect_success '--dirstat=files,cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=cumulative,files' '\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -696,4 +759,13 @@ test_expect_success '--dirstat=files,cumulative,10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=10,cumulative,files' '\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166439","messageId":"1303870359-26083-6-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303870359-26083-1-git-send-email-johan@herland.net","subject":"[PATCHv2 5/6] Use floating point for --dirstat percentages","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T02:12:38Z","receivedAt":"2011-04-27T02:12:38Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Allow specifying --dirstat cut-off percentage as a floating point number.\n\nWhen printing the dirstat output, floating point numbers are presented in\nrounded form (as opposed to truncated). Therefore, this patch includes a\nsignificant churn in the expected output of the dirstat selftests.\n\nA selftest verifying floating-point percentage input has been added.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n\nRemaining questions:\n\n - Locale issues with strod(), e.g. decimal separator is a comma in certain\n   locales.\n\n...Johan\n\n diff.c                  |   14 +-\n diff.h                  |    2 +-\n t/t4046-diff-dirstat.sh |  327 ++++++++++++++++++++++++++---------------------\n 3 files changed, 190 insertions(+), 153 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 1b6e8c0..eb26104 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -31,7 +31,7 @@ static const char *external_diff_cmd_cfg;\n int diff_auto_refresh_index = 1;\n static int diff_mnemonic_prefix;\n static int diff_no_prefix;\n-static int diff_dirstat_percent_default = 3;\n+static double diff_dirstat_percent_default = 3.0;\n static struct diff_options default_diff_options;\n \n static char diff_colors[][COLOR_MAXLEN] = {\n@@ -95,7 +95,7 @@ static void parse_dirstat_params(struct diff_options *options, const char *param\n \t\t}\n \t\telse if (isdigit(*p)) {\n \t\t\tchar *end;\n-\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n+\t\t\toptions->dirstat_percent = strtod(p, &end);\n \t\t\tp = end;\n \t\t}\n \t\telse\n@@ -1504,7 +1504,8 @@ struct dirstat_file {\n \n struct dirstat_dir {\n \tstruct dirstat_file *files;\n-\tint alloc, nr, percent, cumulative;\n+\tdouble percent;\n+\tint alloc, nr, cumulative;\n };\n \n static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n@@ -1552,11 +1553,10 @@ static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n \t */\n \tif (baselen && sources != 1) {\n \t\tif (this_dir) {\n-\t\t\tint permille = this_dir * 1000 / changed;\n-\t\t\tint percent = permille / 10;\n+\t\t\tdouble percent = this_dir * 100.0 / changed;\n \t\t\tif (percent >= dir->percent) {\n-\t\t\t\tfprintf(opt->file, \"%s%4d.%01d%% %.*s\\n\", line_prefix,\n-\t\t\t\t\tpercent, permille % 10, baselen, base);\n+\t\t\t\tfprintf(opt->file, \"%s%6.1f%% %.*s\\n\", line_prefix,\n+\t\t\t\t\tpercent, baselen, base);\n \t\t\t\tif (!dir->cumulative)\n \t\t\t\t\treturn 0;\n \t\t\t}\ndiff --git a/diff.h b/diff.h\nindex 0083d92..781c620 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -111,13 +111,13 @@ struct diff_options {\n \tint rename_score;\n \tint rename_limit;\n \tint warn_on_too_large_rename;\n-\tint dirstat_percent;\n \tint setup;\n \tint abbrev;\n \tconst char *prefix;\n \tint prefix_length;\n \tconst char *stat_sep;\n \tlong xdl_opts;\n+\tdouble dirstat_percent;\n \n \tint stat_width;\n \tint stat_name_width;\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex fa1885c..3cafd0d 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -301,31 +301,31 @@ test_expect_success 'sanity check setup (--stat)' '\n \n # changed/text and rearranged/text falls below default 3% threshold\n cat <<EOF >expect_diff_dirstat\n-  10.8% dst/copy/changed/\n-  10.8% dst/copy/rearranged/\n-  10.8% dst/copy/unchanged/\n-  10.8% dst/move/changed/\n-  10.8% dst/move/rearranged/\n-  10.8% dst/move/unchanged/\n-  10.8% src/move/changed/\n-  10.8% src/move/rearranged/\n-  10.8% src/move/unchanged/\n+  10.9% dst/copy/changed/\n+  10.9% dst/copy/rearranged/\n+  10.9% dst/copy/unchanged/\n+  10.9% dst/move/changed/\n+  10.9% dst/move/rearranged/\n+  10.9% dst/move/unchanged/\n+  10.9% src/move/changed/\n+  10.9% src/move/rearranged/\n+  10.9% src/move/unchanged/\n EOF\n \n # rearranged/text falls below default 3% threshold\n cat <<EOF >expect_diff_dirstat_M\n-   5.8% changed/\n+   5.9% changed/\n   29.3% dst/copy/changed/\n   29.3% dst/copy/rearranged/\n   29.3% dst/copy/unchanged/\n-   5.8% dst/move/changed/\n+   5.9% dst/move/changed/\n EOF\n \n # rearranged/text falls below default 3% threshold\n cat <<EOF >expect_diff_dirstat_CC\n-  32.6% changed/\n-  32.6% dst/copy/changed/\n-  32.6% dst/move/changed/\n+  32.7% changed/\n+  32.7% dst/copy/changed/\n+  32.7% dst/move/changed/\n EOF\n \n test_expect_success 'vanilla --dirstat' '\n@@ -389,36 +389,36 @@ test_expect_success 'non-defaults in config overridden by explicit defaults on c\n '\n \n cat <<EOF >expect_diff_dirstat\n-   2.1% changed/\n-  10.8% dst/copy/changed/\n-  10.8% dst/copy/rearranged/\n-  10.8% dst/copy/unchanged/\n-  10.8% dst/move/changed/\n-  10.8% dst/move/rearranged/\n-  10.8% dst/move/unchanged/\n+   2.2% changed/\n+  10.9% dst/copy/changed/\n+  10.9% dst/copy/rearranged/\n+  10.9% dst/copy/unchanged/\n+  10.9% dst/move/changed/\n+  10.9% dst/move/rearranged/\n+  10.9% dst/move/unchanged/\n    0.0% rearranged/\n-  10.8% src/move/changed/\n-  10.8% src/move/rearranged/\n-  10.8% src/move/unchanged/\n+  10.9% src/move/changed/\n+  10.9% src/move/rearranged/\n+  10.9% src/move/unchanged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_M\n-   5.8% changed/\n+   5.9% changed/\n   29.3% dst/copy/changed/\n   29.3% dst/copy/rearranged/\n   29.3% dst/copy/unchanged/\n-   5.8% dst/move/changed/\n+   5.9% dst/move/changed/\n    0.1% dst/move/rearranged/\n    0.1% rearranged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_CC\n-  32.6% changed/\n-  32.6% dst/copy/changed/\n-   0.6% dst/copy/rearranged/\n-  32.6% dst/move/changed/\n-   0.6% dst/move/rearranged/\n-   0.6% rearranged/\n+  32.7% changed/\n+  32.7% dst/copy/changed/\n+   0.7% dst/copy/rearranged/\n+  32.7% dst/move/changed/\n+   0.7% dst/move/rearranged/\n+   0.7% rearranged/\n EOF\n \n test_expect_success '--dirstat=0' '\n@@ -449,46 +449,46 @@ test_expect_success 'diff.dirstat=0' '\n '\n \n cat <<EOF >expect_diff_dirstat\n-   2.1% changed/\n-  10.8% dst/copy/changed/\n-  10.8% dst/copy/rearranged/\n-  10.8% dst/copy/unchanged/\n-  32.5% dst/copy/\n-  10.8% dst/move/changed/\n-  10.8% dst/move/rearranged/\n-  10.8% dst/move/unchanged/\n-  32.5% dst/move/\n-  65.1% dst/\n+   2.2% changed/\n+  10.9% dst/copy/changed/\n+  10.9% dst/copy/rearranged/\n+  10.9% dst/copy/unchanged/\n+  32.6% dst/copy/\n+  10.9% dst/move/changed/\n+  10.9% dst/move/rearranged/\n+  10.9% dst/move/unchanged/\n+  32.6% dst/move/\n+  65.2% dst/\n    0.0% rearranged/\n-  10.8% src/move/changed/\n-  10.8% src/move/rearranged/\n-  10.8% src/move/unchanged/\n-  32.5% src/move/\n+  10.9% src/move/changed/\n+  10.9% src/move/rearranged/\n+  10.9% src/move/unchanged/\n+  32.6% src/move/\n EOF\n \n cat <<EOF >expect_diff_dirstat_M\n-   5.8% changed/\n+   5.9% changed/\n   29.3% dst/copy/changed/\n   29.3% dst/copy/rearranged/\n   29.3% dst/copy/unchanged/\n   88.0% dst/copy/\n-   5.8% dst/move/changed/\n+   5.9% dst/move/changed/\n    0.1% dst/move/rearranged/\n-   5.9% dst/move/\n+   6.0% dst/move/\n   94.0% dst/\n    0.1% rearranged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_CC\n-  32.6% changed/\n-  32.6% dst/copy/changed/\n-   0.6% dst/copy/rearranged/\n+  32.7% changed/\n+  32.7% dst/copy/changed/\n+   0.7% dst/copy/rearranged/\n   33.3% dst/copy/\n-  32.6% dst/move/changed/\n-   0.6% dst/move/rearranged/\n+  32.7% dst/move/changed/\n+   0.7% dst/move/rearranged/\n   33.3% dst/move/\n-  66.6% dst/\n-   0.6% rearranged/\n+  66.7% dst/\n+   0.7% rearranged/\n EOF\n \n test_expect_success '--dirstat=0 --cumulative' '\n@@ -537,36 +537,36 @@ test_expect_success 'diff.dirstat=0 & --dirstat=cumulative' '\n '\n \n cat <<EOF >expect_diff_dirstat\n-   9.0% changed/\n-   9.0% dst/copy/changed/\n-   9.0% dst/copy/rearranged/\n-   9.0% dst/copy/unchanged/\n-   9.0% dst/move/changed/\n-   9.0% dst/move/rearranged/\n-   9.0% dst/move/unchanged/\n-   9.0% rearranged/\n-   9.0% src/move/changed/\n-   9.0% src/move/rearranged/\n-   9.0% src/move/unchanged/\n+   9.1% changed/\n+   9.1% dst/copy/changed/\n+   9.1% dst/copy/rearranged/\n+   9.1% dst/copy/unchanged/\n+   9.1% dst/move/changed/\n+   9.1% dst/move/rearranged/\n+   9.1% dst/move/unchanged/\n+   9.1% rearranged/\n+   9.1% src/move/changed/\n+   9.1% src/move/rearranged/\n+   9.1% src/move/unchanged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_M\n-  14.2% changed/\n-  14.2% dst/copy/changed/\n-  14.2% dst/copy/rearranged/\n-  14.2% dst/copy/unchanged/\n-  14.2% dst/move/changed/\n-  14.2% dst/move/rearranged/\n-  14.2% rearranged/\n+  14.3% changed/\n+  14.3% dst/copy/changed/\n+  14.3% dst/copy/rearranged/\n+  14.3% dst/copy/unchanged/\n+  14.3% dst/move/changed/\n+  14.3% dst/move/rearranged/\n+  14.3% rearranged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_CC\n-  16.6% changed/\n-  16.6% dst/copy/changed/\n-  16.6% dst/copy/rearranged/\n-  16.6% dst/move/changed/\n-  16.6% dst/move/rearranged/\n-  16.6% rearranged/\n+  16.7% changed/\n+  16.7% dst/copy/changed/\n+  16.7% dst/copy/rearranged/\n+  16.7% dst/move/changed/\n+  16.7% dst/move/rearranged/\n+  16.7% rearranged/\n EOF\n \n test_expect_success '--dirstat-by-file' '\n@@ -597,28 +597,28 @@ test_expect_success 'diff.dirstat=files' '\n '\n \n cat <<EOF >expect_diff_dirstat\n-  27.2% dst/copy/\n-  27.2% dst/move/\n-  27.2% src/move/\n+  27.3% dst/copy/\n+  27.3% dst/move/\n+  27.3% src/move/\n EOF\n \n cat <<EOF >expect_diff_dirstat_M\n-  14.2% changed/\n-  14.2% dst/copy/changed/\n-  14.2% dst/copy/rearranged/\n-  14.2% dst/copy/unchanged/\n-  14.2% dst/move/changed/\n-  14.2% dst/move/rearranged/\n-  14.2% rearranged/\n+  14.3% changed/\n+  14.3% dst/copy/changed/\n+  14.3% dst/copy/rearranged/\n+  14.3% dst/copy/unchanged/\n+  14.3% dst/move/changed/\n+  14.3% dst/move/rearranged/\n+  14.3% rearranged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_CC\n-  16.6% changed/\n-  16.6% dst/copy/changed/\n-  16.6% dst/copy/rearranged/\n-  16.6% dst/move/changed/\n-  16.6% dst/move/rearranged/\n-  16.6% rearranged/\n+  16.7% changed/\n+  16.7% dst/copy/changed/\n+  16.7% dst/copy/rearranged/\n+  16.7% dst/move/changed/\n+  16.7% dst/move/rearranged/\n+  16.7% rearranged/\n EOF\n \n test_expect_success '--dirstat-by-file=10' '\n@@ -649,46 +649,46 @@ test_expect_success 'diff.dirstat=10,files' '\n '\n \n cat <<EOF >expect_diff_dirstat\n-   9.0% changed/\n-   9.0% dst/copy/changed/\n-   9.0% dst/copy/rearranged/\n-   9.0% dst/copy/unchanged/\n-  27.2% dst/copy/\n-   9.0% dst/move/changed/\n-   9.0% dst/move/rearranged/\n-   9.0% dst/move/unchanged/\n-  27.2% dst/move/\n+   9.1% changed/\n+   9.1% dst/copy/changed/\n+   9.1% dst/copy/rearranged/\n+   9.1% dst/copy/unchanged/\n+  27.3% dst/copy/\n+   9.1% dst/move/changed/\n+   9.1% dst/move/rearranged/\n+   9.1% dst/move/unchanged/\n+  27.3% dst/move/\n   54.5% dst/\n-   9.0% rearranged/\n-   9.0% src/move/changed/\n-   9.0% src/move/rearranged/\n-   9.0% src/move/unchanged/\n-  27.2% src/move/\n+   9.1% rearranged/\n+   9.1% src/move/changed/\n+   9.1% src/move/rearranged/\n+   9.1% src/move/unchanged/\n+  27.3% src/move/\n EOF\n \n cat <<EOF >expect_diff_dirstat_M\n-  14.2% changed/\n-  14.2% dst/copy/changed/\n-  14.2% dst/copy/rearranged/\n-  14.2% dst/copy/unchanged/\n-  42.8% dst/copy/\n-  14.2% dst/move/changed/\n-  14.2% dst/move/rearranged/\n-  28.5% dst/move/\n+  14.3% changed/\n+  14.3% dst/copy/changed/\n+  14.3% dst/copy/rearranged/\n+  14.3% dst/copy/unchanged/\n+  42.9% dst/copy/\n+  14.3% dst/move/changed/\n+  14.3% dst/move/rearranged/\n+  28.6% dst/move/\n   71.4% dst/\n-  14.2% rearranged/\n+  14.3% rearranged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_CC\n-  16.6% changed/\n-  16.6% dst/copy/changed/\n-  16.6% dst/copy/rearranged/\n+  16.7% changed/\n+  16.7% dst/copy/changed/\n+  16.7% dst/copy/rearranged/\n   33.3% dst/copy/\n-  16.6% dst/move/changed/\n-  16.6% dst/move/rearranged/\n+  16.7% dst/move/changed/\n+  16.7% dst/move/rearranged/\n   33.3% dst/move/\n-  66.6% dst/\n-  16.6% rearranged/\n+  66.7% dst/\n+  16.7% rearranged/\n EOF\n \n test_expect_success '--dirstat-by-file --cumulative' '\n@@ -719,35 +719,35 @@ test_expect_success 'diff.dirstat=cumulative,files' '\n '\n \n cat <<EOF >expect_diff_dirstat\n-  27.2% dst/copy/\n-  27.2% dst/move/\n+  27.3% dst/copy/\n+  27.3% dst/move/\n   54.5% dst/\n-  27.2% src/move/\n+  27.3% src/move/\n EOF\n \n cat <<EOF >expect_diff_dirstat_M\n-  14.2% changed/\n-  14.2% dst/copy/changed/\n-  14.2% dst/copy/rearranged/\n-  14.2% dst/copy/unchanged/\n-  42.8% dst/copy/\n-  14.2% dst/move/changed/\n-  14.2% dst/move/rearranged/\n-  28.5% dst/move/\n+  14.3% changed/\n+  14.3% dst/copy/changed/\n+  14.3% dst/copy/rearranged/\n+  14.3% dst/copy/unchanged/\n+  42.9% dst/copy/\n+  14.3% dst/move/changed/\n+  14.3% dst/move/rearranged/\n+  28.6% dst/move/\n   71.4% dst/\n-  14.2% rearranged/\n+  14.3% rearranged/\n EOF\n \n cat <<EOF >expect_diff_dirstat_CC\n-  16.6% changed/\n-  16.6% dst/copy/changed/\n-  16.6% dst/copy/rearranged/\n+  16.7% changed/\n+  16.7% dst/copy/changed/\n+  16.7% dst/copy/rearranged/\n   33.3% dst/copy/\n-  16.6% dst/move/changed/\n-  16.6% dst/move/rearranged/\n+  16.7% dst/move/changed/\n+  16.7% dst/move/rearranged/\n   33.3% dst/move/\n-  66.6% dst/\n-  16.6% rearranged/\n+  66.7% dst/\n+  16.7% rearranged/\n EOF\n \n test_expect_success '--dirstat=files,cumulative,10' '\n@@ -768,4 +768,41 @@ test_expect_success 'diff.dirstat=10,cumulative,files' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+cat <<EOF >expect_diff_dirstat\n+  27.3% dst/copy/\n+  27.3% dst/move/\n+  54.5% dst/\n+  27.3% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  42.9% dst/copy/\n+  28.6% dst/move/\n+  71.4% dst/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  33.3% dst/copy/\n+  33.3% dst/move/\n+  66.7% dst/\n+EOF\n+\n+test_expect_success '--dirstat=files,cumulative,16.7' '\n+\tgit diff --dirstat=files,cumulative,16.7 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,16.7 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,16.7 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=16.7,cumulative,files' '\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166437","messageId":"1303870359-26083-7-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303870359-26083-1-git-send-email-johan@herland.net","subject":"[PATCHv2 6/6] New --dirstat=lines mode, doing dirstat analysis based on diffstat","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T02:12:39Z","receivedAt":"2011-04-27T02:12:39Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"This patch adds an alternative implementation of show_dirstat(), called\nshow_dirstat_by_line(), which uses the more expensive diffstat analysis\n(as opposed to show_dirstat()'s own (relatively inexpensive) analysis)\nto derive the numbers from which the --dirstat output is computed.\n\nThe alternative implementation is controlled by the new \"lines\" parameter\nto the --dirstat option (or the diff.dirstat config variable).\n\nFor binary files, the diffstat analysis counts bytes instead of lines,\nso to prevent binary files from dominating the dirstat results, the\nbyte counts for binary files are divided by 64 before being compared to\ntheir textual/line-based counterparts. This is a stupid and ugly - but\nvery cheap - heuristic.\n\nIn linux-2.6.git, running the three different --dirstat modes:\n\n  time git diff v2.6.20..v2.6.30 --dirstat=changes > /dev/null\nvs.\n  time git diff v2.6.20..v2.6.30 --dirstat=lines > /dev/null\nvs.\n  time git diff v2.6.20..v2.6.30 --dirstat=files > /dev/null\n\nyields the following average runtimes on my machine:\n\n - \"changes\" (default): ~6.0 s\n - \"lines\":             ~9.6 s\n - \"files\":             ~0.1 s\n\nSo, as expected, there's a considerable performance hit (~60%) by going\nthrough the full diffstat analysis as compared to the default \"changes\"\nanalysis (obviously, \"files\" is much faster than both). As such, the\n\"lines\" mode is probably only useful if you really need the --dirstat\nnumbers to be consistent with the numbers returned from the other\n--*stat options.\n\nThe patch also includes documentation and tests for the new dirstat mode.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/config.txt       |    8 +++\n Documentation/diff-options.txt |    8 +++\n diff.c                         |   62 ++++++++++++++++++++++++-\n diff.h                         |    1 +\n t/t4046-diff-dirstat.sh        |  100 ++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 177 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex c18dd5a..0cad75c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -837,6 +837,14 @@ diff.dirstat::\n \tthe amount of pure code movements within a file.  In other words,\n \trearranging lines in a file is not counted as much as other changes.\n \tThis is the default behavior when no parameter is given.\n+`lines`;;\n+\tCompute the dirstat numbers by doing the regular line-based diff\n+\tanalysis, and summing the removed/added line counts. (For binary\n+\tfiles, count 64-byte chunks instead, since binary files have no\n+\tnatural concept of lines). This is a more expensive `--dirstat`\n+\tbehavior than the `changes` behavior, but it does count rearranged\n+\tlines within a file as much as other changes. The resulting output\n+\tis consistent with what you get from the other `--*stat` options.\n `files`;;\n \tCompute the dirstat numbers by counting the number of files changed.\n \tEach changed file counts equally in the dirstat analysis. This is\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 4ad50b9..327d10a 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -81,6 +81,14 @@ endif::git-format-patch[]\n \tthe amount of pure code movements within a file.  In other words,\n \trearranging lines in a file is not counted as much as other changes.\n \tThis is the default behavior when no parameter is given.\n+`lines`;;\n+\tCompute the dirstat numbers by doing the regular line-based diff\n+\tanalysis, and summing the removed/added line counts. (For binary\n+\tfiles, count 64-byte chunks instead, since binary files have no\n+\tnatural concept of lines). This is a more expensive `--dirstat`\n+\tbehavior than the `changes` behavior, but it does count rearranged\n+\tlines within a file as much as other changes. The resulting output\n+\tis consistent with what you get from the other `--*stat` options.\n `files`;;\n \tCompute the dirstat numbers by counting the number of files changed.\n \tEach changed file counts equally in the dirstat analysis. This is\ndiff --git a/diff.c b/diff.c\nindex eb26104..37c5fb5 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -79,10 +79,17 @@ static void parse_dirstat_params(struct diff_options *options, const char *param\n \twhile (*p) {\n \t\tif (!prefixcmp(p, \"changes\")) {\n \t\t\tp += 7;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_LINE);\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"lines\")) {\n+\t\t\tp += 5;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n \t\t}\n \t\telse if (!prefixcmp(p, \"files\")) {\n \t\t\tp += 5;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n \t\t}\n \t\telse if (!prefixcmp(p, \"noncumulative\")) {\n@@ -1671,6 +1678,50 @@ found_damage:\n \tgather_dirstat(options, &dir, changed, \"\", 0);\n }\n \n+static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *options)\n+{\n+\tint i;\n+\tunsigned long changed;\n+\tstruct dirstat_dir dir;\n+\n+\tif (data->nr == 0)\n+\t\treturn;\n+\n+\tdir.files = NULL;\n+\tdir.alloc = 0;\n+\tdir.nr = 0;\n+\tdir.percent = options->dirstat_percent;\n+\tdir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);\n+\n+\tchanged = 0;\n+\tfor (i = 0; i < data->nr; i++) {\n+\t\tstruct diffstat_file *file = data->files[i];\n+\t\tunsigned long damage = file->added + file->deleted;\n+\t\tif (file->is_binary)\n+\t\t\t/*\n+\t\t\t * binary files counts bytes, not lines. Must find some\n+\t\t\t * way to normalize binary bytes vs. textual lines.\n+\t\t\t * The following heuristic assumes that there are 64\n+\t\t\t * bytes per \"line\".\n+\t\t\t * This is stupid and ugly, but very cheap...\n+\t\t\t */\n+\t\t\tdamage = (damage + 63) / 64;\n+\t\tALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);\n+\t\tdir.files[dir.nr].name = file->name;\n+\t\tdir.files[dir.nr].changed = damage;\n+\t\tchanged += damage;\n+\t\tdir.nr++;\n+\t}\n+\n+\t/* This can happen even with many files, if everything was renames */\n+\tif (!changed)\n+\t\treturn;\n+\n+\t/* Show all directories with more than x% of the changes */\n+\tqsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);\n+\tgather_dirstat(options, &dir, changed, \"\", 0);\n+}\n+\n static void free_diffstat_info(struct diffstat_t *diffstat)\n {\n \tint i;\n@@ -4080,6 +4131,7 @@ void diff_flush(struct diff_options *options)\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tint i, output_format = options->output_format;\n \tint separator = 0;\n+\tint dirstat_by_line = 0;\n \n \t/*\n \t * Order: raw, stat, summary, patch\n@@ -4100,7 +4152,11 @@ void diff_flush(struct diff_options *options)\n \t\tseparator++;\n \t}\n \n-\tif (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {\n+\tif (output_format & DIFF_FORMAT_DIRSTAT && DIFF_OPT_TST(options, DIRSTAT_BY_LINE))\n+\t\tdirstat_by_line = 1;\n+\n+\tif (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT) ||\n+\t    dirstat_by_line) {\n \t\tstruct diffstat_t diffstat;\n \n \t\tmemset(&diffstat, 0, sizeof(struct diffstat_t));\n@@ -4115,10 +4171,12 @@ void diff_flush(struct diff_options *options)\n \t\t\tshow_stats(&diffstat, options);\n \t\tif (output_format & DIFF_FORMAT_SHORTSTAT)\n \t\t\tshow_shortstats(&diffstat, options);\n+\t\tif (output_format & DIFF_FORMAT_DIRSTAT)\n+\t\t\tshow_dirstat_by_line(&diffstat, options);\n \t\tfree_diffstat_info(&diffstat);\n \t\tseparator++;\n \t}\n-\tif (output_format & DIFF_FORMAT_DIRSTAT)\n+\tif ((output_format & DIFF_FORMAT_DIRSTAT) && !dirstat_by_line)\n \t\tshow_dirstat(options);\n \n \tif (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {\ndiff --git a/diff.h b/diff.h\nindex 781c620..5f12049 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -78,6 +78,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)\n #define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)\n #define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)\n #define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)\n+#define DIFF_OPT_DIRSTAT_BY_LINE     (1 << 28)\n \n #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)\n #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex 3cafd0d..f1946a1 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -805,4 +805,104 @@ test_expect_success 'diff.dirstat=16.7,cumulative,files' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+cat <<EOF >expect_diff_dirstat\n+  10.6% dst/copy/changed/\n+  10.6% dst/copy/rearranged/\n+  10.6% dst/copy/unchanged/\n+  10.6% dst/move/changed/\n+  10.6% dst/move/rearranged/\n+  10.6% dst/move/unchanged/\n+  10.6% src/move/changed/\n+  10.6% src/move/rearranged/\n+  10.6% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.3% changed/\n+  26.3% dst/copy/changed/\n+  26.3% dst/copy/rearranged/\n+  26.3% dst/copy/unchanged/\n+   5.3% dst/move/changed/\n+   5.3% dst/move/rearranged/\n+   5.3% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.7% changed/\n+  16.7% dst/copy/changed/\n+  16.7% dst/copy/rearranged/\n+  16.7% dst/move/changed/\n+  16.7% dst/move/rearranged/\n+  16.7% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=lines' '\n+\tgit diff --dirstat=lines HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=lines -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=lines -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=lines' '\n+\tgit -c diff.dirstat=lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.6% dst/copy/changed/\n+  10.6% dst/copy/rearranged/\n+  10.6% dst/copy/unchanged/\n+  10.6% dst/move/changed/\n+  10.6% dst/move/rearranged/\n+  10.6% dst/move/unchanged/\n+   2.1% rearranged/\n+  10.6% src/move/changed/\n+  10.6% src/move/rearranged/\n+  10.6% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.3% changed/\n+  26.3% dst/copy/changed/\n+  26.3% dst/copy/rearranged/\n+  26.3% dst/copy/unchanged/\n+   5.3% dst/move/changed/\n+   5.3% dst/move/rearranged/\n+   5.3% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.7% changed/\n+  16.7% dst/copy/changed/\n+  16.7% dst/copy/rearranged/\n+  16.7% dst/move/changed/\n+  16.7% dst/move/rearranged/\n+  16.7% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=lines,0' '\n+\tgit diff --dirstat=lines,0 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=lines,0 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=lines,0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=0,lines' '\n+\tgit -c diff.dirstat=0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166442","messageId":"BANLkTi=uWxpyJhEGt97K_qSZV_VPuD5JcQ@mail.gmail.com","threadId":"27015","inReplyTo":"1303870359-26083-6-git-send-email-johan@herland.net","subject":"Re: [PATCHv2 5/6] Use floating point for --dirstat percentages","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2011-04-27T02:45:33Z","receivedAt":"2011-04-27T02:45:33Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Tue, Apr 26, 2011 at 7:12 PM, Johan Herland <johan@herland.net> wrote:\n> Allow specifying --dirstat cut-off percentage as a floating point number.\n>\n> When printing the dirstat output, floating point numbers are presented in\n> rounded form (as opposed to truncated). Therefore, this patch includes a\n> significant churn in the expected output of the dirstat selftests.\n\nHmm. So thinking more about this, I do think it's preferable that the\npercentages never be rounded up.\n\nWhy? Having them add up to 99.9 due to rounding error makes a ton more\nsense to me than having percentages add up to 100.1 But I guess I\ndon't care _that_ much, since most of the time I use \"cumulative\",\nwhere you don't much have that issue anyway.\n\nAlso:\n\n> Remaining questions:\n>\n>  - Locale issues with strod(), e.g. decimal separator is a comma in certain\n>   locales.\n\nI think this is a serious issue. Of course, any sane user will have\nthe numeric locale be set to \"C\", but we know that any argument that\ndepends on sane users is likely broken.\n\nSo regardless of the rounding, I do think we should make sure that we have a\n\n  setlocale(LC_NUMERIC, \"C\");\n\nboth for the parsing _and_ for the printout. And if somebody really\nwants the locale to affect these kinds of things, we might have a\nconfig option to allow it, but I think we should default to a nice\nfixed format in this case.\n\n(I don't know anybody who uses LC_NUMERIC that actually sets the\ndecimal point character to ',' though - it is bound to break a ton of\napplications. And I say that as somebody who grew up with a \"decimal\ncomma\").\n\n                   Linus\n"},{"id":"166446","messageId":"7vipu0b8zy.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"201104270402.29085.johan@herland.net","subject":"Re: [PATCH 5/6] Use floating point for --dirstat percentages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-27T04:42:25Z","receivedAt":"2011-04-27T04:42:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> On Tuesday 26 April 2011, Junio C Hamano wrote:\n>> Johan Herland <johan@herland.net> writes:\n>> > Allow specifying --dirstat cut-off percentage as a floating point\n>> > number.\n>> > \n>> > When printing the dirstat output, floating point numbers are presented\n>> > in rounded form (as opposed to truncated).\n>> \n>> Why isn't it sufficient to change\n>> \n>> \tpermille = this_dir * 1000 / changed\n>> \n>> to\n>> \n>> \tpermille = (this_dir * 2000 + changed) / (changed * 2)\n>> \n>> or something?  If rounding is the only issue that bothers you (I admit\n>> that it does bother me, now that you brought it up), that is.\n>\n> Actually, rounding doesn't bother me at all (or rather, I don't really care \n> if we round or truncate, as long as we're consistent).\n\nIf that is the case, I would rather not see us use floating point for\nthis.\n\nI have used \"#define MAX_SCORE 60000.0\" to sneakily run rename similarity\nscore in floating point, feeling ashamed of it, and have been meaning to\nfix that for quite a long time, but other than that, I do not think we\nhave anything that uses float.\n"},{"id":"166448","messageId":"BANLkTikWhRiDaojR2NipcP0kpP49we5iUQ@mail.gmail.com","threadId":"27015","inReplyTo":"7vipu0b8zy.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 5/6] Use floating point for --dirstat percentages","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2011-04-27T04:53:00Z","receivedAt":"2011-04-27T04:53:00Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Tue, Apr 26, 2011 at 9:42 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> If that is the case, I would rather not see us use floating point for\n> this.\n\nConsidering that we still just output with a tenth of a percent\ngranularity, I'd suggest just continuing with using permille\ninternally - including for the limit.\n\nSo instead of actually using floating point, just parsing a single\ndigit worth of fractional percent would be beautiful. IOW, being able\nto say\n\n  --dirstat=1.5\n\nto give a 1.5% cut-off point would be really nice - but then\ninternally just saying \"15 permille\" and using integers all the way?\n\nDoing all the fake floating point by hand also obviously then avoids\nthe whole LC_NUMERIC locale issue.\n\n                        Linus\n"},{"id":"166449","messageId":"7vaafcb8ho.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"201104270402.10658.johan@herland.net","subject":"Re: [PATCH 3/6] Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-27T04:53:23Z","receivedAt":"2011-04-27T04:53:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> I have tried to consistently use \"option\" for referring to the entire\n> \"--dirstat=whatever\" entity, and then use \"argument\" for referring to\n> each comma-separated token following \"--dirstat=\".\n\nOk.\n\nYour terminology is pretty much consistent with how POSIX calls these\nthings (Cf. *1*, *2*)\n\n * -X is an OPTION;\n * 3 in -X3 is an OPTION ARGUMENT; and\n * OPTION ARGUMENT is explained as \"A parameter that follows certain options\".\n\nBetween \"--option=parameter\" or \"--option=option argument\", the former is\neasier to type and read, so it is slightly more preferable.\n\n\n>> > +--\n>> > +`changes`;;\n>> > +\tCompute the dirstat numbers by counting the lines that have been\n>> > +\tremoved from the source, or added to the destination. This ignores\n>> > +\tthe amount of pure code movements within a file.  In other words,\n>> > +\trearranging lines in a file is not counted as much as other changes.\n>> > +\tThis is the default `--dirstat` behavior.\n>> \n>> \"default behavior when no parameter is given\"?\n\nRight.  Thanks.\n\n[References]\n\n*1* 12.1 Utility Argument Syntax\n\nhttp://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_01\n\n*2* 3.256 Option, 3.257 Option-Argument\n\nhttp://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_256.\n"},{"id":"166456","messageId":"7voc3s9snt.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"BANLkTikWhRiDaojR2NipcP0kpP49we5iUQ@mail.gmail.com","subject":"Re: [PATCH 5/6] Use floating point for --dirstat percentages","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-27T05:20:38Z","receivedAt":"2011-04-27T05:20:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> So instead of actually using floating point, just parsing a single\n> digit worth of fractional percent would be beautiful. IOW, being able\n> to say\n>\n>   --dirstat=1.5\n>\n> to give a 1.5% cut-off point would be really nice - but then\n> internally just saying \"15 permille\" and using integers all the way?\n\nMakes perfect sense to me.\n"},{"id":"166473","messageId":"1303892653-3958-1-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303870359-26083-1-git-send-email-johan@herland.net","subject":"[PATCHv3 0/6] --dirstat fixes, part 2","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T08:24:07Z","receivedAt":"2011-04-27T08:24:07Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Hi,\n\nHere's version 3 with patch 5/6 changed to use integer permille calculation\ninstead of floating point percent calculations. Patch 6/6 also has minor\nupdates caused by the 5/6 changes (s/percent/permille/ plus updated test\nvectors). The other patches are unchanged.\n\n\nHave fun! :)\n\n...Johan\n\nJohan Herland (6):\n  Add several testcases for --dirstat and friends\n  Make --dirstat=0 output directories that contribute < 0.1% of changes\n  Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file\n  Add config variable for specifying default --dirstat behavior\n  Allow specifying --dirstat cut-off percentage as a floating point number\n  New --dirstat=lines mode, doing dirstat analysis based on diffstat\n\n Documentation/config.txt       |   44 ++\n Documentation/diff-options.txt |   54 ++-\n diff.c                         |  191 ++++++++-\n diff.h                         |    3 +-\n t/t4046-diff-dirstat.sh        |  908 ++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 1167 insertions(+), 33 deletions(-)\n create mode 100755 t/t4046-diff-dirstat.sh\n\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166475","messageId":"1303892653-3958-2-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303892653-3958-1-git-send-email-johan@herland.net","subject":"[PATCHv3 1/6] Add several testcases for --dirstat and friends","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T08:24:08Z","receivedAt":"2011-04-27T08:24:08Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Currently, t4013 is the only selftest that exercises the --dirstat machinery,\nbut it only does a superficial verification of --dirstat's output.\n\nThis patch adds a new selftest - t4046-diff-dirstat.sh - which prepares a\ncommit containing:\n - unchanged files, changed files and files with rearranged lines\n - copied files, moved files, and unmoved files\n\nIt then verifies the correct dirstat output for that commit in the following\ndirstat modes:\n - --dirstat\n - -X\n - --dirstat=0\n - -X0\n - --cumulative\n - --dirstat-by-file\n - (plus combinations of the above)\n\nEach of the above tests are also run with:\n - no rename detection\n - rename detection (-M)\n - expensive copy detection (-C -C)\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n t/t4046-diff-dirstat.sh |  580 +++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 580 insertions(+), 0 deletions(-)\n create mode 100755 t/t4046-diff-dirstat.sh\n\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nnew file mode 100755\nindex 0000000..eb6bf47\n--- /dev/null\n+++ b/t/t4046-diff-dirstat.sh\n@@ -0,0 +1,580 @@\n+#!/bin/sh\n+\n+test_description='diff --dirstat tests'\n+. ./test-lib.sh\n+\n+# set up two commits where the second commit has these files\n+# (10 lines in each file):\n+#\n+#   unchanged/text           (unchanged from 1st commit)\n+#   changed/text             (changed 1st line)\n+#   rearranged/text          (swapped 1st and 2nd line)\n+#   dst/copy/unchanged/text  (copied from src/copy/unchanged/text, unchanged)\n+#   dst/copy/changed/text    (copied from src/copy/changed/text, changed)\n+#   dst/copy/rearranged/text (copied from src/copy/rearranged/text, rearranged)\n+#   dst/move/unchanged/text  (moved from src/move/unchanged/text, unchanged)\n+#   dst/move/changed/text    (moved from src/move/changed/text, changed)\n+#   dst/move/rearranged/text (moved from src/move/rearranged/text, rearranged)\n+\n+test_expect_success 'setup' '\n+\tmkdir unchanged &&\n+\tmkdir changed &&\n+\tmkdir rearranged &&\n+\tmkdir src &&\n+\tmkdir src/copy &&\n+\tmkdir src/copy/unchanged &&\n+\tmkdir src/copy/changed &&\n+\tmkdir src/copy/rearranged &&\n+\tmkdir src/move &&\n+\tmkdir src/move/unchanged &&\n+\tmkdir src/move/changed &&\n+\tmkdir src/move/rearranged &&\n+\tcat <<EOF >unchanged/text &&\n+unchanged       line #0\n+unchanged       line #1\n+unchanged       line #2\n+unchanged       line #3\n+unchanged       line #4\n+unchanged       line #5\n+unchanged       line #6\n+unchanged       line #7\n+unchanged       line #8\n+unchanged       line #9\n+EOF\n+\tcat <<EOF >changed/text &&\n+changed         line #0\n+changed         line #1\n+changed         line #2\n+changed         line #3\n+changed         line #4\n+changed         line #5\n+changed         line #6\n+changed         line #7\n+changed         line #8\n+changed         line #9\n+EOF\n+\tcat <<EOF >rearranged/text &&\n+rearranged      line #0\n+rearranged      line #1\n+rearranged      line #2\n+rearranged      line #3\n+rearranged      line #4\n+rearranged      line #5\n+rearranged      line #6\n+rearranged      line #7\n+rearranged      line #8\n+rearranged      line #9\n+EOF\n+\tcat <<EOF >src/copy/unchanged/text &&\n+copy  unchanged line #0\n+copy  unchanged line #1\n+copy  unchanged line #2\n+copy  unchanged line #3\n+copy  unchanged line #4\n+copy  unchanged line #5\n+copy  unchanged line #6\n+copy  unchanged line #7\n+copy  unchanged line #8\n+copy  unchanged line #9\n+EOF\n+\tcat <<EOF >src/copy/changed/text &&\n+copy    changed line #0\n+copy    changed line #1\n+copy    changed line #2\n+copy    changed line #3\n+copy    changed line #4\n+copy    changed line #5\n+copy    changed line #6\n+copy    changed line #7\n+copy    changed line #8\n+copy    changed line #9\n+EOF\n+\tcat <<EOF >src/copy/rearranged/text &&\n+copy rearranged line #0\n+copy rearranged line #1\n+copy rearranged line #2\n+copy rearranged line #3\n+copy rearranged line #4\n+copy rearranged line #5\n+copy rearranged line #6\n+copy rearranged line #7\n+copy rearranged line #8\n+copy rearranged line #9\n+EOF\n+\tcat <<EOF >src/move/unchanged/text &&\n+move  unchanged line #0\n+move  unchanged line #1\n+move  unchanged line #2\n+move  unchanged line #3\n+move  unchanged line #4\n+move  unchanged line #5\n+move  unchanged line #6\n+move  unchanged line #7\n+move  unchanged line #8\n+move  unchanged line #9\n+EOF\n+\tcat <<EOF >src/move/changed/text &&\n+move    changed line #0\n+move    changed line #1\n+move    changed line #2\n+move    changed line #3\n+move    changed line #4\n+move    changed line #5\n+move    changed line #6\n+move    changed line #7\n+move    changed line #8\n+move    changed line #9\n+EOF\n+\tcat <<EOF >src/move/rearranged/text &&\n+move rearranged line #0\n+move rearranged line #1\n+move rearranged line #2\n+move rearranged line #3\n+move rearranged line #4\n+move rearranged line #5\n+move rearranged line #6\n+move rearranged line #7\n+move rearranged line #8\n+move rearranged line #9\n+EOF\n+\tgit add . &&\n+\tgit commit -m \"initial\" &&\n+\tmkdir dst &&\n+\tmkdir dst/copy &&\n+\tmkdir dst/copy/unchanged &&\n+\tmkdir dst/copy/changed &&\n+\tmkdir dst/copy/rearranged &&\n+\tmkdir dst/move &&\n+\tmkdir dst/move/unchanged &&\n+\tmkdir dst/move/changed &&\n+\tmkdir dst/move/rearranged &&\n+\tcat <<EOF >changed/text &&\n+CHANGED XXXXXXX line #0\n+changed         line #1\n+changed         line #2\n+changed         line #3\n+changed         line #4\n+changed         line #5\n+changed         line #6\n+changed         line #7\n+changed         line #8\n+changed         line #9\n+EOF\n+\tcat <<EOF >rearranged/text &&\n+rearranged      line #1\n+rearranged      line #0\n+rearranged      line #2\n+rearranged      line #3\n+rearranged      line #4\n+rearranged      line #5\n+rearranged      line #6\n+rearranged      line #7\n+rearranged      line #8\n+rearranged      line #9\n+EOF\n+\tcat <<EOF >dst/copy/unchanged/text &&\n+copy  unchanged line #0\n+copy  unchanged line #1\n+copy  unchanged line #2\n+copy  unchanged line #3\n+copy  unchanged line #4\n+copy  unchanged line #5\n+copy  unchanged line #6\n+copy  unchanged line #7\n+copy  unchanged line #8\n+copy  unchanged line #9\n+EOF\n+\tcat <<EOF >dst/copy/changed/text &&\n+copy XXXCHANGED line #0\n+copy    changed line #1\n+copy    changed line #2\n+copy    changed line #3\n+copy    changed line #4\n+copy    changed line #5\n+copy    changed line #6\n+copy    changed line #7\n+copy    changed line #8\n+copy    changed line #9\n+EOF\n+\tcat <<EOF >dst/copy/rearranged/text &&\n+copy rearranged line #1\n+copy rearranged line #0\n+copy rearranged line #2\n+copy rearranged line #3\n+copy rearranged line #4\n+copy rearranged line #5\n+copy rearranged line #6\n+copy rearranged line #7\n+copy rearranged line #8\n+copy rearranged line #9\n+EOF\n+\tcat <<EOF >dst/move/unchanged/text &&\n+move  unchanged line #0\n+move  unchanged line #1\n+move  unchanged line #2\n+move  unchanged line #3\n+move  unchanged line #4\n+move  unchanged line #5\n+move  unchanged line #6\n+move  unchanged line #7\n+move  unchanged line #8\n+move  unchanged line #9\n+EOF\n+\tcat <<EOF >dst/move/changed/text &&\n+move XXXCHANGED line #0\n+move    changed line #1\n+move    changed line #2\n+move    changed line #3\n+move    changed line #4\n+move    changed line #5\n+move    changed line #6\n+move    changed line #7\n+move    changed line #8\n+move    changed line #9\n+EOF\n+\tcat <<EOF >dst/move/rearranged/text &&\n+move rearranged line #1\n+move rearranged line #0\n+move rearranged line #2\n+move rearranged line #3\n+move rearranged line #4\n+move rearranged line #5\n+move rearranged line #6\n+move rearranged line #7\n+move rearranged line #8\n+move rearranged line #9\n+EOF\n+\tgit add . &&\n+\tgit rm -r src/move/unchanged &&\n+\tgit rm -r src/move/changed &&\n+\tgit rm -r src/move/rearranged &&\n+\tgit commit -m \"changes\"\n+'\n+\n+cat <<EOF >expect_diff_stat\n+ changed/text             |    2 +-\n+ dst/copy/changed/text    |   10 ++++++++++\n+ dst/copy/rearranged/text |   10 ++++++++++\n+ dst/copy/unchanged/text  |   10 ++++++++++\n+ dst/move/changed/text    |   10 ++++++++++\n+ dst/move/rearranged/text |   10 ++++++++++\n+ dst/move/unchanged/text  |   10 ++++++++++\n+ rearranged/text          |    2 +-\n+ src/move/changed/text    |   10 ----------\n+ src/move/rearranged/text |   10 ----------\n+ src/move/unchanged/text  |   10 ----------\n+ 11 files changed, 62 insertions(+), 32 deletions(-)\n+EOF\n+\n+cat <<EOF >expect_diff_stat_M\n+ changed/text                      |    2 +-\n+ dst/copy/changed/text             |   10 ++++++++++\n+ dst/copy/rearranged/text          |   10 ++++++++++\n+ dst/copy/unchanged/text           |   10 ++++++++++\n+ {src => dst}/move/changed/text    |    2 +-\n+ {src => dst}/move/rearranged/text |    2 +-\n+ {src => dst}/move/unchanged/text  |    0\n+ rearranged/text                   |    2 +-\n+ 8 files changed, 34 insertions(+), 4 deletions(-)\n+EOF\n+\n+cat <<EOF >expect_diff_stat_CC\n+ changed/text                      |    2 +-\n+ {src => dst}/copy/changed/text    |    2 +-\n+ {src => dst}/copy/rearranged/text |    2 +-\n+ {src => dst}/copy/unchanged/text  |    0\n+ {src => dst}/move/changed/text    |    2 +-\n+ {src => dst}/move/rearranged/text |    2 +-\n+ {src => dst}/move/unchanged/text  |    0\n+ rearranged/text                   |    2 +-\n+ 8 files changed, 6 insertions(+), 6 deletions(-)\n+EOF\n+\n+test_expect_success 'sanity check setup (--stat)' '\n+\tgit diff --stat HEAD^..HEAD >actual_diff_stat &&\n+\ttest_cmp expect_diff_stat actual_diff_stat &&\n+\tgit diff --stat -M HEAD^..HEAD >actual_diff_stat_M &&\n+\ttest_cmp expect_diff_stat_M actual_diff_stat_M &&\n+\tgit diff --stat -C -C HEAD^..HEAD >actual_diff_stat_CC &&\n+\ttest_cmp expect_diff_stat_CC actual_diff_stat_CC\n+'\n+\n+# changed/text and rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+EOF\n+\n+# rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+   5.8% dst/move/changed/\n+EOF\n+\n+# rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+  32.6% dst/move/changed/\n+EOF\n+\n+test_expect_success 'vanilla --dirstat' '\n+\tgit diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'vanilla -X' '\n+\tgit diff -X HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -X -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -X -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+   5.8% dst/move/changed/\n+   0.1% dst/move/rearranged/\n+   0.1% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+   0.6% dst/copy/rearranged/\n+  32.6% dst/move/changed/\n+   0.6% dst/move/rearranged/\n+   0.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=0' '\n+\tgit diff --dirstat=0 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success '-X0' '\n+\tgit diff -X0 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -X0 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -X0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  32.5% dst/copy/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  32.5% dst/move/\n+  65.1% dst/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+  32.5% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+  88.0% dst/copy/\n+   5.8% dst/move/changed/\n+   0.1% dst/move/rearranged/\n+   5.9% dst/move/\n+  94.0% dst/\n+   0.1% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+   0.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  32.6% dst/move/changed/\n+   0.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+   0.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=0 --cumulative' '\n+\tgit diff --dirstat=0 --cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0 --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0 --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   9.0% changed/\n+   9.0% dst/copy/changed/\n+   9.0% dst/copy/rearranged/\n+   9.0% dst/copy/unchanged/\n+   9.0% dst/move/changed/\n+   9.0% dst/move/rearranged/\n+   9.0% dst/move/unchanged/\n+   9.0% rearranged/\n+   9.0% src/move/changed/\n+   9.0% src/move/rearranged/\n+   9.0% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file' '\n+\tgit diff --dirstat-by-file HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+  27.2% dst/copy/\n+  27.2% dst/move/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file=10' '\n+\tgit diff --dirstat-by-file=10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file=10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file=10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   9.0% changed/\n+   9.0% dst/copy/changed/\n+   9.0% dst/copy/rearranged/\n+   9.0% dst/copy/unchanged/\n+  27.2% dst/copy/\n+   9.0% dst/move/changed/\n+   9.0% dst/move/rearranged/\n+   9.0% dst/move/unchanged/\n+  27.2% dst/move/\n+  54.5% dst/\n+   9.0% rearranged/\n+   9.0% src/move/changed/\n+   9.0% src/move/rearranged/\n+   9.0% src/move/unchanged/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  42.8% dst/copy/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  28.5% dst/move/\n+  71.4% dst/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file --cumulative' '\n+\tgit diff --dirstat-by-file --cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166474","messageId":"1303892653-3958-3-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303892653-3958-1-git-send-email-johan@herland.net","subject":"[PATCHv3 2/6] Make --dirstat=0 output directories that contribute < 0.1% of changes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T08:24:09Z","receivedAt":"2011-04-27T08:24:09Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"The expected output from --dirstat=0, is to include any directory with\nchanges, even if those changes contribute a minuscule portion of the total\nchanges. However, currently, directories that contribute less than 0.1% are\nnot included, since their 'permille' value is 0, and there is an\n'if (permille)' check in gather_dirstat() that causes them to be ignored.\n\nThis test is obviously intended to exclude directories that contribute no\nchanges whatsoever, but in this case, it hits too broadly. The correct\ncheck is against 'this_dir' from which the permille is calculated. Only if\nthis value is 0 does the directory truly contribute no changes, and should\nbe skipped from the output.\n\nThis patches fixes this issue, and updates corresponding testcases to\nexpect the new behvaior.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n diff.c                  |    4 ++--\n t/t4046-diff-dirstat.sh |    4 ++--\n 2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex abd9cd5..cfbfa92 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1500,8 +1500,8 @@ static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n \t *    under this directory (sources == 1).\n \t */\n \tif (baselen && sources != 1) {\n-\t\tint permille = this_dir * 1000 / changed;\n-\t\tif (permille) {\n+\t\tif (this_dir) {\n+\t\t\tint permille = this_dir * 1000 / changed;\n \t\t\tint percent = permille / 10;\n \t\t\tif (percent >= dir->percent) {\n \t\t\t\tfprintf(opt->file, \"%s%4d.%01d%% %.*s\\n\", line_prefix,\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex eb6bf47..6ff7f9f 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -346,7 +346,6 @@ test_expect_success 'vanilla -X' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n-# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -355,6 +354,7 @@ cat <<EOF >expect_diff_dirstat\n   10.8% dst/move/changed/\n   10.8% dst/move/rearranged/\n   10.8% dst/move/unchanged/\n+   0.0% rearranged/\n   10.8% src/move/changed/\n   10.8% src/move/rearranged/\n   10.8% src/move/unchanged/\n@@ -397,7 +397,6 @@ test_expect_success '-X0' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n-# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -409,6 +408,7 @@ cat <<EOF >expect_diff_dirstat\n   10.8% dst/move/unchanged/\n   32.5% dst/move/\n   65.1% dst/\n+   0.0% rearranged/\n   10.8% src/move/changed/\n   10.8% src/move/rearranged/\n   10.8% src/move/unchanged/\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166479","messageId":"1303892653-3958-4-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303892653-3958-1-git-send-email-johan@herland.net","subject":"[PATCHv3 3/6] Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T08:24:10Z","receivedAt":"2011-04-27T08:24:10Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Instead of having multiple interconnected dirstat-related options, teach\nthe --dirstat option itself to accept all behavior modifiers as parameters.\n\n - Preserve the current --dirstat=<limit> (where <limit> is an integer\n   specifying a cut-off percentage)\n - Add --dirstat=cumulative, replacing --cumulative\n - Add --dirstat=files, replacing --dirstat-by-file\n - Also add --dirstat=changes and --dirstat=noncumulative for specifying the\n   current default behavior. These allow the user to reset other --dirstat\n   parameters (e.g. 'cumulative' and 'files') occuring earlier on the\n   command line.\n\nThe deprecated options (--cumulative and --dirstat-by-file) are still\nfunctional, although they have been removed from the documentation.\n\nAllow multiple parameters to be separated by commas, e.g.:\n  --dirstat=files,10,cumulative\n\nUpdate the documentation accordingly, and add testcases verifying the\nbehavior of the new syntax.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/diff-options.txt |   44 +++++++++++----\n diff.c                         |   99 ++++++++++++++++++++++++++++++---\n t/t4046-diff-dirstat.sh        |  119 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 240 insertions(+), 22 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 7e4bd42..6a3a9c1 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -66,19 +66,39 @@ endif::git-format-patch[]\n \tnumber of modified files, as well as number of added and deleted\n \tlines.\n \n---dirstat[=<limit>]::\n-\tOutput the distribution of relative amount of changes (number of lines added or\n-\tremoved) for each sub-directory. Directories with changes below\n-\ta cut-off percent (3% by default) are not shown. The cut-off percent\n-\tcan be set with `--dirstat=<limit>`. Changes in a child directory are not\n-\tcounted for the parent directory, unless `--cumulative` is used.\n+--dirstat[=<param1,param2,...>]::\n+\tOutput the distribution of relative amount of changes for each\n+\tsub-directory. The behavior of `--dirstat` can be customized by\n+\tpassing it a comma separated list of parameters.\n+\tThe following parameters are available:\n +\n-Note that the `--dirstat` option computes the changes while ignoring\n-the amount of pure code movements within a file.  In other words,\n-rearranging lines in a file is not counted as much as other changes.\n-\n---dirstat-by-file[=<limit>]::\n-\tSame as `--dirstat`, but counts changed files instead of lines.\n+--\n+`changes`;;\n+\tCompute the dirstat numbers by counting the lines that have been\n+\tremoved from the source, or added to the destination. This ignores\n+\tthe amount of pure code movements within a file.  In other words,\n+\trearranging lines in a file is not counted as much as other changes.\n+\tThis is the default behavior when no parameter is given.\n+`files`;;\n+\tCompute the dirstat numbers by counting the number of files changed.\n+\tEach changed file counts equally in the dirstat analysis. This is\n+\tthe computationally cheapest `--dirstat` behavior, since it does\n+\tnot have to look at the file contents at all.\n+`cumulative`;;\n+\tCount changes in a child directory for the parent directory as well.\n+\tNote that when using `cumulative`, the sum of the percentages\n+\treported may exceed 100%. The default (non-cumulative) behavior can\n+\tbe specified with the `noncumulative` parameter.\n+<limit>;;\n+\tAn integer parameter specifies a cut-off percent (3% by default).\n+\tDirectories contributing less than this percentage of the changes\n+\tare not shown in the output.\n+--\n++\n+Example: The following will count changed files, while ignoring\n+directories with less than 10% of the total amount of changed files,\n+and accumulating child directory counts in the parent directories:\n+`--dirstat=files,10,cumulative`.\n \n --summary::\n \tOutput a condensed summary of extended header information\ndiff --git a/diff.c b/diff.c\nindex cfbfa92..0387e4f 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -66,6 +66,49 @@ static int parse_diff_color_slot(const char *var, int ofs)\n \treturn -1;\n }\n \n+#ifdef NO_C99_FORMAT\n+#define PD_FMT \"%d\"\n+#else\n+#define PD_FMT \"%td\"\n+#endif\n+\n+static void parse_dirstat_params(struct diff_options *options, const char *params)\n+{\n+\tconst char *p = params;\n+\twhile (*p) {\n+\t\tif (!prefixcmp(p, \"changes\")) {\n+\t\t\tp += 7;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"files\")) {\n+\t\t\tp += 5;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"noncumulative\")) {\n+\t\t\tp += 13;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"cumulative\")) {\n+\t\t\tp += 10;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n+\t\t}\n+\t\telse if (isdigit(*p)) {\n+\t\t\tchar *end;\n+\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n+\t\t\tp = end;\n+\t\t}\n+\t\telse\n+\t\t\tdie(\"Unknown --dirstat parameter '%s'\", p);\n+\n+\t\tif (*p) { /* more parameters, swallow separator */\n+\t\t\tif (*p != ',')\n+\t\t\t\tdie(\"Missing comma separator at char \" PD_FMT\n+\t\t\t\t    \" of '%s'\", p - params, params);\n+\t\t\tp++;\n+\t\t}\n+\t}\n+}\n+\n static int git_config_rename(const char *var, const char *value)\n {\n \tif (!value)\n@@ -3144,6 +3187,48 @@ static int stat_opt(struct diff_options *options, const char **av)\n \treturn argcount;\n }\n \n+/*\n+ * Parse dirstat-related options and any parameters given to those options.\n+ * Returns 1 if the option in 'arg' is a recognized dirstat-related option;\n+ * otherwise returns 0.\n+ */\n+static int dirstat_opt(struct diff_options *options, const char *arg)\n+{\n+\tconst char *p;\n+\tchar *mangled = NULL;\n+\n+\tif (!strcmp(arg, \"--cumulative\")) /* deprecated */\n+\t\t/* handle '--cumulative' like '--dirstat=cumulative' */\n+\t\tp = \"cumulative\";\n+\telse if (!strcmp(arg, \"--dirstat-by-file\") ||\n+\t\t !prefixcmp(arg, \"--dirstat-by-file=\")) { /* deprecated */\n+\t\t/* handle '--dirstat-by-file=*' like '--dirstat=files,*' */\n+\t\tmangled = xstrdup(arg + 12);\n+\t\tmemcpy(mangled, \"files\", 5);\n+\t\tif (mangled[5]) {\n+\t\t\tassert(mangled[5] == '=');\n+\t\t\tmangled[5] = ',';\n+\t\t}\n+\t\tp = mangled;\n+\t}\n+\telse if (!prefixcmp(arg, \"-X\"))\n+\t\tp = arg + 2;\n+\telse if (!prefixcmp(arg, \"--dirstat\"))\n+\t\tp = arg + 9;\n+\telse\n+\t\treturn 0;\n+\n+\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n+\n+\tif (*p)\n+\t\tif (*p == '=')\n+\t\t\tp++;\n+\t\tparse_dirstat_params(options, p);\n+\n+\tfree(mangled);\n+\treturn 1;\n+}\n+\n int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n {\n \tconst char *arg = av[0];\n@@ -3163,16 +3248,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \t\toptions->output_format |= DIFF_FORMAT_NUMSTAT;\n \telse if (!strcmp(arg, \"--shortstat\"))\n \t\toptions->output_format |= DIFF_FORMAT_SHORTSTAT;\n-\telse if (opt_arg(arg, 'X', \"dirstat\", &options->dirstat_percent))\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\telse if (!strcmp(arg, \"--cumulative\")) {\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n-\t} else if (opt_arg(arg, 0, \"dirstat-by-file\",\n-\t\t\t   &options->dirstat_percent)) {\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n-\t}\n+\telse if (!prefixcmp(arg, \"-X\") || !prefixcmp(arg, \"--dirstat\") ||\n+\t\t !strcmp(arg, \"--cumulative\"))\n+\t\t/* -X, --dirstat[=<args>], --dirstat-by-file, or --cumulative */\n+\t\treturn dirstat_opt(options, arg);\n \telse if (!strcmp(arg, \"--check\"))\n \t\toptions->output_format |= DIFF_FORMAT_CHECKDIFF;\n \telse if (!strcmp(arg, \"--summary\"))\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex 6ff7f9f..0ede619 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -346,6 +346,39 @@ test_expect_success 'vanilla -X' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'explicit defaults: --dirstat=changes,noncumulative,3' '\n+\tgit diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'explicit defaults: -Xchanges,noncumulative,3' '\n+\tgit diff -Xchanges,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -Xchanges,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -Xchanges,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'later options override earlier options:' '\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -449,6 +482,24 @@ test_expect_success '--dirstat=0 --cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=0,cumulative' '\n+\tgit diff --dirstat=0,cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success '-X0,cumulative' '\n+\tgit diff -X0,cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -X0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -X0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -491,6 +542,15 @@ test_expect_success '--dirstat-by-file' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files' '\n+\tgit diff --dirstat=files HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -525,6 +585,15 @@ test_expect_success '--dirstat-by-file=10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files,10' '\n+\tgit diff --dirstat=files,10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -577,4 +646,54 @@ test_expect_success '--dirstat-by-file --cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files,cumulative' '\n+\tgit diff --dirstat=files,cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+  27.2% dst/copy/\n+  27.2% dst/move/\n+  54.5% dst/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  42.8% dst/copy/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  28.5% dst/move/\n+  71.4% dst/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=files,cumulative,10' '\n+\tgit diff --dirstat=files,cumulative,10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166476","messageId":"1303892653-3958-5-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303892653-3958-1-git-send-email-johan@herland.net","subject":"[PATCHv3 4/6] Add config variable for specifying default --dirstat behavior","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T08:24:11Z","receivedAt":"2011-04-27T08:24:11Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"The new diff.dirstat config variable takes the same arguments as\n'--dirstat=<args>', and specifies the default arguments for --dirstat.\nThe config is obviously overridden by --dirstat arguments passed on the\ncommand line.\n\nWhen not specified, the --dirstat defaults are 'changes,noncumulative,3'.\n\nThe patch also adds several tests verifying the interaction between the\ndiff.dirstat config variable, and the --dirstat command line option.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/config.txt       |   36 ++++++++++++++++++++\n Documentation/diff-options.txt |    2 +\n diff.c                         |   10 +++++-\n t/t4046-diff-dirstat.sh        |   72 ++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 119 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6babbc7..c18dd5a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -822,6 +822,42 @@ diff.autorefreshindex::\n \taffects only 'git diff' Porcelain, and not lower level\n \t'diff' commands such as 'git diff-files'.\n \n+diff.dirstat::\n+\tA comma separated list of `--dirstat` parameters specifying the\n+\tdefault behavior of the `--dirstat` option to linkgit:git-diff[1]`\n+\tand friends. The defaults can be overridden on the command line\n+\t(using `--dirstat=<param1,param2,...>`). The fallback defaults\n+\t(when not changed by `diff.dirstat`) are `changes,noncumulative,3`.\n+\tThe following parameters are available:\n++\n+--\n+`changes`;;\n+\tCompute the dirstat numbers by counting the lines that have been\n+\tremoved from the source, or added to the destination. This ignores\n+\tthe amount of pure code movements within a file.  In other words,\n+\trearranging lines in a file is not counted as much as other changes.\n+\tThis is the default behavior when no parameter is given.\n+`files`;;\n+\tCompute the dirstat numbers by counting the number of files changed.\n+\tEach changed file counts equally in the dirstat analysis. This is\n+\tthe computationally cheapest `--dirstat` behavior, since it does\n+\tnot have to look at the file contents at all.\n+`cumulative`;;\n+\tCount changes in a child directory for the parent directory as well.\n+\tNote that when using `cumulative`, the sum of the percentages\n+\treported may exceed 100%. The default (non-cumulative) behavior can\n+\tbe specified with the `noncumulative` parameter.\n+<limit>;;\n+\tAn integer parameter specifies a cut-off percent (3% by default).\n+\tDirectories contributing less than this percentage of the changes\n+\tare not shown in the output.\n+--\n++\n+Example: The following will count changed files, while ignoring\n+directories with less than 10% of the total amount of changed files,\n+and accumulating child directory counts in the parent directories:\n+`files,10,cumulative`.\n+\n diff.external::\n \tIf this config variable is set, diff generation is not\n \tperformed using the internal diff machinery, but using the\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 6a3a9c1..4ad50b9 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -70,6 +70,8 @@ endif::git-format-patch[]\n \tOutput the distribution of relative amount of changes for each\n \tsub-directory. The behavior of `--dirstat` can be customized by\n \tpassing it a comma separated list of parameters.\n+\tThe defaults are controlled by the `diff.dirstat` configuration\n+\tvariable (see linkgit:git-config[1]).\n \tThe following parameters are available:\n +\n --\ndiff --git a/diff.c b/diff.c\nindex 0387e4f..1b6e8c0 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -31,6 +31,7 @@ static const char *external_diff_cmd_cfg;\n int diff_auto_refresh_index = 1;\n static int diff_mnemonic_prefix;\n static int diff_no_prefix;\n+static int diff_dirstat_percent_default = 3;\n static struct diff_options default_diff_options;\n \n static char diff_colors[][COLOR_MAXLEN] = {\n@@ -188,6 +189,13 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"diff.dirstat\")) {\n+\t\tdefault_diff_options.dirstat_percent = diff_dirstat_percent_default;\n+\t\tparse_dirstat_params(&default_diff_options, value);\n+\t\tdiff_dirstat_percent_default = default_diff_options.dirstat_percent;\n+\t\treturn 0;\n+\t}\n+\n \tif (!prefixcmp(var, \"submodule.\"))\n \t\treturn parse_submodule_config_option(var, value);\n \n@@ -2929,7 +2937,7 @@ void diff_setup(struct diff_options *options)\n \toptions->line_termination = '\\n';\n \toptions->break_opt = -1;\n \toptions->rename_limit = -1;\n-\toptions->dirstat_percent = 3;\n+\toptions->dirstat_percent = diff_dirstat_percent_default;\n \toptions->context = 3;\n \n \toptions->change = diff_change;\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex 0ede619..fa1885c 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -379,6 +379,15 @@ test_expect_success 'later options override earlier options:' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'non-defaults in config overridden by explicit defaults on command line' '\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -430,6 +439,15 @@ test_expect_success '-X0' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=0' '\n+\tgit -c diff.dirstat=0 diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0 diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0 diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -500,6 +518,24 @@ test_expect_success '-X0,cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=0,cumulative' '\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=0 & --dirstat=cumulative' '\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -551,6 +587,15 @@ test_expect_success '--dirstat=files' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=files' '\n+\tgit -c diff.dirstat=files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -594,6 +639,15 @@ test_expect_success '--dirstat=files,10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=10,files' '\n+\tgit -c diff.dirstat=10,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=10,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=10,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -655,6 +709,15 @@ test_expect_success '--dirstat=files,cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=cumulative,files' '\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -696,4 +759,13 @@ test_expect_success '--dirstat=files,cumulative,10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=10,cumulative,files' '\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166477","messageId":"1303892653-3958-6-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303892653-3958-1-git-send-email-johan@herland.net","subject":"[PATCHv3 5/6] Allow specifying --dirstat cut-off percentage as a floating point number","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T08:24:12Z","receivedAt":"2011-04-27T08:24:12Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Only the first digit after the decimal point is kept, as the dirstat\ncalculations all happen in permille.\n\nA selftest verifying floating-point percentage input has been added.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nImproved-by: Linus Torvalds <torvalds@linux-foundation.org>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n diff.c                  |   26 ++++++++++++++++----------\n diff.h                  |    2 +-\n t/t4046-diff-dirstat.sh |   37 +++++++++++++++++++++++++++++++++++++\n 3 files changed, 54 insertions(+), 11 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 1b6e8c0..3ca129c 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -31,7 +31,7 @@ static const char *external_diff_cmd_cfg;\n int diff_auto_refresh_index = 1;\n static int diff_mnemonic_prefix;\n static int diff_no_prefix;\n-static int diff_dirstat_percent_default = 3;\n+static int diff_dirstat_permille_default = 30;\n static struct diff_options default_diff_options;\n \n static char diff_colors[][COLOR_MAXLEN] = {\n@@ -95,8 +95,15 @@ static void parse_dirstat_params(struct diff_options *options, const char *param\n \t\t}\n \t\telse if (isdigit(*p)) {\n \t\t\tchar *end;\n-\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n+\t\t\toptions->dirstat_permille = strtoul(p, &end, 10) * 10;\n \t\t\tp = end;\n+\t\t\tif (*p == '.' && isdigit(*(++p))) {\n+\t\t\t\tint permille = strtoul(p, &end, 10);\n+\t\t\t\tp = end;\n+\t\t\t\twhile (permille >= 10)\n+\t\t\t\t\tpermille /= 10; /* only use first digit */\n+\t\t\t\toptions->dirstat_permille += permille;\n+\t\t\t}\n \t\t}\n \t\telse\n \t\t\tdie(\"Unknown --dirstat parameter '%s'\", p);\n@@ -190,9 +197,9 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)\n \t}\n \n \tif (!strcmp(var, \"diff.dirstat\")) {\n-\t\tdefault_diff_options.dirstat_percent = diff_dirstat_percent_default;\n+\t\tdefault_diff_options.dirstat_permille = diff_dirstat_permille_default;\n \t\tparse_dirstat_params(&default_diff_options, value);\n-\t\tdiff_dirstat_percent_default = default_diff_options.dirstat_percent;\n+\t\tdiff_dirstat_permille_default = default_diff_options.dirstat_permille;\n \t\treturn 0;\n \t}\n \n@@ -1504,7 +1511,7 @@ struct dirstat_file {\n \n struct dirstat_dir {\n \tstruct dirstat_file *files;\n-\tint alloc, nr, percent, cumulative;\n+\tint alloc, nr, permille, cumulative;\n };\n \n static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n@@ -1553,10 +1560,9 @@ static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n \tif (baselen && sources != 1) {\n \t\tif (this_dir) {\n \t\t\tint permille = this_dir * 1000 / changed;\n-\t\t\tint percent = permille / 10;\n-\t\t\tif (percent >= dir->percent) {\n+\t\t\tif (permille >= dir->permille) {\n \t\t\t\tfprintf(opt->file, \"%s%4d.%01d%% %.*s\\n\", line_prefix,\n-\t\t\t\t\tpercent, permille % 10, baselen, base);\n+\t\t\t\t\tpermille / 10, permille % 10, baselen, base);\n \t\t\t\tif (!dir->cumulative)\n \t\t\t\t\treturn 0;\n \t\t\t}\n@@ -1582,7 +1588,7 @@ static void show_dirstat(struct diff_options *options)\n \tdir.files = NULL;\n \tdir.alloc = 0;\n \tdir.nr = 0;\n-\tdir.percent = options->dirstat_percent;\n+\tdir.permille = options->dirstat_permille;\n \tdir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);\n \n \tchanged = 0;\n@@ -2937,7 +2943,7 @@ void diff_setup(struct diff_options *options)\n \toptions->line_termination = '\\n';\n \toptions->break_opt = -1;\n \toptions->rename_limit = -1;\n-\toptions->dirstat_percent = diff_dirstat_percent_default;\n+\toptions->dirstat_permille = diff_dirstat_permille_default;\n \toptions->context = 3;\n \n \toptions->change = diff_change;\ndiff --git a/diff.h b/diff.h\nindex 0083d92..08b4fe0 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -111,7 +111,7 @@ struct diff_options {\n \tint rename_score;\n \tint rename_limit;\n \tint warn_on_too_large_rename;\n-\tint dirstat_percent;\n+\tint dirstat_permille;\n \tint setup;\n \tint abbrev;\n \tconst char *prefix;\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex fa1885c..cf75a38 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -768,4 +768,41 @@ test_expect_success 'diff.dirstat=10,cumulative,files' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+cat <<EOF >expect_diff_dirstat\n+  27.2% dst/copy/\n+  27.2% dst/move/\n+  54.5% dst/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  42.8% dst/copy/\n+  28.5% dst/move/\n+  71.4% dst/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  33.3% dst/copy/\n+  33.3% dst/move/\n+  66.6% dst/\n+EOF\n+\n+test_expect_success '--dirstat=files,cumulative,16.7' '\n+\tgit diff --dirstat=files,cumulative,16.7 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,16.7 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,16.7 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=16.7,cumulative,files' '\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166478","messageId":"1303892653-3958-7-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303892653-3958-1-git-send-email-johan@herland.net","subject":"[PATCHv3 6/6] New --dirstat=lines mode, doing dirstat analysis based on diffstat","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T08:24:13Z","receivedAt":"2011-04-27T08:24:13Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"This patch adds an alternative implementation of show_dirstat(), called\nshow_dirstat_by_line(), which uses the more expensive diffstat analysis\n(as opposed to show_dirstat()'s own (relatively inexpensive) analysis)\nto derive the numbers from which the --dirstat output is computed.\n\nThe alternative implementation is controlled by the new \"lines\" parameter\nto the --dirstat option (or the diff.dirstat config variable).\n\nFor binary files, the diffstat analysis counts bytes instead of lines,\nso to prevent binary files from dominating the dirstat results, the\nbyte counts for binary files are divided by 64 before being compared to\ntheir textual/line-based counterparts. This is a stupid and ugly - but\nvery cheap - heuristic.\n\nIn linux-2.6.git, running the three different --dirstat modes:\n\n  time git diff v2.6.20..v2.6.30 --dirstat=changes > /dev/null\nvs.\n  time git diff v2.6.20..v2.6.30 --dirstat=lines > /dev/null\nvs.\n  time git diff v2.6.20..v2.6.30 --dirstat=files > /dev/null\n\nyields the following average runtimes on my machine:\n\n - \"changes\" (default): ~6.0 s\n - \"lines\":             ~9.6 s\n - \"files\":             ~0.1 s\n\nSo, as expected, there's a considerable performance hit (~60%) by going\nthrough the full diffstat analysis as compared to the default \"changes\"\nanalysis (obviously, \"files\" is much faster than both). As such, the\n\"lines\" mode is probably only useful if you really need the --dirstat\nnumbers to be consistent with the numbers returned from the other\n--*stat options.\n\nThe patch also includes documentation and tests for the new dirstat mode.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/config.txt       |    8 +++\n Documentation/diff-options.txt |    8 +++\n diff.c                         |   62 ++++++++++++++++++++++++-\n diff.h                         |    1 +\n t/t4046-diff-dirstat.sh        |  100 ++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 177 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex c18dd5a..0cad75c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -837,6 +837,14 @@ diff.dirstat::\n \tthe amount of pure code movements within a file.  In other words,\n \trearranging lines in a file is not counted as much as other changes.\n \tThis is the default behavior when no parameter is given.\n+`lines`;;\n+\tCompute the dirstat numbers by doing the regular line-based diff\n+\tanalysis, and summing the removed/added line counts. (For binary\n+\tfiles, count 64-byte chunks instead, since binary files have no\n+\tnatural concept of lines). This is a more expensive `--dirstat`\n+\tbehavior than the `changes` behavior, but it does count rearranged\n+\tlines within a file as much as other changes. The resulting output\n+\tis consistent with what you get from the other `--*stat` options.\n `files`;;\n \tCompute the dirstat numbers by counting the number of files changed.\n \tEach changed file counts equally in the dirstat analysis. This is\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 4ad50b9..327d10a 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -81,6 +81,14 @@ endif::git-format-patch[]\n \tthe amount of pure code movements within a file.  In other words,\n \trearranging lines in a file is not counted as much as other changes.\n \tThis is the default behavior when no parameter is given.\n+`lines`;;\n+\tCompute the dirstat numbers by doing the regular line-based diff\n+\tanalysis, and summing the removed/added line counts. (For binary\n+\tfiles, count 64-byte chunks instead, since binary files have no\n+\tnatural concept of lines). This is a more expensive `--dirstat`\n+\tbehavior than the `changes` behavior, but it does count rearranged\n+\tlines within a file as much as other changes. The resulting output\n+\tis consistent with what you get from the other `--*stat` options.\n `files`;;\n \tCompute the dirstat numbers by counting the number of files changed.\n \tEach changed file counts equally in the dirstat analysis. This is\ndiff --git a/diff.c b/diff.c\nindex 3ca129c..cefa145 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -79,10 +79,17 @@ static void parse_dirstat_params(struct diff_options *options, const char *param\n \twhile (*p) {\n \t\tif (!prefixcmp(p, \"changes\")) {\n \t\t\tp += 7;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_LINE);\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n+\t\t}\n+\t\telse if (!prefixcmp(p, \"lines\")) {\n+\t\t\tp += 5;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n \t\t}\n \t\telse if (!prefixcmp(p, \"files\")) {\n \t\t\tp += 5;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n \t\t}\n \t\telse if (!prefixcmp(p, \"noncumulative\")) {\n@@ -1677,6 +1684,50 @@ found_damage:\n \tgather_dirstat(options, &dir, changed, \"\", 0);\n }\n \n+static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *options)\n+{\n+\tint i;\n+\tunsigned long changed;\n+\tstruct dirstat_dir dir;\n+\n+\tif (data->nr == 0)\n+\t\treturn;\n+\n+\tdir.files = NULL;\n+\tdir.alloc = 0;\n+\tdir.nr = 0;\n+\tdir.permille = options->dirstat_permille;\n+\tdir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);\n+\n+\tchanged = 0;\n+\tfor (i = 0; i < data->nr; i++) {\n+\t\tstruct diffstat_file *file = data->files[i];\n+\t\tunsigned long damage = file->added + file->deleted;\n+\t\tif (file->is_binary)\n+\t\t\t/*\n+\t\t\t * binary files counts bytes, not lines. Must find some\n+\t\t\t * way to normalize binary bytes vs. textual lines.\n+\t\t\t * The following heuristic assumes that there are 64\n+\t\t\t * bytes per \"line\".\n+\t\t\t * This is stupid and ugly, but very cheap...\n+\t\t\t */\n+\t\t\tdamage = (damage + 63) / 64;\n+\t\tALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);\n+\t\tdir.files[dir.nr].name = file->name;\n+\t\tdir.files[dir.nr].changed = damage;\n+\t\tchanged += damage;\n+\t\tdir.nr++;\n+\t}\n+\n+\t/* This can happen even with many files, if everything was renames */\n+\tif (!changed)\n+\t\treturn;\n+\n+\t/* Show all directories with more than x% of the changes */\n+\tqsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);\n+\tgather_dirstat(options, &dir, changed, \"\", 0);\n+}\n+\n static void free_diffstat_info(struct diffstat_t *diffstat)\n {\n \tint i;\n@@ -4086,6 +4137,7 @@ void diff_flush(struct diff_options *options)\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tint i, output_format = options->output_format;\n \tint separator = 0;\n+\tint dirstat_by_line = 0;\n \n \t/*\n \t * Order: raw, stat, summary, patch\n@@ -4106,7 +4158,11 @@ void diff_flush(struct diff_options *options)\n \t\tseparator++;\n \t}\n \n-\tif (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {\n+\tif (output_format & DIFF_FORMAT_DIRSTAT && DIFF_OPT_TST(options, DIRSTAT_BY_LINE))\n+\t\tdirstat_by_line = 1;\n+\n+\tif (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT) ||\n+\t    dirstat_by_line) {\n \t\tstruct diffstat_t diffstat;\n \n \t\tmemset(&diffstat, 0, sizeof(struct diffstat_t));\n@@ -4121,10 +4177,12 @@ void diff_flush(struct diff_options *options)\n \t\t\tshow_stats(&diffstat, options);\n \t\tif (output_format & DIFF_FORMAT_SHORTSTAT)\n \t\t\tshow_shortstats(&diffstat, options);\n+\t\tif (output_format & DIFF_FORMAT_DIRSTAT)\n+\t\t\tshow_dirstat_by_line(&diffstat, options);\n \t\tfree_diffstat_info(&diffstat);\n \t\tseparator++;\n \t}\n-\tif (output_format & DIFF_FORMAT_DIRSTAT)\n+\tif ((output_format & DIFF_FORMAT_DIRSTAT) && !dirstat_by_line)\n \t\tshow_dirstat(options);\n \n \tif (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {\ndiff --git a/diff.h b/diff.h\nindex 08b4fe0..1a8b685 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -78,6 +78,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)\n #define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)\n #define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)\n #define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)\n+#define DIFF_OPT_DIRSTAT_BY_LINE     (1 << 28)\n \n #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)\n #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex cf75a38..9f8ecf1 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -805,4 +805,104 @@ test_expect_success 'diff.dirstat=16.7,cumulative,files' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+cat <<EOF >expect_diff_dirstat\n+  10.6% dst/copy/changed/\n+  10.6% dst/copy/rearranged/\n+  10.6% dst/copy/unchanged/\n+  10.6% dst/move/changed/\n+  10.6% dst/move/rearranged/\n+  10.6% dst/move/unchanged/\n+  10.6% src/move/changed/\n+  10.6% src/move/rearranged/\n+  10.6% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.2% changed/\n+  26.3% dst/copy/changed/\n+  26.3% dst/copy/rearranged/\n+  26.3% dst/copy/unchanged/\n+   5.2% dst/move/changed/\n+   5.2% dst/move/rearranged/\n+   5.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=lines' '\n+\tgit diff --dirstat=lines HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=lines -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=lines -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=lines' '\n+\tgit -c diff.dirstat=lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.6% dst/copy/changed/\n+  10.6% dst/copy/rearranged/\n+  10.6% dst/copy/unchanged/\n+  10.6% dst/move/changed/\n+  10.6% dst/move/rearranged/\n+  10.6% dst/move/unchanged/\n+   2.1% rearranged/\n+  10.6% src/move/changed/\n+  10.6% src/move/rearranged/\n+  10.6% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.2% changed/\n+  26.3% dst/copy/changed/\n+  26.3% dst/copy/rearranged/\n+  26.3% dst/copy/unchanged/\n+   5.2% dst/move/changed/\n+   5.2% dst/move/rearranged/\n+   5.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=lines,0' '\n+\tgit diff --dirstat=lines,0 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=lines,0 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=lines,0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=0,lines' '\n+\tgit -c diff.dirstat=0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166482","messageId":"BANLkTi=xJXFKwzu4cCZAW_T2dXQ8wOq70w@mail.gmail.com","threadId":"27015","inReplyTo":"1303892653-3958-6-git-send-email-johan@herland.net","subject":"Re: [PATCHv3 5/6] Allow specifying --dirstat cut-off percentage as a floating point number","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2011-04-27T08:37:17Z","receivedAt":"2011-04-27T08:37:17Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Wed, Apr 27, 2011 at 1:24 AM, Johan Herland <johan@herland.net> wrote:\n> +                       options->dirstat_permille = strtoul(p, &end, 10) * 10;\n>                        p = end;\n> +                       if (*p == '.' && isdigit(*(++p))) {\n> +                               int permille = strtoul(p, &end, 10);\n> +                               p = end;\n> +                               while (permille >= 10)\n> +                                       permille /= 10; /* only use first digit */\n> +                               options->dirstat_permille += permille;\n> +                       }\n\nHeh. That's both unnecessarily complicated, and doesn't work.\n\nIt gets the wrong answer for something like \"0.0001\", since 'permille'\nin that case ends up starting out as '0001', ie just 1, so you never\nactually do that whole while-loop.\n\nSo the right approach is just something like\n\n  if (*p == '.' && isdigit(*++p)) {\n    /* only use first digit */\n    options->dirstat_permille += *p - '0';\n    /* .. and ignore any further digits */\n    while (isdigit(*++p))\n      /* nothing */;\n  }\n\n(totally untested, of course)\n\n                    Linus\n"},{"id":"166497","messageId":"201104271229.25478.johan@herland.net","threadId":"27015","inReplyTo":"BANLkTi=xJXFKwzu4cCZAW_T2dXQ8wOq70w@mail.gmail.com","subject":"[PATCHv4 5/6] Allow specifying --dirstat cut-off percentage as a floating point number","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-27T10:29:25Z","receivedAt":"2011-04-27T10:29:25Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Only the first digit after the decimal point is kept, as the dirstat\ncalculations all happen in permille.\n\nSelftests verifying floating-point percentage input has been added.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nImproved-by: Linus Torvalds <torvalds@linux-foundation.org>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n\nOn Wednesday 27 April 2011, Linus Torvalds wrote:\n> On Wed, Apr 27, 2011 at 1:24 AM, Johan Herland <johan@herland.net> wrote:\n> > +                       options->dirstat_permille = strtoul(p, &end, 10) * 10;\n> > +                       p = end;\n> > +                       if (*p == '.' && isdigit(*(++p))) {\n> > +                               int permille = strtoul(p, &end, 10);\n> > +                               p = end;\n> > +                               while (permille >= 10)\n> > +                                       permille /= 10; /* only use first digit */\n> > +                               options->dirstat_permille += permille;\n> > +                       }\n>\n> Heh. That's both unnecessarily complicated, and doesn't work.\n>\n> It gets the wrong answer for something like \"0.0001\", since\n> 'permille' in that case ends up starting out as '0001', ie just 1, so\n> you never actually do that whole while-loop.\n\n*facepalm* Of course. That'll teach me not to code before breakfast...\n\n> So the right approach is just something like\n>\n>   if (*p == '.' && isdigit(*++p)) {\n>     /* only use first digit */\n>     options->dirstat_permille += *p - '0';\n>     /* .. and ignore any further digits */\n>     while (isdigit(*++p))\n>       /* nothing */;\n>   }\n>\n> (totally untested, of course)\n\nHere it is, tested and everything. :)\n\n\nThanks! :)\n\n...Johan\n\n diff.c                  |   26 +++++++++++-------\n diff.h                  |    2 +-\n t/t4046-diff-dirstat.sh |   64 +++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 81 insertions(+), 11 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 1b6e8c0..2cd815c 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -31,7 +31,7 @@ static const char *external_diff_cmd_cfg;\n int diff_auto_refresh_index = 1;\n static int diff_mnemonic_prefix;\n static int diff_no_prefix;\n-static int diff_dirstat_percent_default = 3;\n+static int diff_dirstat_permille_default = 30;\n static struct diff_options default_diff_options;\n \n static char diff_colors[][COLOR_MAXLEN] = {\n@@ -95,8 +95,15 @@ static void parse_dirstat_params(struct diff_options *options, const char *param\n \t\t}\n \t\telse if (isdigit(*p)) {\n \t\t\tchar *end;\n-\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n+\t\t\toptions->dirstat_permille = strtoul(p, &end, 10) * 10;\n \t\t\tp = end;\n+\t\t\tif (*p == '.' && isdigit(*++p)) {\n+\t\t\t\t/* only use first digit */\n+\t\t\t\toptions->dirstat_permille += *p - '0';\n+\t\t\t\t/* .. and ignore any further digits */\n+\t\t\t\twhile (isdigit(*++p))\n+\t\t\t\t\t/* nothing */;\n+\t\t\t}\n \t\t}\n \t\telse\n \t\t\tdie(\"Unknown --dirstat parameter '%s'\", p);\n@@ -190,9 +197,9 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)\n \t}\n \n \tif (!strcmp(var, \"diff.dirstat\")) {\n-\t\tdefault_diff_options.dirstat_percent = diff_dirstat_percent_default;\n+\t\tdefault_diff_options.dirstat_permille = diff_dirstat_permille_default;\n \t\tparse_dirstat_params(&default_diff_options, value);\n-\t\tdiff_dirstat_percent_default = default_diff_options.dirstat_percent;\n+\t\tdiff_dirstat_permille_default = default_diff_options.dirstat_permille;\n \t\treturn 0;\n \t}\n \n@@ -1504,7 +1511,7 @@ struct dirstat_file {\n \n struct dirstat_dir {\n \tstruct dirstat_file *files;\n-\tint alloc, nr, percent, cumulative;\n+\tint alloc, nr, permille, cumulative;\n };\n \n static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n@@ -1553,10 +1560,9 @@ static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n \tif (baselen && sources != 1) {\n \t\tif (this_dir) {\n \t\t\tint permille = this_dir * 1000 / changed;\n-\t\t\tint percent = permille / 10;\n-\t\t\tif (percent >= dir->percent) {\n+\t\t\tif (permille >= dir->permille) {\n \t\t\t\tfprintf(opt->file, \"%s%4d.%01d%% %.*s\\n\", line_prefix,\n-\t\t\t\t\tpercent, permille % 10, baselen, base);\n+\t\t\t\t\tpermille / 10, permille % 10, baselen, base);\n \t\t\t\tif (!dir->cumulative)\n \t\t\t\t\treturn 0;\n \t\t\t}\n@@ -1582,7 +1588,7 @@ static void show_dirstat(struct diff_options *options)\n \tdir.files = NULL;\n \tdir.alloc = 0;\n \tdir.nr = 0;\n-\tdir.percent = options->dirstat_percent;\n+\tdir.permille = options->dirstat_permille;\n \tdir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);\n \n \tchanged = 0;\n@@ -2937,7 +2943,7 @@ void diff_setup(struct diff_options *options)\n \toptions->line_termination = '\\n';\n \toptions->break_opt = -1;\n \toptions->rename_limit = -1;\n-\toptions->dirstat_percent = diff_dirstat_percent_default;\n+\toptions->dirstat_permille = diff_dirstat_permille_default;\n \toptions->context = 3;\n \n \toptions->change = diff_change;\ndiff --git a/diff.h b/diff.h\nindex 0083d92..08b4fe0 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -111,7 +111,7 @@ struct diff_options {\n \tint rename_score;\n \tint rename_limit;\n \tint warn_on_too_large_rename;\n-\tint dirstat_percent;\n+\tint dirstat_permille;\n \tint setup;\n \tint abbrev;\n \tconst char *prefix;\ndiff --git a/t/t4046-diff-dirstat.sh b/t/t4046-diff-dirstat.sh\nindex fa1885c..b3062b4 100755\n--- a/t/t4046-diff-dirstat.sh\n+++ b/t/t4046-diff-dirstat.sh\n@@ -768,4 +768,68 @@ test_expect_success 'diff.dirstat=10,cumulative,files' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+cat <<EOF >expect_diff_dirstat\n+  27.2% dst/copy/\n+  27.2% dst/move/\n+  54.5% dst/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  42.8% dst/copy/\n+  28.5% dst/move/\n+  71.4% dst/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  33.3% dst/copy/\n+  33.3% dst/move/\n+  66.6% dst/\n+EOF\n+\n+test_expect_success '--dirstat=files,cumulative,16.7' '\n+\tgit diff --dirstat=files,cumulative,16.7 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,16.7 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,16.7 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=16.7,cumulative,files' '\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=16.70,cumulative,files' '\n+\tgit -c diff.dirstat=16.70,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=16.70,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=16.70,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success '--dirstat=files,cumulative,27.2' '\n+\tgit diff --dirstat=files,cumulative,27.2 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,27.2 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,27.2 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success '--dirstat=files,cumulative,27.09' '\n+\tgit diff --dirstat=files,cumulative,27.09 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,27.09 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,27.09 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1\n"},{"id":"166555","messageId":"7vaafb76zb.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"201104270402.10658.johan@herland.net","subject":"Re: [PATCH 3/6] Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-27T20:51:52Z","receivedAt":"2011-04-27T20:51:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n>> Even better, probably they can be left to diff_opt_parse() without\n>> calling this function, as you are deprecating them and do not have to\n>> allow them to take the opt1,opt2,... form of parameter.\n>\n> I understand, but politely disagree: Patch 6/6 complicates the logic\n> that DIFF_OPT_SET()/CLR() various bits in the diff options. I'd rather\n> keep that logic in one place, than duplicate it into diff_opt_parse().\n\nI've given a brief look at the v3.  Looks better than the previous one;\nnot using double is especailly a big and good thing.\n\nThere still are a few things I noticed, two of which I'd attempt to show\nhow to fix in the attached patch (on top of the whole 6 patch series, as I\ndon't have time to break it down and I know you are capable enough to do\nso yourself).\n\n * We eradicated the use of C99_FORMAT at 28bd70d (unbreak and eliminate\n   NO_C99_FORMAT, 2011-03-16) and \"%td\" at 31d713d (mktag: avoid %td in\n   format string, 2011-03-16).\n\n * parse_dirstat_params() can die when it sees an input that it does not\n   understand, instead of silently returning and indicating an error by\n   its return value, even when it is called from the codepath to read the\n   configuration files.  Dying upon an erroneous command line argument is\n   fine and diagnosing it is preferred, but the configuration parser\n   should ignore values that it does not understand (may want to warn) so\n   that you can keep using older git (i.e. the version resulting from your\n   patch) in a repository you usually use newer git that supports even\n   more features with its --dirstat option.\n\n * Temporary memory allocation in your dirstat_opt() to handle commonly\n   used shorthand stands out as a sore thumb.\n\n * The parsing implemented in dirstat_opt() is a bit too loose.  For\n   example, we never accepted \"-X=3\" nor \"--dirstat40\" but I suspect your\n   parser would.  Accepting the former might not be such a big deal, but\n   not the latter.\n\nThe attached is not a complete fix-up, but addresses the last two issues,\nand it also should be a good starting point for the second issue.\n\nI tried not to fix style issues, but parse_dirstat_params() should follow\n\n\tif (...) {\n            ... compound ...\n\t} else if (...) {\n            ... compound ...\n\t} else if (...) {\n\t    ...\n\ni.e. close brace just before the \"else if\" on the same line.\n\n diff.c |   94 +++++++++++++++++++++++++++++----------------------------------\n 1 files changed, 43 insertions(+), 51 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 9008e88..7c6a8d1 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -73,9 +73,14 @@ static int parse_diff_color_slot(const char *var, int ofs)\n #define PD_FMT \"%td\"\n #endif\n \n-static void parse_dirstat_params(struct diff_options *options, const char *params)\n+static int parse_dirstat_params(struct diff_options *options, const char *params,\n+\t\t\t\tint die_on_error)\n {\n \tconst char *p = params;\n+\tconst char *unknown_param_error = \"Unknown --dirstat parameter '%s'\";\n+\tconst char *missing_comma_error = \"Missing comma separator at char \" PD_FMT\n+\t\t\" of '%s'\";\n+\n \twhile (*p) {\n \t\tif (!prefixcmp(p, \"changes\")) {\n \t\t\tp += 7;\n@@ -109,19 +114,27 @@ static void parse_dirstat_params(struct diff_options *options, const char *param\n \t\t\t\toptions->dirstat_permille += *p - '0';\n \t\t\t\t/* .. and ignore any further digits */\n \t\t\t\twhile (isdigit(*++p))\n-\t\t\t\t\t/* nothing */;\n+\t\t\t\t\t; /* nothing */\n \t\t\t}\n+\t\t} else if (die_on_error) {\n+\t\t\tdie(unknown_param_error, p);\n+\t\t} else {\n+\t\t\treturn error(unknown_param_error, p);\n \t\t}\n-\t\telse\n-\t\t\tdie(\"Unknown --dirstat parameter '%s'\", p);\n \n-\t\tif (*p) { /* more parameters, swallow separator */\n-\t\t\tif (*p != ',')\n-\t\t\t\tdie(\"Missing comma separator at char \" PD_FMT\n-\t\t\t\t    \" of '%s'\", p - params, params);\n-\t\t\tp++;\n+\t\tif (*p) {\n+\t\t\t/* more parameters, swallow separator */\n+\t\t\tif (*p == ',') {\n+\t\t\t\tp++;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (die_on_error)\n+\t\t\t\tdie(missing_comma_error, p - params, params);\n+\t\t\telse\n+\t\t\t\treturn error(missing_comma_error, p - params, params);\n \t\t}\n \t}\n+\treturn 0;\n }\n \n static int git_config_rename(const char *var, const char *value)\n@@ -205,7 +218,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)\n \n \tif (!strcmp(var, \"diff.dirstat\")) {\n \t\tdefault_diff_options.dirstat_permille = diff_dirstat_permille_default;\n-\t\tparse_dirstat_params(&default_diff_options, value);\n+\t\t(void) parse_dirstat_params(&default_diff_options, value, 0);\n \t\tdiff_dirstat_permille_default = default_diff_options.dirstat_permille;\n \t\treturn 0;\n \t}\n@@ -3252,45 +3265,14 @@ static int stat_opt(struct diff_options *options, const char **av)\n \treturn argcount;\n }\n \n-/*\n- * Parse dirstat-related options and any parameters given to those options.\n- * Returns 1 if the option in 'arg' is a recognized dirstat-related option;\n- * otherwise returns 0.\n- */\n-static int dirstat_opt(struct diff_options *options, const char *arg)\n+static int parse_dirstat_opt(struct diff_options *options, const char *params)\n {\n-\tconst char *p;\n-\tchar *mangled = NULL;\n-\n-\tif (!strcmp(arg, \"--cumulative\")) /* deprecated */\n-\t\t/* handle '--cumulative' like '--dirstat=cumulative' */\n-\t\tp = \"cumulative\";\n-\telse if (!strcmp(arg, \"--dirstat-by-file\") ||\n-\t\t !prefixcmp(arg, \"--dirstat-by-file=\")) { /* deprecated */\n-\t\t/* handle '--dirstat-by-file=*' like '--dirstat=files,*' */\n-\t\tmangled = xstrdup(arg + 12);\n-\t\tmemcpy(mangled, \"files\", 5);\n-\t\tif (mangled[5]) {\n-\t\t\tassert(mangled[5] == '=');\n-\t\t\tmangled[5] = ',';\n-\t\t}\n-\t\tp = mangled;\n-\t}\n-\telse if (!prefixcmp(arg, \"-X\"))\n-\t\tp = arg + 2;\n-\telse if (!prefixcmp(arg, \"--dirstat\"))\n-\t\tp = arg + 9;\n-\telse\n-\t\treturn 0;\n-\n+\tparse_dirstat_params(options, params, 1);\n+\t/*\n+\t * The caller knows a dirstat-related option is given from the command\n+\t * line; allow it to say \"return this_function();\"\n+\t */\n \toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\n-\tif (*p)\n-\t\tif (*p == '=')\n-\t\t\tp++;\n-\t\tparse_dirstat_params(options, p);\n-\n-\tfree(mangled);\n \treturn 1;\n }\n \n@@ -3313,10 +3295,20 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \t\toptions->output_format |= DIFF_FORMAT_NUMSTAT;\n \telse if (!strcmp(arg, \"--shortstat\"))\n \t\toptions->output_format |= DIFF_FORMAT_SHORTSTAT;\n-\telse if (!prefixcmp(arg, \"-X\") || !prefixcmp(arg, \"--dirstat\") ||\n-\t\t !strcmp(arg, \"--cumulative\"))\n-\t\t/* -X, --dirstat[=<args>], --dirstat-by-file, or --cumulative */\n-\t\treturn dirstat_opt(options, arg);\n+\telse if (!strcmp(arg, \"-X\") || !strcmp(arg, \"--dirstat\"))\n+\t\treturn parse_dirstat_opt(options, \"\");\n+\telse if (!prefixcmp(arg, \"-X\"))\n+\t\treturn parse_dirstat_opt(options, arg + 2);\n+\telse if (!prefixcmp(arg, \"--dirstat=\"))\n+\t\treturn parse_dirstat_opt(options, arg + 10);\n+\telse if (!strcmp(arg, \"--cumulative\"))\n+\t\treturn parse_dirstat_opt(options, \"cumulative\");\n+\telse if (!strcmp(arg, \"--dirstat-by-file\"))\n+\t\treturn parse_dirstat_opt(options, \"files\");\n+\telse if (!prefixcmp(arg, \"--dirstat-by-file=\")) {\n+\t\tparse_dirstat_opt(options, \"files\");\n+\t\treturn parse_dirstat_opt(options, arg + 18);\n+\t}\n \telse if (!strcmp(arg, \"--check\"))\n \t\toptions->output_format |= DIFF_FORMAT_CHECKDIFF;\n \telse if (!strcmp(arg, \"--summary\"))\n"},{"id":"166556","messageId":"7v62pz76im.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"7vaafb76zb.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 3/6] Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-27T21:01:53Z","receivedAt":"2011-04-27T21:01:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Johan Herland <johan@herland.net> writes:\n>\n>>> Even better, probably they can be left to diff_opt_parse() without\n>>> calling this function, as you are deprecating them and do not have to\n>>> allow them to take the opt1,opt2,... form of parameter.\n>>\n>> I understand, but politely disagree: Patch 6/6 complicates the logic\n>> that DIFF_OPT_SET()/CLR() various bits in the diff options. I'd rather\n>> keep that logic in one place, than duplicate it into diff_opt_parse().\n>\n> I've given a brief look at the v3.  Looks better than the previous one;\n> not using double is especailly a big and good thing.\n\nAlso, I had to rename the new test to t4047 to avoid crashing with an\nexisting test when merged to 'pu'.\n"},{"id":"166583","messageId":"1303953442-26536-1-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303892653-3958-1-git-send-email-johan@herland.net","subject":"[PATCHv5 0/7] --dirstat fixes, part 2","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-28T01:17:15Z","receivedAt":"2011-04-28T01:17:15Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Hi,\n\nHere's version 5 incorporating Junio's feedback and patch from elsewhere\nin this thread.\n\nHave fun! :)\n\n...Johan\n\n\nJohan Herland (7):\n  Add several testcases for --dirstat and friends\n  Make --dirstat=0 output directories that contribute < 0.1% of changes\n  Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file\n  Add config variable for specifying default --dirstat behavior\n  Allow specifying --dirstat cut-off percentage as a floating point number\n  New --dirstat=lines mode, doing dirstat analysis based on diffstat\n  Improve error handling when parsing dirstat parameters\n\n Documentation/config.txt       |   44 ++\n Documentation/diff-options.txt |   54 ++-\n diff.c                         |  158 ++++++-\n diff.h                         |    3 +-\n t/t4047-diff-dirstat.sh        |  965 ++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 1192 insertions(+), 32 deletions(-)\n create mode 100755 t/t4047-diff-dirstat.sh\n\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166587","messageId":"1303953442-26536-2-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303953442-26536-1-git-send-email-johan@herland.net","subject":"[PATCHv5 1/7] Add several testcases for --dirstat and friends","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-28T01:17:16Z","receivedAt":"2011-04-28T01:17:16Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Currently, t4013 is the only selftest that exercises the --dirstat machinery,\nbut it only does a superficial verification of --dirstat's output.\n\nThis patch adds a new selftest - t4047-diff-dirstat.sh - which prepares a\ncommit containing:\n - unchanged files, changed files and files with rearranged lines\n - copied files, moved files, and unmoved files\n\nIt then verifies the correct dirstat output for that commit in the following\ndirstat modes:\n - --dirstat\n - -X\n - --dirstat=0\n - -X0\n - --cumulative\n - --dirstat-by-file\n - (plus combinations of the above)\n\nEach of the above tests are also run with:\n - no rename detection\n - rename detection (-M)\n - expensive copy detection (-C -C)\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n t/t4047-diff-dirstat.sh |  580 +++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 580 insertions(+), 0 deletions(-)\n create mode 100755 t/t4047-diff-dirstat.sh\n\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nnew file mode 100755\nindex 0000000..eb6bf47\n--- /dev/null\n+++ b/t/t4047-diff-dirstat.sh\n@@ -0,0 +1,580 @@\n+#!/bin/sh\n+\n+test_description='diff --dirstat tests'\n+. ./test-lib.sh\n+\n+# set up two commits where the second commit has these files\n+# (10 lines in each file):\n+#\n+#   unchanged/text           (unchanged from 1st commit)\n+#   changed/text             (changed 1st line)\n+#   rearranged/text          (swapped 1st and 2nd line)\n+#   dst/copy/unchanged/text  (copied from src/copy/unchanged/text, unchanged)\n+#   dst/copy/changed/text    (copied from src/copy/changed/text, changed)\n+#   dst/copy/rearranged/text (copied from src/copy/rearranged/text, rearranged)\n+#   dst/move/unchanged/text  (moved from src/move/unchanged/text, unchanged)\n+#   dst/move/changed/text    (moved from src/move/changed/text, changed)\n+#   dst/move/rearranged/text (moved from src/move/rearranged/text, rearranged)\n+\n+test_expect_success 'setup' '\n+\tmkdir unchanged &&\n+\tmkdir changed &&\n+\tmkdir rearranged &&\n+\tmkdir src &&\n+\tmkdir src/copy &&\n+\tmkdir src/copy/unchanged &&\n+\tmkdir src/copy/changed &&\n+\tmkdir src/copy/rearranged &&\n+\tmkdir src/move &&\n+\tmkdir src/move/unchanged &&\n+\tmkdir src/move/changed &&\n+\tmkdir src/move/rearranged &&\n+\tcat <<EOF >unchanged/text &&\n+unchanged       line #0\n+unchanged       line #1\n+unchanged       line #2\n+unchanged       line #3\n+unchanged       line #4\n+unchanged       line #5\n+unchanged       line #6\n+unchanged       line #7\n+unchanged       line #8\n+unchanged       line #9\n+EOF\n+\tcat <<EOF >changed/text &&\n+changed         line #0\n+changed         line #1\n+changed         line #2\n+changed         line #3\n+changed         line #4\n+changed         line #5\n+changed         line #6\n+changed         line #7\n+changed         line #8\n+changed         line #9\n+EOF\n+\tcat <<EOF >rearranged/text &&\n+rearranged      line #0\n+rearranged      line #1\n+rearranged      line #2\n+rearranged      line #3\n+rearranged      line #4\n+rearranged      line #5\n+rearranged      line #6\n+rearranged      line #7\n+rearranged      line #8\n+rearranged      line #9\n+EOF\n+\tcat <<EOF >src/copy/unchanged/text &&\n+copy  unchanged line #0\n+copy  unchanged line #1\n+copy  unchanged line #2\n+copy  unchanged line #3\n+copy  unchanged line #4\n+copy  unchanged line #5\n+copy  unchanged line #6\n+copy  unchanged line #7\n+copy  unchanged line #8\n+copy  unchanged line #9\n+EOF\n+\tcat <<EOF >src/copy/changed/text &&\n+copy    changed line #0\n+copy    changed line #1\n+copy    changed line #2\n+copy    changed line #3\n+copy    changed line #4\n+copy    changed line #5\n+copy    changed line #6\n+copy    changed line #7\n+copy    changed line #8\n+copy    changed line #9\n+EOF\n+\tcat <<EOF >src/copy/rearranged/text &&\n+copy rearranged line #0\n+copy rearranged line #1\n+copy rearranged line #2\n+copy rearranged line #3\n+copy rearranged line #4\n+copy rearranged line #5\n+copy rearranged line #6\n+copy rearranged line #7\n+copy rearranged line #8\n+copy rearranged line #9\n+EOF\n+\tcat <<EOF >src/move/unchanged/text &&\n+move  unchanged line #0\n+move  unchanged line #1\n+move  unchanged line #2\n+move  unchanged line #3\n+move  unchanged line #4\n+move  unchanged line #5\n+move  unchanged line #6\n+move  unchanged line #7\n+move  unchanged line #8\n+move  unchanged line #9\n+EOF\n+\tcat <<EOF >src/move/changed/text &&\n+move    changed line #0\n+move    changed line #1\n+move    changed line #2\n+move    changed line #3\n+move    changed line #4\n+move    changed line #5\n+move    changed line #6\n+move    changed line #7\n+move    changed line #8\n+move    changed line #9\n+EOF\n+\tcat <<EOF >src/move/rearranged/text &&\n+move rearranged line #0\n+move rearranged line #1\n+move rearranged line #2\n+move rearranged line #3\n+move rearranged line #4\n+move rearranged line #5\n+move rearranged line #6\n+move rearranged line #7\n+move rearranged line #8\n+move rearranged line #9\n+EOF\n+\tgit add . &&\n+\tgit commit -m \"initial\" &&\n+\tmkdir dst &&\n+\tmkdir dst/copy &&\n+\tmkdir dst/copy/unchanged &&\n+\tmkdir dst/copy/changed &&\n+\tmkdir dst/copy/rearranged &&\n+\tmkdir dst/move &&\n+\tmkdir dst/move/unchanged &&\n+\tmkdir dst/move/changed &&\n+\tmkdir dst/move/rearranged &&\n+\tcat <<EOF >changed/text &&\n+CHANGED XXXXXXX line #0\n+changed         line #1\n+changed         line #2\n+changed         line #3\n+changed         line #4\n+changed         line #5\n+changed         line #6\n+changed         line #7\n+changed         line #8\n+changed         line #9\n+EOF\n+\tcat <<EOF >rearranged/text &&\n+rearranged      line #1\n+rearranged      line #0\n+rearranged      line #2\n+rearranged      line #3\n+rearranged      line #4\n+rearranged      line #5\n+rearranged      line #6\n+rearranged      line #7\n+rearranged      line #8\n+rearranged      line #9\n+EOF\n+\tcat <<EOF >dst/copy/unchanged/text &&\n+copy  unchanged line #0\n+copy  unchanged line #1\n+copy  unchanged line #2\n+copy  unchanged line #3\n+copy  unchanged line #4\n+copy  unchanged line #5\n+copy  unchanged line #6\n+copy  unchanged line #7\n+copy  unchanged line #8\n+copy  unchanged line #9\n+EOF\n+\tcat <<EOF >dst/copy/changed/text &&\n+copy XXXCHANGED line #0\n+copy    changed line #1\n+copy    changed line #2\n+copy    changed line #3\n+copy    changed line #4\n+copy    changed line #5\n+copy    changed line #6\n+copy    changed line #7\n+copy    changed line #8\n+copy    changed line #9\n+EOF\n+\tcat <<EOF >dst/copy/rearranged/text &&\n+copy rearranged line #1\n+copy rearranged line #0\n+copy rearranged line #2\n+copy rearranged line #3\n+copy rearranged line #4\n+copy rearranged line #5\n+copy rearranged line #6\n+copy rearranged line #7\n+copy rearranged line #8\n+copy rearranged line #9\n+EOF\n+\tcat <<EOF >dst/move/unchanged/text &&\n+move  unchanged line #0\n+move  unchanged line #1\n+move  unchanged line #2\n+move  unchanged line #3\n+move  unchanged line #4\n+move  unchanged line #5\n+move  unchanged line #6\n+move  unchanged line #7\n+move  unchanged line #8\n+move  unchanged line #9\n+EOF\n+\tcat <<EOF >dst/move/changed/text &&\n+move XXXCHANGED line #0\n+move    changed line #1\n+move    changed line #2\n+move    changed line #3\n+move    changed line #4\n+move    changed line #5\n+move    changed line #6\n+move    changed line #7\n+move    changed line #8\n+move    changed line #9\n+EOF\n+\tcat <<EOF >dst/move/rearranged/text &&\n+move rearranged line #1\n+move rearranged line #0\n+move rearranged line #2\n+move rearranged line #3\n+move rearranged line #4\n+move rearranged line #5\n+move rearranged line #6\n+move rearranged line #7\n+move rearranged line #8\n+move rearranged line #9\n+EOF\n+\tgit add . &&\n+\tgit rm -r src/move/unchanged &&\n+\tgit rm -r src/move/changed &&\n+\tgit rm -r src/move/rearranged &&\n+\tgit commit -m \"changes\"\n+'\n+\n+cat <<EOF >expect_diff_stat\n+ changed/text             |    2 +-\n+ dst/copy/changed/text    |   10 ++++++++++\n+ dst/copy/rearranged/text |   10 ++++++++++\n+ dst/copy/unchanged/text  |   10 ++++++++++\n+ dst/move/changed/text    |   10 ++++++++++\n+ dst/move/rearranged/text |   10 ++++++++++\n+ dst/move/unchanged/text  |   10 ++++++++++\n+ rearranged/text          |    2 +-\n+ src/move/changed/text    |   10 ----------\n+ src/move/rearranged/text |   10 ----------\n+ src/move/unchanged/text  |   10 ----------\n+ 11 files changed, 62 insertions(+), 32 deletions(-)\n+EOF\n+\n+cat <<EOF >expect_diff_stat_M\n+ changed/text                      |    2 +-\n+ dst/copy/changed/text             |   10 ++++++++++\n+ dst/copy/rearranged/text          |   10 ++++++++++\n+ dst/copy/unchanged/text           |   10 ++++++++++\n+ {src => dst}/move/changed/text    |    2 +-\n+ {src => dst}/move/rearranged/text |    2 +-\n+ {src => dst}/move/unchanged/text  |    0\n+ rearranged/text                   |    2 +-\n+ 8 files changed, 34 insertions(+), 4 deletions(-)\n+EOF\n+\n+cat <<EOF >expect_diff_stat_CC\n+ changed/text                      |    2 +-\n+ {src => dst}/copy/changed/text    |    2 +-\n+ {src => dst}/copy/rearranged/text |    2 +-\n+ {src => dst}/copy/unchanged/text  |    0\n+ {src => dst}/move/changed/text    |    2 +-\n+ {src => dst}/move/rearranged/text |    2 +-\n+ {src => dst}/move/unchanged/text  |    0\n+ rearranged/text                   |    2 +-\n+ 8 files changed, 6 insertions(+), 6 deletions(-)\n+EOF\n+\n+test_expect_success 'sanity check setup (--stat)' '\n+\tgit diff --stat HEAD^..HEAD >actual_diff_stat &&\n+\ttest_cmp expect_diff_stat actual_diff_stat &&\n+\tgit diff --stat -M HEAD^..HEAD >actual_diff_stat_M &&\n+\ttest_cmp expect_diff_stat_M actual_diff_stat_M &&\n+\tgit diff --stat -C -C HEAD^..HEAD >actual_diff_stat_CC &&\n+\ttest_cmp expect_diff_stat_CC actual_diff_stat_CC\n+'\n+\n+# changed/text and rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+EOF\n+\n+# rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+   5.8% dst/move/changed/\n+EOF\n+\n+# rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+  32.6% dst/move/changed/\n+EOF\n+\n+test_expect_success 'vanilla --dirstat' '\n+\tgit diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'vanilla -X' '\n+\tgit diff -X HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -X -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -X -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+   5.8% dst/move/changed/\n+   0.1% dst/move/rearranged/\n+   0.1% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+   0.6% dst/copy/rearranged/\n+  32.6% dst/move/changed/\n+   0.6% dst/move/rearranged/\n+   0.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=0' '\n+\tgit diff --dirstat=0 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success '-X0' '\n+\tgit diff -X0 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -X0 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -X0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  32.5% dst/copy/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  32.5% dst/move/\n+  65.1% dst/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+  32.5% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+  88.0% dst/copy/\n+   5.8% dst/move/changed/\n+   0.1% dst/move/rearranged/\n+   5.9% dst/move/\n+  94.0% dst/\n+   0.1% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+   0.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  32.6% dst/move/changed/\n+   0.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+   0.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=0 --cumulative' '\n+\tgit diff --dirstat=0 --cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0 --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0 --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   9.0% changed/\n+   9.0% dst/copy/changed/\n+   9.0% dst/copy/rearranged/\n+   9.0% dst/copy/unchanged/\n+   9.0% dst/move/changed/\n+   9.0% dst/move/rearranged/\n+   9.0% dst/move/unchanged/\n+   9.0% rearranged/\n+   9.0% src/move/changed/\n+   9.0% src/move/rearranged/\n+   9.0% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file' '\n+\tgit diff --dirstat-by-file HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+  27.2% dst/copy/\n+  27.2% dst/move/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file=10' '\n+\tgit diff --dirstat-by-file=10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file=10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file=10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   9.0% changed/\n+   9.0% dst/copy/changed/\n+   9.0% dst/copy/rearranged/\n+   9.0% dst/copy/unchanged/\n+  27.2% dst/copy/\n+   9.0% dst/move/changed/\n+   9.0% dst/move/rearranged/\n+   9.0% dst/move/unchanged/\n+  27.2% dst/move/\n+  54.5% dst/\n+   9.0% rearranged/\n+   9.0% src/move/changed/\n+   9.0% src/move/rearranged/\n+   9.0% src/move/unchanged/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  42.8% dst/copy/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  28.5% dst/move/\n+  71.4% dst/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file --cumulative' '\n+\tgit diff --dirstat-by-file --cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166584","messageId":"1303953442-26536-3-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303953442-26536-1-git-send-email-johan@herland.net","subject":"[PATCHv5 2/7] Make --dirstat=0 output directories that contribute < 0.1% of changes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-28T01:17:17Z","receivedAt":"2011-04-28T01:17:17Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"The expected output from --dirstat=0, is to include any directory with\nchanges, even if those changes contribute a minuscule portion of the total\nchanges. However, currently, directories that contribute less than 0.1% are\nnot included, since their 'permille' value is 0, and there is an\n'if (permille)' check in gather_dirstat() that causes them to be ignored.\n\nThis test is obviously intended to exclude directories that contribute no\nchanges whatsoever, but in this case, it hits too broadly. The correct\ncheck is against 'this_dir' from which the permille is calculated. Only if\nthis value is 0 does the directory truly contribute no changes, and should\nbe skipped from the output.\n\nThis patches fixes this issue, and updates corresponding testcases to\nexpect the new behvaior.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n diff.c                  |    4 ++--\n t/t4047-diff-dirstat.sh |    4 ++--\n 2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex abd9cd5..cfbfa92 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1500,8 +1500,8 @@ static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n \t *    under this directory (sources == 1).\n \t */\n \tif (baselen && sources != 1) {\n-\t\tint permille = this_dir * 1000 / changed;\n-\t\tif (permille) {\n+\t\tif (this_dir) {\n+\t\t\tint permille = this_dir * 1000 / changed;\n \t\t\tint percent = permille / 10;\n \t\t\tif (percent >= dir->percent) {\n \t\t\t\tfprintf(opt->file, \"%s%4d.%01d%% %.*s\\n\", line_prefix,\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nindex eb6bf47..6ff7f9f 100755\n--- a/t/t4047-diff-dirstat.sh\n+++ b/t/t4047-diff-dirstat.sh\n@@ -346,7 +346,6 @@ test_expect_success 'vanilla -X' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n-# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -355,6 +354,7 @@ cat <<EOF >expect_diff_dirstat\n   10.8% dst/move/changed/\n   10.8% dst/move/rearranged/\n   10.8% dst/move/unchanged/\n+   0.0% rearranged/\n   10.8% src/move/changed/\n   10.8% src/move/rearranged/\n   10.8% src/move/unchanged/\n@@ -397,7 +397,6 @@ test_expect_success '-X0' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n-# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -409,6 +408,7 @@ cat <<EOF >expect_diff_dirstat\n   10.8% dst/move/unchanged/\n   32.5% dst/move/\n   65.1% dst/\n+   0.0% rearranged/\n   10.8% src/move/changed/\n   10.8% src/move/rearranged/\n   10.8% src/move/unchanged/\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166586","messageId":"1303953442-26536-4-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303953442-26536-1-git-send-email-johan@herland.net","subject":"[PATCHv5 3/7] Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-28T01:17:18Z","receivedAt":"2011-04-28T01:17:18Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Instead of having multiple interconnected dirstat-related options, teach\nthe --dirstat option itself to accept all behavior modifiers as parameters.\n\n - Preserve the current --dirstat=<limit> (where <limit> is an integer\n   specifying a cut-off percentage)\n - Add --dirstat=cumulative, replacing --cumulative\n - Add --dirstat=files, replacing --dirstat-by-file\n - Also add --dirstat=changes and --dirstat=noncumulative for specifying the\n   current default behavior. These allow the user to reset other --dirstat\n   parameters (e.g. 'cumulative' and 'files') occuring earlier on the\n   command line.\n\nThe deprecated options (--cumulative and --dirstat-by-file) are still\nfunctional, although they have been removed from the documentation.\n\nAllow multiple parameters to be separated by commas, e.g.:\n  --dirstat=files,10,cumulative\n\nUpdate the documentation accordingly, and add testcases verifying the\nbehavior of the new syntax.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/diff-options.txt |   44 +++++++++++----\n diff.c                         |   69 ++++++++++++++++++++---\n t/t4047-diff-dirstat.sh        |  119 ++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 211 insertions(+), 21 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 7e4bd42..6a3a9c1 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -66,19 +66,39 @@ endif::git-format-patch[]\n \tnumber of modified files, as well as number of added and deleted\n \tlines.\n \n---dirstat[=<limit>]::\n-\tOutput the distribution of relative amount of changes (number of lines added or\n-\tremoved) for each sub-directory. Directories with changes below\n-\ta cut-off percent (3% by default) are not shown. The cut-off percent\n-\tcan be set with `--dirstat=<limit>`. Changes in a child directory are not\n-\tcounted for the parent directory, unless `--cumulative` is used.\n+--dirstat[=<param1,param2,...>]::\n+\tOutput the distribution of relative amount of changes for each\n+\tsub-directory. The behavior of `--dirstat` can be customized by\n+\tpassing it a comma separated list of parameters.\n+\tThe following parameters are available:\n +\n-Note that the `--dirstat` option computes the changes while ignoring\n-the amount of pure code movements within a file.  In other words,\n-rearranging lines in a file is not counted as much as other changes.\n-\n---dirstat-by-file[=<limit>]::\n-\tSame as `--dirstat`, but counts changed files instead of lines.\n+--\n+`changes`;;\n+\tCompute the dirstat numbers by counting the lines that have been\n+\tremoved from the source, or added to the destination. This ignores\n+\tthe amount of pure code movements within a file.  In other words,\n+\trearranging lines in a file is not counted as much as other changes.\n+\tThis is the default behavior when no parameter is given.\n+`files`;;\n+\tCompute the dirstat numbers by counting the number of files changed.\n+\tEach changed file counts equally in the dirstat analysis. This is\n+\tthe computationally cheapest `--dirstat` behavior, since it does\n+\tnot have to look at the file contents at all.\n+`cumulative`;;\n+\tCount changes in a child directory for the parent directory as well.\n+\tNote that when using `cumulative`, the sum of the percentages\n+\treported may exceed 100%. The default (non-cumulative) behavior can\n+\tbe specified with the `noncumulative` parameter.\n+<limit>;;\n+\tAn integer parameter specifies a cut-off percent (3% by default).\n+\tDirectories contributing less than this percentage of the changes\n+\tare not shown in the output.\n+--\n++\n+Example: The following will count changed files, while ignoring\n+directories with less than 10% of the total amount of changed files,\n+and accumulating child directory counts in the parent directories:\n+`--dirstat=files,10,cumulative`.\n \n --summary::\n \tOutput a condensed summary of extended header information\ndiff --git a/diff.c b/diff.c\nindex cfbfa92..0e4a510 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -66,6 +66,41 @@ static int parse_diff_color_slot(const char *var, int ofs)\n \treturn -1;\n }\n \n+static int parse_dirstat_params(struct diff_options *options, const char *params)\n+{\n+\tconst char *p = params;\n+\twhile (*p) {\n+\t\tif (!prefixcmp(p, \"changes\")) {\n+\t\t\tp += 7;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n+\t\t} else if (!prefixcmp(p, \"files\")) {\n+\t\t\tp += 5;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n+\t\t} else if (!prefixcmp(p, \"noncumulative\")) {\n+\t\t\tp += 13;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);\n+\t\t} else if (!prefixcmp(p, \"cumulative\")) {\n+\t\t\tp += 10;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n+\t\t} else if (isdigit(*p)) {\n+\t\t\tchar *end;\n+\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n+\t\t\tp = end;\n+\t\t} else\n+\t\t\treturn error(\"Unknown --dirstat parameter '%s'\", p);\n+\n+\t\tif (*p) {\n+\t\t\t/* more parameters, swallow separator */\n+\t\t\tif (*p != ',')\n+\t\t\t\treturn error(\"Missing comma separator at char \"\n+\t\t\t\t\t\"%\"PRIuMAX\" of '%s'\",\n+\t\t\t\t\t(uintmax_t) (p - params), params);\n+\t\t\tp++;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n static int git_config_rename(const char *var, const char *value)\n {\n \tif (!value)\n@@ -3144,6 +3179,18 @@ static int stat_opt(struct diff_options *options, const char **av)\n \treturn argcount;\n }\n \n+static int parse_dirstat_opt(struct diff_options *options, const char *params)\n+{\n+\tif (parse_dirstat_params(options, params))\n+\t\tdie(\"Failed to parse --dirstat/-X option parameter\");\n+\t/*\n+\t * The caller knows a dirstat-related option is given from the command\n+\t * line; allow it to say \"return this_function();\"\n+\t */\n+\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n+\treturn 1;\n+}\n+\n int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n {\n \tconst char *arg = av[0];\n@@ -3163,15 +3210,19 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \t\toptions->output_format |= DIFF_FORMAT_NUMSTAT;\n \telse if (!strcmp(arg, \"--shortstat\"))\n \t\toptions->output_format |= DIFF_FORMAT_SHORTSTAT;\n-\telse if (opt_arg(arg, 'X', \"dirstat\", &options->dirstat_percent))\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\telse if (!strcmp(arg, \"--cumulative\")) {\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n-\t} else if (opt_arg(arg, 0, \"dirstat-by-file\",\n-\t\t\t   &options->dirstat_percent)) {\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n+\telse if (!strcmp(arg, \"-X\") || !strcmp(arg, \"--dirstat\"))\n+\t\treturn parse_dirstat_opt(options, \"\");\n+\telse if (!prefixcmp(arg, \"-X\"))\n+\t\treturn parse_dirstat_opt(options, arg + 2);\n+\telse if (!prefixcmp(arg, \"--dirstat=\"))\n+\t\treturn parse_dirstat_opt(options, arg + 10);\n+\telse if (!strcmp(arg, \"--cumulative\"))\n+\t\treturn parse_dirstat_opt(options, \"cumulative\");\n+\telse if (!strcmp(arg, \"--dirstat-by-file\"))\n+\t\treturn parse_dirstat_opt(options, \"files\");\n+\telse if (!prefixcmp(arg, \"--dirstat-by-file=\")) {\n+\t\tparse_dirstat_opt(options, \"files\");\n+\t\treturn parse_dirstat_opt(options, arg + 18);\n \t}\n \telse if (!strcmp(arg, \"--check\"))\n \t\toptions->output_format |= DIFF_FORMAT_CHECKDIFF;\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nindex 6ff7f9f..0ede619 100755\n--- a/t/t4047-diff-dirstat.sh\n+++ b/t/t4047-diff-dirstat.sh\n@@ -346,6 +346,39 @@ test_expect_success 'vanilla -X' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'explicit defaults: --dirstat=changes,noncumulative,3' '\n+\tgit diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'explicit defaults: -Xchanges,noncumulative,3' '\n+\tgit diff -Xchanges,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -Xchanges,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -Xchanges,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'later options override earlier options:' '\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -449,6 +482,24 @@ test_expect_success '--dirstat=0 --cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=0,cumulative' '\n+\tgit diff --dirstat=0,cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success '-X0,cumulative' '\n+\tgit diff -X0,cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -X0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -X0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -491,6 +542,15 @@ test_expect_success '--dirstat-by-file' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files' '\n+\tgit diff --dirstat=files HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -525,6 +585,15 @@ test_expect_success '--dirstat-by-file=10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files,10' '\n+\tgit diff --dirstat=files,10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -577,4 +646,54 @@ test_expect_success '--dirstat-by-file --cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files,cumulative' '\n+\tgit diff --dirstat=files,cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+  27.2% dst/copy/\n+  27.2% dst/move/\n+  54.5% dst/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  42.8% dst/copy/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  28.5% dst/move/\n+  71.4% dst/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=files,cumulative,10' '\n+\tgit diff --dirstat=files,cumulative,10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166590","messageId":"1303953442-26536-5-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303953442-26536-1-git-send-email-johan@herland.net","subject":"[PATCHv5 4/7] Add config variable for specifying default --dirstat behavior","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-28T01:17:19Z","receivedAt":"2011-04-28T01:17:19Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"The new diff.dirstat config variable takes the same arguments as\n'--dirstat=<args>', and specifies the default arguments for --dirstat.\nThe config is obviously overridden by --dirstat arguments passed on the\ncommand line.\n\nWhen not specified, the --dirstat defaults are 'changes,noncumulative,3'.\n\nThe patch also adds several tests verifying the interaction between the\ndiff.dirstat config variable, and the --dirstat command line option.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/config.txt       |   36 ++++++++++++++++++++\n Documentation/diff-options.txt |    2 +\n diff.c                         |   10 +++++-\n t/t4047-diff-dirstat.sh        |   72 ++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 119 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6babbc7..c18dd5a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -822,6 +822,42 @@ diff.autorefreshindex::\n \taffects only 'git diff' Porcelain, and not lower level\n \t'diff' commands such as 'git diff-files'.\n \n+diff.dirstat::\n+\tA comma separated list of `--dirstat` parameters specifying the\n+\tdefault behavior of the `--dirstat` option to linkgit:git-diff[1]`\n+\tand friends. The defaults can be overridden on the command line\n+\t(using `--dirstat=<param1,param2,...>`). The fallback defaults\n+\t(when not changed by `diff.dirstat`) are `changes,noncumulative,3`.\n+\tThe following parameters are available:\n++\n+--\n+`changes`;;\n+\tCompute the dirstat numbers by counting the lines that have been\n+\tremoved from the source, or added to the destination. This ignores\n+\tthe amount of pure code movements within a file.  In other words,\n+\trearranging lines in a file is not counted as much as other changes.\n+\tThis is the default behavior when no parameter is given.\n+`files`;;\n+\tCompute the dirstat numbers by counting the number of files changed.\n+\tEach changed file counts equally in the dirstat analysis. This is\n+\tthe computationally cheapest `--dirstat` behavior, since it does\n+\tnot have to look at the file contents at all.\n+`cumulative`;;\n+\tCount changes in a child directory for the parent directory as well.\n+\tNote that when using `cumulative`, the sum of the percentages\n+\treported may exceed 100%. The default (non-cumulative) behavior can\n+\tbe specified with the `noncumulative` parameter.\n+<limit>;;\n+\tAn integer parameter specifies a cut-off percent (3% by default).\n+\tDirectories contributing less than this percentage of the changes\n+\tare not shown in the output.\n+--\n++\n+Example: The following will count changed files, while ignoring\n+directories with less than 10% of the total amount of changed files,\n+and accumulating child directory counts in the parent directories:\n+`files,10,cumulative`.\n+\n diff.external::\n \tIf this config variable is set, diff generation is not\n \tperformed using the internal diff machinery, but using the\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 6a3a9c1..4ad50b9 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -70,6 +70,8 @@ endif::git-format-patch[]\n \tOutput the distribution of relative amount of changes for each\n \tsub-directory. The behavior of `--dirstat` can be customized by\n \tpassing it a comma separated list of parameters.\n+\tThe defaults are controlled by the `diff.dirstat` configuration\n+\tvariable (see linkgit:git-config[1]).\n \tThe following parameters are available:\n +\n --\ndiff --git a/diff.c b/diff.c\nindex 0e4a510..92508b0 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -31,6 +31,7 @@ static const char *external_diff_cmd_cfg;\n int diff_auto_refresh_index = 1;\n static int diff_mnemonic_prefix;\n static int diff_no_prefix;\n+static int diff_dirstat_percent_default = 3;\n static struct diff_options default_diff_options;\n \n static char diff_colors[][COLOR_MAXLEN] = {\n@@ -180,6 +181,13 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"diff.dirstat\")) {\n+\t\tdefault_diff_options.dirstat_percent = diff_dirstat_percent_default;\n+\t\t(void) parse_dirstat_params(&default_diff_options, value);\n+\t\tdiff_dirstat_percent_default = default_diff_options.dirstat_percent;\n+\t\treturn 0;\n+\t}\n+\n \tif (!prefixcmp(var, \"submodule.\"))\n \t\treturn parse_submodule_config_option(var, value);\n \n@@ -2921,7 +2929,7 @@ void diff_setup(struct diff_options *options)\n \toptions->line_termination = '\\n';\n \toptions->break_opt = -1;\n \toptions->rename_limit = -1;\n-\toptions->dirstat_percent = 3;\n+\toptions->dirstat_percent = diff_dirstat_percent_default;\n \toptions->context = 3;\n \n \toptions->change = diff_change;\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nindex 0ede619..fa1885c 100755\n--- a/t/t4047-diff-dirstat.sh\n+++ b/t/t4047-diff-dirstat.sh\n@@ -379,6 +379,15 @@ test_expect_success 'later options override earlier options:' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'non-defaults in config overridden by explicit defaults on command line' '\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -430,6 +439,15 @@ test_expect_success '-X0' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=0' '\n+\tgit -c diff.dirstat=0 diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0 diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0 diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -500,6 +518,24 @@ test_expect_success '-X0,cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=0,cumulative' '\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=0 & --dirstat=cumulative' '\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -551,6 +587,15 @@ test_expect_success '--dirstat=files' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=files' '\n+\tgit -c diff.dirstat=files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -594,6 +639,15 @@ test_expect_success '--dirstat=files,10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=10,files' '\n+\tgit -c diff.dirstat=10,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=10,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=10,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -655,6 +709,15 @@ test_expect_success '--dirstat=files,cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=cumulative,files' '\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -696,4 +759,13 @@ test_expect_success '--dirstat=files,cumulative,10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=10,cumulative,files' '\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166589","messageId":"1303953442-26536-6-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303953442-26536-1-git-send-email-johan@herland.net","subject":"[PATCHv5 5/7] Allow specifying --dirstat cut-off percentage as a floating point number","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-28T01:17:20Z","receivedAt":"2011-04-28T01:17:20Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Only the first digit after the decimal point is kept, as the dirstat\ncalculations all happen in permille.\n\nSelftests verifying floating-point percentage input has been added.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nImproved-by: Linus Torvalds <torvalds@linux-foundation.org>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n diff.c                  |   26 +++++++++++-------\n diff.h                  |    2 +-\n t/t4047-diff-dirstat.sh |   64 +++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 81 insertions(+), 11 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 92508b0..e0de4fa 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -31,7 +31,7 @@ static const char *external_diff_cmd_cfg;\n int diff_auto_refresh_index = 1;\n static int diff_mnemonic_prefix;\n static int diff_no_prefix;\n-static int diff_dirstat_percent_default = 3;\n+static int diff_dirstat_permille_default = 30;\n static struct diff_options default_diff_options;\n \n static char diff_colors[][COLOR_MAXLEN] = {\n@@ -85,8 +85,15 @@ static int parse_dirstat_params(struct diff_options *options, const char *params\n \t\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n \t\t} else if (isdigit(*p)) {\n \t\t\tchar *end;\n-\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n+\t\t\toptions->dirstat_permille = strtoul(p, &end, 10) * 10;\n \t\t\tp = end;\n+\t\t\tif (*p == '.' && isdigit(*++p)) {\n+\t\t\t\t/* only use first digit */\n+\t\t\t\toptions->dirstat_permille += *p - '0';\n+\t\t\t\t/* .. and ignore any further digits */\n+\t\t\t\twhile (isdigit(*++p))\n+\t\t\t\t\t; /* nothing */\n+\t\t\t}\n \t\t} else\n \t\t\treturn error(\"Unknown --dirstat parameter '%s'\", p);\n \n@@ -182,9 +189,9 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)\n \t}\n \n \tif (!strcmp(var, \"diff.dirstat\")) {\n-\t\tdefault_diff_options.dirstat_percent = diff_dirstat_percent_default;\n+\t\tdefault_diff_options.dirstat_permille = diff_dirstat_permille_default;\n \t\t(void) parse_dirstat_params(&default_diff_options, value);\n-\t\tdiff_dirstat_percent_default = default_diff_options.dirstat_percent;\n+\t\tdiff_dirstat_permille_default = default_diff_options.dirstat_permille;\n \t\treturn 0;\n \t}\n \n@@ -1496,7 +1503,7 @@ struct dirstat_file {\n \n struct dirstat_dir {\n \tstruct dirstat_file *files;\n-\tint alloc, nr, percent, cumulative;\n+\tint alloc, nr, permille, cumulative;\n };\n \n static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n@@ -1545,10 +1552,9 @@ static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n \tif (baselen && sources != 1) {\n \t\tif (this_dir) {\n \t\t\tint permille = this_dir * 1000 / changed;\n-\t\t\tint percent = permille / 10;\n-\t\t\tif (percent >= dir->percent) {\n+\t\t\tif (permille >= dir->permille) {\n \t\t\t\tfprintf(opt->file, \"%s%4d.%01d%% %.*s\\n\", line_prefix,\n-\t\t\t\t\tpercent, permille % 10, baselen, base);\n+\t\t\t\t\tpermille / 10, permille % 10, baselen, base);\n \t\t\t\tif (!dir->cumulative)\n \t\t\t\t\treturn 0;\n \t\t\t}\n@@ -1574,7 +1580,7 @@ static void show_dirstat(struct diff_options *options)\n \tdir.files = NULL;\n \tdir.alloc = 0;\n \tdir.nr = 0;\n-\tdir.percent = options->dirstat_percent;\n+\tdir.permille = options->dirstat_permille;\n \tdir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);\n \n \tchanged = 0;\n@@ -2929,7 +2935,7 @@ void diff_setup(struct diff_options *options)\n \toptions->line_termination = '\\n';\n \toptions->break_opt = -1;\n \toptions->rename_limit = -1;\n-\toptions->dirstat_percent = diff_dirstat_percent_default;\n+\toptions->dirstat_permille = diff_dirstat_permille_default;\n \toptions->context = 3;\n \n \toptions->change = diff_change;\ndiff --git a/diff.h b/diff.h\nindex 0083d92..08b4fe0 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -111,7 +111,7 @@ struct diff_options {\n \tint rename_score;\n \tint rename_limit;\n \tint warn_on_too_large_rename;\n-\tint dirstat_percent;\n+\tint dirstat_permille;\n \tint setup;\n \tint abbrev;\n \tconst char *prefix;\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nindex fa1885c..b3062b4 100755\n--- a/t/t4047-diff-dirstat.sh\n+++ b/t/t4047-diff-dirstat.sh\n@@ -768,4 +768,68 @@ test_expect_success 'diff.dirstat=10,cumulative,files' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+cat <<EOF >expect_diff_dirstat\n+  27.2% dst/copy/\n+  27.2% dst/move/\n+  54.5% dst/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  42.8% dst/copy/\n+  28.5% dst/move/\n+  71.4% dst/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  33.3% dst/copy/\n+  33.3% dst/move/\n+  66.6% dst/\n+EOF\n+\n+test_expect_success '--dirstat=files,cumulative,16.7' '\n+\tgit diff --dirstat=files,cumulative,16.7 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,16.7 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,16.7 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=16.7,cumulative,files' '\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=16.70,cumulative,files' '\n+\tgit -c diff.dirstat=16.70,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=16.70,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=16.70,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success '--dirstat=files,cumulative,27.2' '\n+\tgit diff --dirstat=files,cumulative,27.2 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,27.2 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,27.2 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success '--dirstat=files,cumulative,27.09' '\n+\tgit diff --dirstat=files,cumulative,27.09 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,27.09 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,27.09 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166588","messageId":"1303953442-26536-7-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303953442-26536-1-git-send-email-johan@herland.net","subject":"[PATCHv5 6/7] New --dirstat=lines mode, doing dirstat analysis based on diffstat","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-28T01:17:21Z","receivedAt":"2011-04-28T01:17:21Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"This patch adds an alternative implementation of show_dirstat(), called\nshow_dirstat_by_line(), which uses the more expensive diffstat analysis\n(as opposed to show_dirstat()'s own (relatively inexpensive) analysis)\nto derive the numbers from which the --dirstat output is computed.\n\nThe alternative implementation is controlled by the new \"lines\" parameter\nto the --dirstat option (or the diff.dirstat config variable).\n\nFor binary files, the diffstat analysis counts bytes instead of lines,\nso to prevent binary files from dominating the dirstat results, the\nbyte counts for binary files are divided by 64 before being compared to\ntheir textual/line-based counterparts. This is a stupid and ugly - but\nvery cheap - heuristic.\n\nIn linux-2.6.git, running the three different --dirstat modes:\n\n  time git diff v2.6.20..v2.6.30 --dirstat=changes > /dev/null\nvs.\n  time git diff v2.6.20..v2.6.30 --dirstat=lines > /dev/null\nvs.\n  time git diff v2.6.20..v2.6.30 --dirstat=files > /dev/null\n\nyields the following average runtimes on my machine:\n\n - \"changes\" (default): ~6.0 s\n - \"lines\":             ~9.6 s\n - \"files\":             ~0.1 s\n\nSo, as expected, there's a considerable performance hit (~60%) by going\nthrough the full diffstat analysis as compared to the default \"changes\"\nanalysis (obviously, \"files\" is much faster than both). As such, the\n\"lines\" mode is probably only useful if you really need the --dirstat\nnumbers to be consistent with the numbers returned from the other\n--*stat options.\n\nThe patch also includes documentation and tests for the new dirstat mode.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/config.txt       |    8 +++\n Documentation/diff-options.txt |    8 +++\n diff.c                         |   61 +++++++++++++++++++++++-\n diff.h                         |    1 +\n t/t4047-diff-dirstat.sh        |  100 ++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 176 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex c18dd5a..0cad75c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -837,6 +837,14 @@ diff.dirstat::\n \tthe amount of pure code movements within a file.  In other words,\n \trearranging lines in a file is not counted as much as other changes.\n \tThis is the default behavior when no parameter is given.\n+`lines`;;\n+\tCompute the dirstat numbers by doing the regular line-based diff\n+\tanalysis, and summing the removed/added line counts. (For binary\n+\tfiles, count 64-byte chunks instead, since binary files have no\n+\tnatural concept of lines). This is a more expensive `--dirstat`\n+\tbehavior than the `changes` behavior, but it does count rearranged\n+\tlines within a file as much as other changes. The resulting output\n+\tis consistent with what you get from the other `--*stat` options.\n `files`;;\n \tCompute the dirstat numbers by counting the number of files changed.\n \tEach changed file counts equally in the dirstat analysis. This is\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 4ad50b9..327d10a 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -81,6 +81,14 @@ endif::git-format-patch[]\n \tthe amount of pure code movements within a file.  In other words,\n \trearranging lines in a file is not counted as much as other changes.\n \tThis is the default behavior when no parameter is given.\n+`lines`;;\n+\tCompute the dirstat numbers by doing the regular line-based diff\n+\tanalysis, and summing the removed/added line counts. (For binary\n+\tfiles, count 64-byte chunks instead, since binary files have no\n+\tnatural concept of lines). This is a more expensive `--dirstat`\n+\tbehavior than the `changes` behavior, but it does count rearranged\n+\tlines within a file as much as other changes. The resulting output\n+\tis consistent with what you get from the other `--*stat` options.\n `files`;;\n \tCompute the dirstat numbers by counting the number of files changed.\n \tEach changed file counts equally in the dirstat analysis. This is\ndiff --git a/diff.c b/diff.c\nindex e0de4fa..8703763 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -73,9 +73,15 @@ static int parse_dirstat_params(struct diff_options *options, const char *params\n \twhile (*p) {\n \t\tif (!prefixcmp(p, \"changes\")) {\n \t\t\tp += 7;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_LINE);\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n+\t\t} else if (!prefixcmp(p, \"lines\")) {\n+\t\t\tp += 5;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n \t\t} else if (!prefixcmp(p, \"files\")) {\n \t\t\tp += 5;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n \t\t} else if (!prefixcmp(p, \"noncumulative\")) {\n \t\t\tp += 13;\n@@ -1669,6 +1675,50 @@ found_damage:\n \tgather_dirstat(options, &dir, changed, \"\", 0);\n }\n \n+static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *options)\n+{\n+\tint i;\n+\tunsigned long changed;\n+\tstruct dirstat_dir dir;\n+\n+\tif (data->nr == 0)\n+\t\treturn;\n+\n+\tdir.files = NULL;\n+\tdir.alloc = 0;\n+\tdir.nr = 0;\n+\tdir.permille = options->dirstat_permille;\n+\tdir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);\n+\n+\tchanged = 0;\n+\tfor (i = 0; i < data->nr; i++) {\n+\t\tstruct diffstat_file *file = data->files[i];\n+\t\tunsigned long damage = file->added + file->deleted;\n+\t\tif (file->is_binary)\n+\t\t\t/*\n+\t\t\t * binary files counts bytes, not lines. Must find some\n+\t\t\t * way to normalize binary bytes vs. textual lines.\n+\t\t\t * The following heuristic assumes that there are 64\n+\t\t\t * bytes per \"line\".\n+\t\t\t * This is stupid and ugly, but very cheap...\n+\t\t\t */\n+\t\t\tdamage = (damage + 63) / 64;\n+\t\tALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);\n+\t\tdir.files[dir.nr].name = file->name;\n+\t\tdir.files[dir.nr].changed = damage;\n+\t\tchanged += damage;\n+\t\tdir.nr++;\n+\t}\n+\n+\t/* This can happen even with many files, if everything was renames */\n+\tif (!changed)\n+\t\treturn;\n+\n+\t/* Show all directories with more than x% of the changes */\n+\tqsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);\n+\tgather_dirstat(options, &dir, changed, \"\", 0);\n+}\n+\n static void free_diffstat_info(struct diffstat_t *diffstat)\n {\n \tint i;\n@@ -4058,6 +4108,7 @@ void diff_flush(struct diff_options *options)\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tint i, output_format = options->output_format;\n \tint separator = 0;\n+\tint dirstat_by_line = 0;\n \n \t/*\n \t * Order: raw, stat, summary, patch\n@@ -4078,7 +4129,11 @@ void diff_flush(struct diff_options *options)\n \t\tseparator++;\n \t}\n \n-\tif (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {\n+\tif (output_format & DIFF_FORMAT_DIRSTAT && DIFF_OPT_TST(options, DIRSTAT_BY_LINE))\n+\t\tdirstat_by_line = 1;\n+\n+\tif (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT) ||\n+\t    dirstat_by_line) {\n \t\tstruct diffstat_t diffstat;\n \n \t\tmemset(&diffstat, 0, sizeof(struct diffstat_t));\n@@ -4093,10 +4148,12 @@ void diff_flush(struct diff_options *options)\n \t\t\tshow_stats(&diffstat, options);\n \t\tif (output_format & DIFF_FORMAT_SHORTSTAT)\n \t\t\tshow_shortstats(&diffstat, options);\n+\t\tif (output_format & DIFF_FORMAT_DIRSTAT)\n+\t\t\tshow_dirstat_by_line(&diffstat, options);\n \t\tfree_diffstat_info(&diffstat);\n \t\tseparator++;\n \t}\n-\tif (output_format & DIFF_FORMAT_DIRSTAT)\n+\tif ((output_format & DIFF_FORMAT_DIRSTAT) && !dirstat_by_line)\n \t\tshow_dirstat(options);\n \n \tif (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {\ndiff --git a/diff.h b/diff.h\nindex 08b4fe0..1a8b685 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -78,6 +78,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)\n #define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)\n #define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)\n #define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)\n+#define DIFF_OPT_DIRSTAT_BY_LINE     (1 << 28)\n \n #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)\n #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nindex b3062b4..8ca1d58 100755\n--- a/t/t4047-diff-dirstat.sh\n+++ b/t/t4047-diff-dirstat.sh\n@@ -832,4 +832,104 @@ test_expect_success '--dirstat=files,cumulative,27.09' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+cat <<EOF >expect_diff_dirstat\n+  10.6% dst/copy/changed/\n+  10.6% dst/copy/rearranged/\n+  10.6% dst/copy/unchanged/\n+  10.6% dst/move/changed/\n+  10.6% dst/move/rearranged/\n+  10.6% dst/move/unchanged/\n+  10.6% src/move/changed/\n+  10.6% src/move/rearranged/\n+  10.6% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.2% changed/\n+  26.3% dst/copy/changed/\n+  26.3% dst/copy/rearranged/\n+  26.3% dst/copy/unchanged/\n+   5.2% dst/move/changed/\n+   5.2% dst/move/rearranged/\n+   5.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=lines' '\n+\tgit diff --dirstat=lines HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=lines -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=lines -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=lines' '\n+\tgit -c diff.dirstat=lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.6% dst/copy/changed/\n+  10.6% dst/copy/rearranged/\n+  10.6% dst/copy/unchanged/\n+  10.6% dst/move/changed/\n+  10.6% dst/move/rearranged/\n+  10.6% dst/move/unchanged/\n+   2.1% rearranged/\n+  10.6% src/move/changed/\n+  10.6% src/move/rearranged/\n+  10.6% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.2% changed/\n+  26.3% dst/copy/changed/\n+  26.3% dst/copy/rearranged/\n+  26.3% dst/copy/unchanged/\n+   5.2% dst/move/changed/\n+   5.2% dst/move/rearranged/\n+   5.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=lines,0' '\n+\tgit diff --dirstat=lines,0 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=lines,0 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=lines,0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=0,lines' '\n+\tgit -c diff.dirstat=0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166585","messageId":"1303953442-26536-8-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1303953442-26536-1-git-send-email-johan@herland.net","subject":"[PATCHv5 7/7] Improve error handling when parsing dirstat parameters","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-28T01:17:22Z","receivedAt":"2011-04-28T01:17:22Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"When encountering errors or unknown tokens while parsing parameters to the\n--dirstat option, it makes sense to die() with an error message informing\nthe user of which parameter did not make sense. However, when parsing the\ndiff.dirstat config variable, we cannot simply die(), but should instead\n(after warning the user) ignore the erroneous or unrecognized parameter.\nAfter all, future Git versions might add more dirstat parameters, and\nusing two different Git versions on the same repo should not cripple the\nolder Git version just because of a parameter that is only understood by\na more recent Git version.\n\nThis patch fixes the issue by refactoring the dirstat parameter parsing\nso that parse_dirstat_params() keeps on parsing parameters, even if an\nearlier parameter was not recognized. When parsing has finished, it returns\nzero if all parameters were successfully parsed, and non-zero if one or\nmore parameters were not recognized.\n\nThe parse_dirstat_params() callers then decide (based on the return value\nfrom parse_dirstat_params()) whether to warn and ignore (in case of\ndiff.dirstat), or to warn and die (in case of --dirstat).\n\nThe patch also adds a couple of tests verifying the correct behavior of\n--dirstat and diff.dirstat in the face of unknown (possibly future) dirstat\nparameters.\n\nSuggested-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n diff.c                  |   52 ++++++++++++++++++++++------------------------\n t/t4047-diff-dirstat.sh |   30 +++++++++++++++++++++++++++\n 2 files changed, 55 insertions(+), 27 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 8703763..1ce21f1 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -70,49 +70,46 @@ static int parse_diff_color_slot(const char *var, int ofs)\n static int parse_dirstat_params(struct diff_options *options, const char *params)\n {\n \tconst char *p = params;\n+\tint p_len, ret = 0;\n+\n \twhile (*p) {\n-\t\tif (!prefixcmp(p, \"changes\")) {\n-\t\t\tp += 7;\n+\t\tp_len = strchrnul(p, ',') - p;\n+\t\tif (!memcmp(p, \"changes\", p_len)) {\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n-\t\t} else if (!prefixcmp(p, \"lines\")) {\n-\t\t\tp += 5;\n+\t\t} else if (!memcmp(p, \"lines\", p_len)) {\n \t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n-\t\t} else if (!prefixcmp(p, \"files\")) {\n-\t\t\tp += 5;\n+\t\t} else if (!memcmp(p, \"files\", p_len)) {\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n-\t\t} else if (!prefixcmp(p, \"noncumulative\")) {\n-\t\t\tp += 13;\n+\t\t} else if (!memcmp(p, \"noncumulative\", p_len)) {\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);\n-\t\t} else if (!prefixcmp(p, \"cumulative\")) {\n-\t\t\tp += 10;\n+\t\t} else if (!memcmp(p, \"cumulative\", p_len)) {\n \t\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n \t\t} else if (isdigit(*p)) {\n \t\t\tchar *end;\n-\t\t\toptions->dirstat_permille = strtoul(p, &end, 10) * 10;\n-\t\t\tp = end;\n-\t\t\tif (*p == '.' && isdigit(*++p)) {\n+\t\t\tint permille = strtoul(p, &end, 10) * 10;\n+\t\t\tif (*end == '.' && isdigit(*++end)) {\n \t\t\t\t/* only use first digit */\n-\t\t\t\toptions->dirstat_permille += *p - '0';\n+\t\t\t\tpermille += *end - '0';\n \t\t\t\t/* .. and ignore any further digits */\n-\t\t\t\twhile (isdigit(*++p))\n+\t\t\t\twhile (isdigit(*++end))\n \t\t\t\t\t; /* nothing */\n \t\t\t}\n+\t\t\tif (end - p == p_len)\n+\t\t\t\toptions->dirstat_permille = permille;\n+\t\t\telse\n+\t\t\t\tret = error(\"Failed to parse dirstat cut-off percentage '%.*s'\", p_len, p);\n \t\t} else\n-\t\t\treturn error(\"Unknown --dirstat parameter '%s'\", p);\n-\n-\t\tif (*p) {\n-\t\t\t/* more parameters, swallow separator */\n-\t\t\tif (*p != ',')\n-\t\t\t\treturn error(\"Missing comma separator at char \"\n-\t\t\t\t\t\"%\"PRIuMAX\" of '%s'\",\n-\t\t\t\t\t(uintmax_t) (p - params), params);\n-\t\t\tp++;\n-\t\t}\n+\t\t\tret = error(\"Unknown dirstat parameter '%.*s'\", p_len, p);\n+\n+\t\tp += p_len;\n+\n+\t\tif (*p)\n+\t\t\tp++; /* more parameters, swallow separator */\n \t}\n-\treturn 0;\n+\treturn ret;\n }\n \n static int git_config_rename(const char *var, const char *value)\n@@ -196,7 +193,8 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)\n \n \tif (!strcmp(var, \"diff.dirstat\")) {\n \t\tdefault_diff_options.dirstat_permille = diff_dirstat_permille_default;\n-\t\t(void) parse_dirstat_params(&default_diff_options, value);\n+\t\tif (parse_dirstat_params(&default_diff_options, value))\n+\t\t\twarning(\"Found errors in 'diff.dirstat' config variable\");\n \t\tdiff_dirstat_permille_default = default_diff_options.dirstat_permille;\n \t\treturn 0;\n \t}\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nindex 8ca1d58..20a59ac 100755\n--- a/t/t4047-diff-dirstat.sh\n+++ b/t/t4047-diff-dirstat.sh\n@@ -932,4 +932,34 @@ test_expect_success 'diff.dirstat=0,lines' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=future_param,lines,0 should fail loudly' '\n+\ttest_must_fail git diff --dirstat=future_param,lines,0 HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&\n+\ttest_cmp /dev/null actual_diff_dirstat &&\n+\tgrep -q \"future_param\" actual_error &&\n+\tgrep -q \"\\--dirstat\" actual_error &&\n+\ttest_must_fail git diff --dirstat=future_param,lines,0 -M HEAD^..HEAD >actual_diff_dirstat_M 2>actual_error &&\n+\ttest_cmp /dev/null actual_diff_dirstat_M &&\n+\tgrep -q \"future_param\" actual_error &&\n+\tgrep -q \"\\--dirstat\" actual_error &&\n+\ttest_must_fail git diff --dirstat=future_param,lines,0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC 2>actual_error &&\n+\ttest_cmp /dev/null actual_diff_dirstat_CC &&\n+\tgrep -q \"future_param\" actual_error &&\n+\tgrep -q \"\\--dirstat\" actual_error\n+'\n+\n+test_expect_success 'diff.dirstat=future_param,0,lines should warn, but still work' '\n+\tgit -c diff.dirstat=future_param,0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgrep -q \"future_param\" actual_error &&\n+\tgrep -q \"diff.dirstat\" actual_error &&\n+\tgit -c diff.dirstat=future_param,0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M 2>actual_error &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgrep -q \"future_param\" actual_error &&\n+\tgrep -q \"diff.dirstat\" actual_error &&\n+\tgit -c diff.dirstat=future_param,0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC 2>actual_error &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC &&\n+\tgrep -q \"future_param\" actual_error &&\n+\tgrep -q \"diff.dirstat\" actual_error\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166663","messageId":"7vhb9i43sl.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"1303953442-26536-8-git-send-email-johan@herland.net","subject":"Re: [PATCHv5 7/7] Improve error handling when parsing dirstat parameters","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-28T18:41:14Z","receivedAt":"2011-04-28T18:41:14Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> ...\n> The patch also adds a couple of tests verifying the correct behavior of\n> --dirstat and diff.dirstat in the face of unknown (possibly future) dirstat\n> parameters.\n\nThanks.  Patches 1-6/7 looks much better.\n\nWhen writing a shiny new feature, people tend to test only the cases they\nexpect to work, leaving the cases that should error out unspecified,\nleading to future confusion.  Negative tests to specify and guard error\nbehaviour are very important, and I like this 7/7 very much.\n\nHaving said that, you might want to add tests for parsing --dirstat/-X\noptions themselves for the same reason.  I think you had troubles with -X3\n(the first round), --dirstat40 (the third round), and possibly -X=3; they\ncould have been avoided if you had such tests.  They probably should be\nadded to 1/7.\n\n> diff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\n> index 8ca1d58..20a59ac 100755\n> --- a/t/t4047-diff-dirstat.sh\n> +++ b/t/t4047-diff-dirstat.sh\n> @@ -932,4 +932,34 @@ test_expect_success 'diff.dirstat=0,lines' '\n>  \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n>  '\n>  \n> +test_expect_success '--dirstat=future_param,lines,0 should fail loudly' '\n> +\ttest_must_fail git diff --dirstat=future_param,lines,0 HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&\n> +\ttest_cmp /dev/null actual_diff_dirstat &&\n> +\tgrep -q \"future_param\" actual_error &&\n> +\tgrep -q \"\\--dirstat\" actual_error &&\n> +\ttest_must_fail git diff --dirstat=future_param,lines,0 -M HEAD^..HEAD >actual_diff_dirstat_M 2>actual_error &&\n> +\ttest_cmp /dev/null actual_diff_dirstat_M &&\n> +\tgrep -q \"future_param\" actual_error &&\n> +\tgrep -q \"\\--dirstat\" actual_error &&\n> +\ttest_must_fail git diff --dirstat=future_param,lines,0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC 2>actual_error &&\n> +\ttest_cmp /dev/null actual_diff_dirstat_CC &&\n> +\tgrep -q \"future_param\" actual_error &&\n> +\tgrep -q \"\\--dirstat\" actual_error\n> +'\n\nI am not sure if three combinations (vanilla, -M and -C -C) need to be\ntested to produce an empty result.  If so, it would make it easier to read\nif they are split into three tests, or at least have a blank line between\nthem, but I suspect that you would agree that it is not worth to have\nthree separate test_expect_success for these.\n\nI also wanted to see the error output.  How about adding:\n\n\ttest_debug \"cat actual_error\" &&\n\nimmediately after invocation of \"git diff\"?\n\nThe error output shows \"error:\" followed by \"warning:\", which looked\nsomewhat questionable.  Perhaps allow a pointer to a structure be passed\nin to describe the nature of a breakage to parse_dirstat_params()?\n\nTelling \"grep\" that the pattern string is not an option by quoting the\nfirst dash (i.e. \"\\--dirstat\") is clever, and it is more portable than\nusing an explicit \"-e\" to accomodate ancient implementations of grep.\n\n\tSide note: we seem to already use \"grep -e\" in some other tests\n\t(2200, 2204 and 5540).  We probably should get rid of -e from\n\tthese places.\n\n> +test_expect_success 'diff.dirstat=future_param,0,lines should warn, but still work' '\n> +\tgit -c diff.dirstat=future_param,0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&\n> +\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n> +\tgrep -q \"future_param\" actual_error &&\n> +\tgrep -q \"diff.dirstat\" actual_error &&\n\nThis should avoid matching \".\" with anything, i.e.\n\n\tgrep -q \"diff\\\\.dirstat\" actual_error &&\n\n t/t4047-diff-dirstat.sh |   25 ++++++++-----------------\n 1 files changed, 8 insertions(+), 17 deletions(-)\n\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nindex 20a59ac..0942bdb 100755\n--- a/t/t4047-diff-dirstat.sh\n+++ b/t/t4047-diff-dirstat.sh\n@@ -934,32 +934,23 @@ test_expect_success 'diff.dirstat=0,lines' '\n \n test_expect_success '--dirstat=future_param,lines,0 should fail loudly' '\n \ttest_must_fail git diff --dirstat=future_param,lines,0 HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&\n+\ttest_debug \"cat actual_error\" &&\n \ttest_cmp /dev/null actual_diff_dirstat &&\n \tgrep -q \"future_param\" actual_error &&\n-\tgrep -q \"\\--dirstat\" actual_error &&\n-\ttest_must_fail git diff --dirstat=future_param,lines,0 -M HEAD^..HEAD >actual_diff_dirstat_M 2>actual_error &&\n-\ttest_cmp /dev/null actual_diff_dirstat_M &&\n-\tgrep -q \"future_param\" actual_error &&\n-\tgrep -q \"\\--dirstat\" actual_error &&\n-\ttest_must_fail git diff --dirstat=future_param,lines,0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC 2>actual_error &&\n-\ttest_cmp /dev/null actual_diff_dirstat_CC &&\n-\tgrep -q \"future_param\" actual_error &&\n \tgrep -q \"\\--dirstat\" actual_error\n '\n \n test_expect_success 'diff.dirstat=future_param,0,lines should warn, but still work' '\n \tgit -c diff.dirstat=future_param,0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&\n+\ttest_debug \"cat actual_error\" &&\n \ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n \tgrep -q \"future_param\" actual_error &&\n-\tgrep -q \"diff.dirstat\" actual_error &&\n-\tgit -c diff.dirstat=future_param,0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M 2>actual_error &&\n-\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n-\tgrep -q \"future_param\" actual_error &&\n-\tgrep -q \"diff.dirstat\" actual_error &&\n-\tgit -c diff.dirstat=future_param,0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC 2>actual_error &&\n-\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC &&\n-\tgrep -q \"future_param\" actual_error &&\n-\tgrep -q \"diff.dirstat\" actual_error\n+\tgrep -q \"diff\\\\.dirstat\" actual_error\n+'\n+\n+test_expect_success 'various ways to misspell --dirstat' '\n+\ttest_must_fail git show --dirstat10,files &&\n+\ttest_must_fail git show -X=20\n '\n \n test_done\n"},{"id":"166678","messageId":"7vd3k641zn.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"7vhb9i43sl.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv5 7/7] Improve error handling when parsing dirstat parameters","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-28T19:20:12Z","receivedAt":"2011-04-28T19:20:12Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I am not sure if three combinations (vanilla, -M and -C -C) need to be\n> tested to produce an empty result.  If so, it would make it easier to read\n> if they are split into three tests, or at least have a blank line between\n> them, but I suspect that you would agree that it is not worth to have\n> three separate test_expect_success for these.\n\nI think it makes sense to cull these three cases into one for the case we\nexpect the command to stop without doing anything, but we would still want\nto validate the output for three variants in the \"config\" case.\n\nAlso I forgot to say that the new \"grep\" invocations added to check the\nerror output might have to be test_i18ngrep.  Please check with\n\n    make GETTEXT_POISON=YesPlease test\n\nThe configuration variable names and typo in user input should appear\nsomewhere in the output for any real locale, but I think gettext-poison\nwould throw these away.\n\nBy the way, should the following two entries make any difference, and if\nso how?\n\n\t[diff]\n\n        \tdirstat = unknown,0,lines\n                dirstat = 0,lines,unknown\n"},{"id":"166699","messageId":"201104290113.26481.johan@herland.net","threadId":"27015","inReplyTo":"7vhb9i43sl.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv5 7/7] Improve error handling when parsing dirstat parameters","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-28T23:13:26Z","receivedAt":"2011-04-28T23:13:26Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"(I've snipped the comments that I don't dispute or need to comment on)\n\nOn Thursday 28 April 2011, Junio C Hamano wrote:\n> The error output shows \"error:\" followed by \"warning:\", which looked\n> somewhat questionable.  Perhaps allow a pointer to a structure be passed\n> in to describe the nature of a breakage to parse_dirstat_params()?\n\nNot sure what you mean here. You want the caller to supply a string_list, to \nwhich parse_dirstat_params() appends error messages, and then the caller \ndetermines how to display those error messages to the user after \nparse_dirstat_params() has returned?\n\nI'd rather go the simpler way, and simply turn the first \"error:\" into a \n\"warning:\". In other words, parse_dirstat_params() should only output \n\"warning:\", and then it's up to the caller whether to follow up with another \n\"warning:\" (in the diff.dirstat case), or a \"fatal:\" (in the --dirstat \ncase).\n\n\nOtherwise, I agree with all your comments, and the next re-roll will be \nupdated accordingly.\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"166700","messageId":"201104290116.28647.johan@herland.net","threadId":"27015","inReplyTo":"7vd3k641zn.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCHv5 7/7] Improve error handling when parsing dirstat parameters","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-28T23:16:28Z","receivedAt":"2011-04-28T23:16:28Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"On Thursday 28 April 2011, Junio C Hamano wrote:\n> Junio C Hamano <gitster@pobox.com> writes:\n> > I am not sure if three combinations (vanilla, -M and -C -C) need to be\n> > tested to produce an empty result.  If so, it would make it easier to\n> > read if they are split into three tests, or at least have a blank line\n> > between them, but I suspect that you would agree that it is not worth\n> > to have three separate test_expect_success for these.\n> \n> I think it makes sense to cull these three cases into one for the case we\n> expect the command to stop without doing anything, but we would still\n> want to validate the output for three variants in the \"config\" case.\n\nAgreed.\n\n> Also I forgot to say that the new \"grep\" invocations added to check the\n> error output might have to be test_i18ngrep.  Please check with\n> \n>     make GETTEXT_POISON=YesPlease test\n> \n> The configuration variable names and typo in user input should appear\n> somewhere in the output for any real locale, but I think gettext-poison\n> would throw these away.\n\nHmm. Not exactly sure how this is supposed to work. I ran the above command \n(after a test merge with 'pu' to get GETTEXT_POISON in my working tree), and \nit succeeded. But then, I have not marked my added strings for translation \nwith \"_()\". Should I? AFAICS no other strings in diff.c are marked for \ntranslation either...\n\n> By the way, should the following two entries make any difference, and if\n> so how?\n> \n> \t[diff]\n>           dirstat = unknown,0,lines\n>           dirstat = 0,lines,unknown\n\nNo difference. The \"rules\" that apply in this case are:\n- Tokens are separated by commas\n- Unrecognized tokens are ignored\n\nThis is fundamentally what 7/7 tries to accomplish.\n\n\n...Johan\n\n-- \nJohan Herland, <johan@herland.net>\nwww.herland.net\n"},{"id":"166705","messageId":"7vfwp13dme.fsf@alter.siamese.dyndns.org","threadId":"27015","inReplyTo":"201104290113.26481.johan@herland.net","subject":"Re: [PATCHv5 7/7] Improve error handling when parsing dirstat parameters","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-04-29T04:06:33Z","receivedAt":"2011-04-29T04:06:33Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johan Herland <johan@herland.net> writes:\n\n> Not sure what you mean here. You want the caller to supply a\n> string_list, to which parse_dirstat_params() appends error messages, and\n> then the caller determines how to display those error messages to the\n> user after parse_dirstat_params() has returned?\n\nA rough outline of what I had in mind was:\n\n\tstruct dirstat_param_error {\n        \tenum {\n                \tERR_DIRSTAT_PERCENT = 1,\n                        ERR_DIRSTAT_UNKNOWN\n\t\t} kind;\n                strbuf msg;\n\t};\n\n\tstatic int parse_dirstat_params(struct diff_options *options,\n\t\t        \tconst char *params,\n\t\t\t\tstruct dirstat_param_error *errinfo)\n\t{\n\t\twhile (...) {\n                        ...\n                        else if (isdigit(*p)) {\n                                ...\n                                if (end - p == p_len)\n                                        options->dirstat_permille = permille;\n                                else {\n                                        errinfo->kind = ERR_DIRSTAT_PERCENT;\n                                        strbuf_add(&errinfo->msg, p, p_len);\n                                        ret = -1;\n                                }\n                        } else {\n                                errinfo->kind = ERR_DIRSTAT_UNKNOWN;\n                                strbuf_add(&errinfo->sb, p, p_len);\n                                ret = -1;\n                        }\n                        p += p_len;\n                        if (*p)\n                                p++;\n                }\n                return ret;\n\t}\n\nand then the caller can extract the information to format.\n\nBut you produce more than one one error messages, so a single errinfo\napproach would not work.  Instead, we should be able to pass in the\npointer to a single strbuf errmsg, and accumulate the errors in it by\ncalling strbuf_addf() for the same effect.  The format string given to\nstrbuf_addf() may probably need to be marked with _().\n\nThe caller can then check errmsg->len to see if there was an error.\n"},{"id":"166716","messageId":"1304069783-22739-1-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"7vfwp13dme.fsf@alter.siamese.dyndns.org","subject":"[PATCHv6 0/8] --dirstat fixes, part 2","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-29T09:36:15Z","receivedAt":"2011-04-29T09:36:15Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Hi,\n\nHere's version 6 incorporating Junio's feedback regarding tests,\nerror messages, and i18n. The following patches have changed:\n\n - 1/8: Added test: 'various ways to misspell --dirstat'\n\n - 3/8: Added a couple of more misspellings to the above test\n\n - 7/8: Improved error message handling, and tests covering error messages\n\n - 8/8: New patch marking dirstat error messages for translation.\n\nThe new 8/8 patch WILL NOT COMPILE until the topic branch is merged with\nan i18n/gettext-aware branch (i.e. where _() and test_i18ngrep is present).\n\n\nHave fun! :)\n\n...Johan\n\nJohan Herland (8):\n  Add several testcases for --dirstat and friends\n  Make --dirstat=0 output directories that contribute < 0.1% of changes\n  Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file\n  Add config variable for specifying default --dirstat behavior\n  Allow specifying --dirstat cut-off percentage as a floating point number\n  New --dirstat=lines mode, doing dirstat analysis based on diffstat\n  Improve error handling when parsing dirstat parameters\n  Mark dirstat error messages for translation\n\n Documentation/config.txt       |   44 ++\n Documentation/diff-options.txt |   54 ++-\n diff.c                         |  171 +++++++-\n diff.h                         |    3 +-\n t/t4047-diff-dirstat.sh        |  979 ++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 1219 insertions(+), 32 deletions(-)\n create mode 100755 t/t4047-diff-dirstat.sh\n\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166718","messageId":"1304069783-22739-2-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1304069783-22739-1-git-send-email-johan@herland.net","subject":"[PATCHv6 1/8] Add several testcases for --dirstat and friends","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-29T09:36:16Z","receivedAt":"2011-04-29T09:36:16Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Currently, t4013 is the only selftest that exercises the --dirstat machinery,\nbut it only does a superficial verification of --dirstat's output.\n\nThis patch adds a new selftest - t4047-diff-dirstat.sh - which prepares a\ncommit containing:\n - unchanged files, changed files and files with rearranged lines\n - copied files, moved files, and unmoved files\n\nIt then verifies the correct dirstat output for that commit in the following\ndirstat modes:\n - --dirstat\n - -X\n - --dirstat=0\n - -X0\n - --cumulative\n - --dirstat-by-file\n - (plus combinations of the above)\n\nEach of the above tests are also run with:\n - no rename detection\n - rename detection (-M)\n - expensive copy detection (-C -C)\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n t/t4047-diff-dirstat.sh |  585 +++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 585 insertions(+), 0 deletions(-)\n create mode 100755 t/t4047-diff-dirstat.sh\n\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nnew file mode 100755\nindex 0000000..ce7c403\n--- /dev/null\n+++ b/t/t4047-diff-dirstat.sh\n@@ -0,0 +1,585 @@\n+#!/bin/sh\n+\n+test_description='diff --dirstat tests'\n+. ./test-lib.sh\n+\n+# set up two commits where the second commit has these files\n+# (10 lines in each file):\n+#\n+#   unchanged/text           (unchanged from 1st commit)\n+#   changed/text             (changed 1st line)\n+#   rearranged/text          (swapped 1st and 2nd line)\n+#   dst/copy/unchanged/text  (copied from src/copy/unchanged/text, unchanged)\n+#   dst/copy/changed/text    (copied from src/copy/changed/text, changed)\n+#   dst/copy/rearranged/text (copied from src/copy/rearranged/text, rearranged)\n+#   dst/move/unchanged/text  (moved from src/move/unchanged/text, unchanged)\n+#   dst/move/changed/text    (moved from src/move/changed/text, changed)\n+#   dst/move/rearranged/text (moved from src/move/rearranged/text, rearranged)\n+\n+test_expect_success 'setup' '\n+\tmkdir unchanged &&\n+\tmkdir changed &&\n+\tmkdir rearranged &&\n+\tmkdir src &&\n+\tmkdir src/copy &&\n+\tmkdir src/copy/unchanged &&\n+\tmkdir src/copy/changed &&\n+\tmkdir src/copy/rearranged &&\n+\tmkdir src/move &&\n+\tmkdir src/move/unchanged &&\n+\tmkdir src/move/changed &&\n+\tmkdir src/move/rearranged &&\n+\tcat <<EOF >unchanged/text &&\n+unchanged       line #0\n+unchanged       line #1\n+unchanged       line #2\n+unchanged       line #3\n+unchanged       line #4\n+unchanged       line #5\n+unchanged       line #6\n+unchanged       line #7\n+unchanged       line #8\n+unchanged       line #9\n+EOF\n+\tcat <<EOF >changed/text &&\n+changed         line #0\n+changed         line #1\n+changed         line #2\n+changed         line #3\n+changed         line #4\n+changed         line #5\n+changed         line #6\n+changed         line #7\n+changed         line #8\n+changed         line #9\n+EOF\n+\tcat <<EOF >rearranged/text &&\n+rearranged      line #0\n+rearranged      line #1\n+rearranged      line #2\n+rearranged      line #3\n+rearranged      line #4\n+rearranged      line #5\n+rearranged      line #6\n+rearranged      line #7\n+rearranged      line #8\n+rearranged      line #9\n+EOF\n+\tcat <<EOF >src/copy/unchanged/text &&\n+copy  unchanged line #0\n+copy  unchanged line #1\n+copy  unchanged line #2\n+copy  unchanged line #3\n+copy  unchanged line #4\n+copy  unchanged line #5\n+copy  unchanged line #6\n+copy  unchanged line #7\n+copy  unchanged line #8\n+copy  unchanged line #9\n+EOF\n+\tcat <<EOF >src/copy/changed/text &&\n+copy    changed line #0\n+copy    changed line #1\n+copy    changed line #2\n+copy    changed line #3\n+copy    changed line #4\n+copy    changed line #5\n+copy    changed line #6\n+copy    changed line #7\n+copy    changed line #8\n+copy    changed line #9\n+EOF\n+\tcat <<EOF >src/copy/rearranged/text &&\n+copy rearranged line #0\n+copy rearranged line #1\n+copy rearranged line #2\n+copy rearranged line #3\n+copy rearranged line #4\n+copy rearranged line #5\n+copy rearranged line #6\n+copy rearranged line #7\n+copy rearranged line #8\n+copy rearranged line #9\n+EOF\n+\tcat <<EOF >src/move/unchanged/text &&\n+move  unchanged line #0\n+move  unchanged line #1\n+move  unchanged line #2\n+move  unchanged line #3\n+move  unchanged line #4\n+move  unchanged line #5\n+move  unchanged line #6\n+move  unchanged line #7\n+move  unchanged line #8\n+move  unchanged line #9\n+EOF\n+\tcat <<EOF >src/move/changed/text &&\n+move    changed line #0\n+move    changed line #1\n+move    changed line #2\n+move    changed line #3\n+move    changed line #4\n+move    changed line #5\n+move    changed line #6\n+move    changed line #7\n+move    changed line #8\n+move    changed line #9\n+EOF\n+\tcat <<EOF >src/move/rearranged/text &&\n+move rearranged line #0\n+move rearranged line #1\n+move rearranged line #2\n+move rearranged line #3\n+move rearranged line #4\n+move rearranged line #5\n+move rearranged line #6\n+move rearranged line #7\n+move rearranged line #8\n+move rearranged line #9\n+EOF\n+\tgit add . &&\n+\tgit commit -m \"initial\" &&\n+\tmkdir dst &&\n+\tmkdir dst/copy &&\n+\tmkdir dst/copy/unchanged &&\n+\tmkdir dst/copy/changed &&\n+\tmkdir dst/copy/rearranged &&\n+\tmkdir dst/move &&\n+\tmkdir dst/move/unchanged &&\n+\tmkdir dst/move/changed &&\n+\tmkdir dst/move/rearranged &&\n+\tcat <<EOF >changed/text &&\n+CHANGED XXXXXXX line #0\n+changed         line #1\n+changed         line #2\n+changed         line #3\n+changed         line #4\n+changed         line #5\n+changed         line #6\n+changed         line #7\n+changed         line #8\n+changed         line #9\n+EOF\n+\tcat <<EOF >rearranged/text &&\n+rearranged      line #1\n+rearranged      line #0\n+rearranged      line #2\n+rearranged      line #3\n+rearranged      line #4\n+rearranged      line #5\n+rearranged      line #6\n+rearranged      line #7\n+rearranged      line #8\n+rearranged      line #9\n+EOF\n+\tcat <<EOF >dst/copy/unchanged/text &&\n+copy  unchanged line #0\n+copy  unchanged line #1\n+copy  unchanged line #2\n+copy  unchanged line #3\n+copy  unchanged line #4\n+copy  unchanged line #5\n+copy  unchanged line #6\n+copy  unchanged line #7\n+copy  unchanged line #8\n+copy  unchanged line #9\n+EOF\n+\tcat <<EOF >dst/copy/changed/text &&\n+copy XXXCHANGED line #0\n+copy    changed line #1\n+copy    changed line #2\n+copy    changed line #3\n+copy    changed line #4\n+copy    changed line #5\n+copy    changed line #6\n+copy    changed line #7\n+copy    changed line #8\n+copy    changed line #9\n+EOF\n+\tcat <<EOF >dst/copy/rearranged/text &&\n+copy rearranged line #1\n+copy rearranged line #0\n+copy rearranged line #2\n+copy rearranged line #3\n+copy rearranged line #4\n+copy rearranged line #5\n+copy rearranged line #6\n+copy rearranged line #7\n+copy rearranged line #8\n+copy rearranged line #9\n+EOF\n+\tcat <<EOF >dst/move/unchanged/text &&\n+move  unchanged line #0\n+move  unchanged line #1\n+move  unchanged line #2\n+move  unchanged line #3\n+move  unchanged line #4\n+move  unchanged line #5\n+move  unchanged line #6\n+move  unchanged line #7\n+move  unchanged line #8\n+move  unchanged line #9\n+EOF\n+\tcat <<EOF >dst/move/changed/text &&\n+move XXXCHANGED line #0\n+move    changed line #1\n+move    changed line #2\n+move    changed line #3\n+move    changed line #4\n+move    changed line #5\n+move    changed line #6\n+move    changed line #7\n+move    changed line #8\n+move    changed line #9\n+EOF\n+\tcat <<EOF >dst/move/rearranged/text &&\n+move rearranged line #1\n+move rearranged line #0\n+move rearranged line #2\n+move rearranged line #3\n+move rearranged line #4\n+move rearranged line #5\n+move rearranged line #6\n+move rearranged line #7\n+move rearranged line #8\n+move rearranged line #9\n+EOF\n+\tgit add . &&\n+\tgit rm -r src/move/unchanged &&\n+\tgit rm -r src/move/changed &&\n+\tgit rm -r src/move/rearranged &&\n+\tgit commit -m \"changes\"\n+'\n+\n+cat <<EOF >expect_diff_stat\n+ changed/text             |    2 +-\n+ dst/copy/changed/text    |   10 ++++++++++\n+ dst/copy/rearranged/text |   10 ++++++++++\n+ dst/copy/unchanged/text  |   10 ++++++++++\n+ dst/move/changed/text    |   10 ++++++++++\n+ dst/move/rearranged/text |   10 ++++++++++\n+ dst/move/unchanged/text  |   10 ++++++++++\n+ rearranged/text          |    2 +-\n+ src/move/changed/text    |   10 ----------\n+ src/move/rearranged/text |   10 ----------\n+ src/move/unchanged/text  |   10 ----------\n+ 11 files changed, 62 insertions(+), 32 deletions(-)\n+EOF\n+\n+cat <<EOF >expect_diff_stat_M\n+ changed/text                      |    2 +-\n+ dst/copy/changed/text             |   10 ++++++++++\n+ dst/copy/rearranged/text          |   10 ++++++++++\n+ dst/copy/unchanged/text           |   10 ++++++++++\n+ {src => dst}/move/changed/text    |    2 +-\n+ {src => dst}/move/rearranged/text |    2 +-\n+ {src => dst}/move/unchanged/text  |    0\n+ rearranged/text                   |    2 +-\n+ 8 files changed, 34 insertions(+), 4 deletions(-)\n+EOF\n+\n+cat <<EOF >expect_diff_stat_CC\n+ changed/text                      |    2 +-\n+ {src => dst}/copy/changed/text    |    2 +-\n+ {src => dst}/copy/rearranged/text |    2 +-\n+ {src => dst}/copy/unchanged/text  |    0\n+ {src => dst}/move/changed/text    |    2 +-\n+ {src => dst}/move/rearranged/text |    2 +-\n+ {src => dst}/move/unchanged/text  |    0\n+ rearranged/text                   |    2 +-\n+ 8 files changed, 6 insertions(+), 6 deletions(-)\n+EOF\n+\n+test_expect_success 'sanity check setup (--stat)' '\n+\tgit diff --stat HEAD^..HEAD >actual_diff_stat &&\n+\ttest_cmp expect_diff_stat actual_diff_stat &&\n+\tgit diff --stat -M HEAD^..HEAD >actual_diff_stat_M &&\n+\ttest_cmp expect_diff_stat_M actual_diff_stat_M &&\n+\tgit diff --stat -C -C HEAD^..HEAD >actual_diff_stat_CC &&\n+\ttest_cmp expect_diff_stat_CC actual_diff_stat_CC\n+'\n+\n+# changed/text and rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+EOF\n+\n+# rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+   5.8% dst/move/changed/\n+EOF\n+\n+# rearranged/text falls below default 3% threshold\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+  32.6% dst/move/changed/\n+EOF\n+\n+test_expect_success 'various ways to misspell --dirstat' '\n+\ttest_must_fail git show --dirstat10 &&\n+\ttest_must_fail git show -X=20\n+'\n+\n+test_expect_success 'vanilla --dirstat' '\n+\tgit diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'vanilla -X' '\n+\tgit diff -X HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -X -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -X -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+   5.8% dst/move/changed/\n+   0.1% dst/move/rearranged/\n+   0.1% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+   0.6% dst/copy/rearranged/\n+  32.6% dst/move/changed/\n+   0.6% dst/move/rearranged/\n+   0.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=0' '\n+\tgit diff --dirstat=0 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success '-X0' '\n+\tgit diff -X0 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -X0 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -X0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.8% dst/copy/changed/\n+  10.8% dst/copy/rearranged/\n+  10.8% dst/copy/unchanged/\n+  32.5% dst/copy/\n+  10.8% dst/move/changed/\n+  10.8% dst/move/rearranged/\n+  10.8% dst/move/unchanged/\n+  32.5% dst/move/\n+  65.1% dst/\n+  10.8% src/move/changed/\n+  10.8% src/move/rearranged/\n+  10.8% src/move/unchanged/\n+  32.5% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.8% changed/\n+  29.3% dst/copy/changed/\n+  29.3% dst/copy/rearranged/\n+  29.3% dst/copy/unchanged/\n+  88.0% dst/copy/\n+   5.8% dst/move/changed/\n+   0.1% dst/move/rearranged/\n+   5.9% dst/move/\n+  94.0% dst/\n+   0.1% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  32.6% changed/\n+  32.6% dst/copy/changed/\n+   0.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  32.6% dst/move/changed/\n+   0.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+   0.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=0 --cumulative' '\n+\tgit diff --dirstat=0 --cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0 --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0 --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   9.0% changed/\n+   9.0% dst/copy/changed/\n+   9.0% dst/copy/rearranged/\n+   9.0% dst/copy/unchanged/\n+   9.0% dst/move/changed/\n+   9.0% dst/move/rearranged/\n+   9.0% dst/move/unchanged/\n+   9.0% rearranged/\n+   9.0% src/move/changed/\n+   9.0% src/move/rearranged/\n+   9.0% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file' '\n+\tgit diff --dirstat-by-file HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+  27.2% dst/copy/\n+  27.2% dst/move/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file=10' '\n+\tgit diff --dirstat-by-file=10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file=10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file=10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   9.0% changed/\n+   9.0% dst/copy/changed/\n+   9.0% dst/copy/rearranged/\n+   9.0% dst/copy/unchanged/\n+  27.2% dst/copy/\n+   9.0% dst/move/changed/\n+   9.0% dst/move/rearranged/\n+   9.0% dst/move/unchanged/\n+  27.2% dst/move/\n+  54.5% dst/\n+   9.0% rearranged/\n+   9.0% src/move/changed/\n+   9.0% src/move/rearranged/\n+   9.0% src/move/unchanged/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  42.8% dst/copy/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  28.5% dst/move/\n+  71.4% dst/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat-by-file --cumulative' '\n+\tgit diff --dirstat-by-file --cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat-by-file --cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat-by-file --cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166717","messageId":"1304069783-22739-3-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1304069783-22739-1-git-send-email-johan@herland.net","subject":"[PATCHv6 2/8] Make --dirstat=0 output directories that contribute < 0.1% of changes","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-29T09:36:17Z","receivedAt":"2011-04-29T09:36:17Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"The expected output from --dirstat=0, is to include any directory with\nchanges, even if those changes contribute a minuscule portion of the total\nchanges. However, currently, directories that contribute less than 0.1% are\nnot included, since their 'permille' value is 0, and there is an\n'if (permille)' check in gather_dirstat() that causes them to be ignored.\n\nThis test is obviously intended to exclude directories that contribute no\nchanges whatsoever, but in this case, it hits too broadly. The correct\ncheck is against 'this_dir' from which the permille is calculated. Only if\nthis value is 0 does the directory truly contribute no changes, and should\nbe skipped from the output.\n\nThis patches fixes this issue, and updates corresponding testcases to\nexpect the new behvaior.\n\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n diff.c                  |    4 ++--\n t/t4047-diff-dirstat.sh |    4 ++--\n 2 files changed, 4 insertions(+), 4 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex abd9cd5..cfbfa92 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1500,8 +1500,8 @@ static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n \t *    under this directory (sources == 1).\n \t */\n \tif (baselen && sources != 1) {\n-\t\tint permille = this_dir * 1000 / changed;\n-\t\tif (permille) {\n+\t\tif (this_dir) {\n+\t\t\tint permille = this_dir * 1000 / changed;\n \t\t\tint percent = permille / 10;\n \t\t\tif (percent >= dir->percent) {\n \t\t\t\tfprintf(opt->file, \"%s%4d.%01d%% %.*s\\n\", line_prefix,\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nindex ce7c403..1c5adad 100755\n--- a/t/t4047-diff-dirstat.sh\n+++ b/t/t4047-diff-dirstat.sh\n@@ -351,7 +351,6 @@ test_expect_success 'vanilla -X' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n-# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -360,6 +359,7 @@ cat <<EOF >expect_diff_dirstat\n   10.8% dst/move/changed/\n   10.8% dst/move/rearranged/\n   10.8% dst/move/unchanged/\n+   0.0% rearranged/\n   10.8% src/move/changed/\n   10.8% src/move/rearranged/\n   10.8% src/move/unchanged/\n@@ -402,7 +402,6 @@ test_expect_success '-X0' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n-# rearranged/text falls below 0% threshold (1 / (240 * 9 + 48 + 1) ~= 0.045 %)\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -414,6 +413,7 @@ cat <<EOF >expect_diff_dirstat\n   10.8% dst/move/unchanged/\n   32.5% dst/move/\n   65.1% dst/\n+   0.0% rearranged/\n   10.8% src/move/changed/\n   10.8% src/move/rearranged/\n   10.8% src/move/unchanged/\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166720","messageId":"1304069783-22739-4-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1304069783-22739-1-git-send-email-johan@herland.net","subject":"[PATCHv6 3/8] Refactor --dirstat parsing; deprecate --cumulative and --dirstat-by-file","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-29T09:36:18Z","receivedAt":"2011-04-29T09:36:18Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Instead of having multiple interconnected dirstat-related options, teach\nthe --dirstat option itself to accept all behavior modifiers as parameters.\n\n - Preserve the current --dirstat=<limit> (where <limit> is an integer\n   specifying a cut-off percentage)\n - Add --dirstat=cumulative, replacing --cumulative\n - Add --dirstat=files, replacing --dirstat-by-file\n - Also add --dirstat=changes and --dirstat=noncumulative for specifying the\n   current default behavior. These allow the user to reset other --dirstat\n   parameters (e.g. 'cumulative' and 'files') occuring earlier on the\n   command line.\n\nThe deprecated options (--cumulative and --dirstat-by-file) are still\nfunctional, although they have been removed from the documentation.\n\nAllow multiple parameters to be separated by commas, e.g.:\n  --dirstat=files,10,cumulative\n\nUpdate the documentation accordingly, and add testcases verifying the\nbehavior of the new syntax.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/diff-options.txt |   44 ++++++++++----\n diff.c                         |   69 +++++++++++++++++++---\n t/t4047-diff-dirstat.sh        |  123 +++++++++++++++++++++++++++++++++++++++-\n 3 files changed, 214 insertions(+), 22 deletions(-)\n\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 7e4bd42..6a3a9c1 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -66,19 +66,39 @@ endif::git-format-patch[]\n \tnumber of modified files, as well as number of added and deleted\n \tlines.\n \n---dirstat[=<limit>]::\n-\tOutput the distribution of relative amount of changes (number of lines added or\n-\tremoved) for each sub-directory. Directories with changes below\n-\ta cut-off percent (3% by default) are not shown. The cut-off percent\n-\tcan be set with `--dirstat=<limit>`. Changes in a child directory are not\n-\tcounted for the parent directory, unless `--cumulative` is used.\n+--dirstat[=<param1,param2,...>]::\n+\tOutput the distribution of relative amount of changes for each\n+\tsub-directory. The behavior of `--dirstat` can be customized by\n+\tpassing it a comma separated list of parameters.\n+\tThe following parameters are available:\n +\n-Note that the `--dirstat` option computes the changes while ignoring\n-the amount of pure code movements within a file.  In other words,\n-rearranging lines in a file is not counted as much as other changes.\n-\n---dirstat-by-file[=<limit>]::\n-\tSame as `--dirstat`, but counts changed files instead of lines.\n+--\n+`changes`;;\n+\tCompute the dirstat numbers by counting the lines that have been\n+\tremoved from the source, or added to the destination. This ignores\n+\tthe amount of pure code movements within a file.  In other words,\n+\trearranging lines in a file is not counted as much as other changes.\n+\tThis is the default behavior when no parameter is given.\n+`files`;;\n+\tCompute the dirstat numbers by counting the number of files changed.\n+\tEach changed file counts equally in the dirstat analysis. This is\n+\tthe computationally cheapest `--dirstat` behavior, since it does\n+\tnot have to look at the file contents at all.\n+`cumulative`;;\n+\tCount changes in a child directory for the parent directory as well.\n+\tNote that when using `cumulative`, the sum of the percentages\n+\treported may exceed 100%. The default (non-cumulative) behavior can\n+\tbe specified with the `noncumulative` parameter.\n+<limit>;;\n+\tAn integer parameter specifies a cut-off percent (3% by default).\n+\tDirectories contributing less than this percentage of the changes\n+\tare not shown in the output.\n+--\n++\n+Example: The following will count changed files, while ignoring\n+directories with less than 10% of the total amount of changed files,\n+and accumulating child directory counts in the parent directories:\n+`--dirstat=files,10,cumulative`.\n \n --summary::\n \tOutput a condensed summary of extended header information\ndiff --git a/diff.c b/diff.c\nindex cfbfa92..0e4a510 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -66,6 +66,41 @@ static int parse_diff_color_slot(const char *var, int ofs)\n \treturn -1;\n }\n \n+static int parse_dirstat_params(struct diff_options *options, const char *params)\n+{\n+\tconst char *p = params;\n+\twhile (*p) {\n+\t\tif (!prefixcmp(p, \"changes\")) {\n+\t\t\tp += 7;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n+\t\t} else if (!prefixcmp(p, \"files\")) {\n+\t\t\tp += 5;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n+\t\t} else if (!prefixcmp(p, \"noncumulative\")) {\n+\t\t\tp += 13;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);\n+\t\t} else if (!prefixcmp(p, \"cumulative\")) {\n+\t\t\tp += 10;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n+\t\t} else if (isdigit(*p)) {\n+\t\t\tchar *end;\n+\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n+\t\t\tp = end;\n+\t\t} else\n+\t\t\treturn error(\"Unknown --dirstat parameter '%s'\", p);\n+\n+\t\tif (*p) {\n+\t\t\t/* more parameters, swallow separator */\n+\t\t\tif (*p != ',')\n+\t\t\t\treturn error(\"Missing comma separator at char \"\n+\t\t\t\t\t\"%\"PRIuMAX\" of '%s'\",\n+\t\t\t\t\t(uintmax_t) (p - params), params);\n+\t\t\tp++;\n+\t\t}\n+\t}\n+\treturn 0;\n+}\n+\n static int git_config_rename(const char *var, const char *value)\n {\n \tif (!value)\n@@ -3144,6 +3179,18 @@ static int stat_opt(struct diff_options *options, const char **av)\n \treturn argcount;\n }\n \n+static int parse_dirstat_opt(struct diff_options *options, const char *params)\n+{\n+\tif (parse_dirstat_params(options, params))\n+\t\tdie(\"Failed to parse --dirstat/-X option parameter\");\n+\t/*\n+\t * The caller knows a dirstat-related option is given from the command\n+\t * line; allow it to say \"return this_function();\"\n+\t */\n+\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n+\treturn 1;\n+}\n+\n int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n {\n \tconst char *arg = av[0];\n@@ -3163,15 +3210,19 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)\n \t\toptions->output_format |= DIFF_FORMAT_NUMSTAT;\n \telse if (!strcmp(arg, \"--shortstat\"))\n \t\toptions->output_format |= DIFF_FORMAT_SHORTSTAT;\n-\telse if (opt_arg(arg, 'X', \"dirstat\", &options->dirstat_percent))\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\telse if (!strcmp(arg, \"--cumulative\")) {\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n-\t} else if (opt_arg(arg, 0, \"dirstat-by-file\",\n-\t\t\t   &options->dirstat_percent)) {\n-\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n-\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n+\telse if (!strcmp(arg, \"-X\") || !strcmp(arg, \"--dirstat\"))\n+\t\treturn parse_dirstat_opt(options, \"\");\n+\telse if (!prefixcmp(arg, \"-X\"))\n+\t\treturn parse_dirstat_opt(options, arg + 2);\n+\telse if (!prefixcmp(arg, \"--dirstat=\"))\n+\t\treturn parse_dirstat_opt(options, arg + 10);\n+\telse if (!strcmp(arg, \"--cumulative\"))\n+\t\treturn parse_dirstat_opt(options, \"cumulative\");\n+\telse if (!strcmp(arg, \"--dirstat-by-file\"))\n+\t\treturn parse_dirstat_opt(options, \"files\");\n+\telse if (!prefixcmp(arg, \"--dirstat-by-file=\")) {\n+\t\tparse_dirstat_opt(options, \"files\");\n+\t\treturn parse_dirstat_opt(options, arg + 18);\n \t}\n \telse if (!strcmp(arg, \"--check\"))\n \t\toptions->output_format |= DIFF_FORMAT_CHECKDIFF;\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nindex 1c5adad..d0ed62c 100755\n--- a/t/t4047-diff-dirstat.sh\n+++ b/t/t4047-diff-dirstat.sh\n@@ -330,7 +330,9 @@ EOF\n \n test_expect_success 'various ways to misspell --dirstat' '\n \ttest_must_fail git show --dirstat10 &&\n-\ttest_must_fail git show -X=20\n+\ttest_must_fail git show --dirstat10,files &&\n+\ttest_must_fail git show -X=20 &&\n+\ttest_must_fail git show -X=20,cumulative\n '\n \n test_expect_success 'vanilla --dirstat' '\n@@ -351,6 +353,39 @@ test_expect_success 'vanilla -X' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'explicit defaults: --dirstat=changes,noncumulative,3' '\n+\tgit diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'explicit defaults: -Xchanges,noncumulative,3' '\n+\tgit diff -Xchanges,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -Xchanges,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -Xchanges,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'later options override earlier options:' '\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,10,cumulative,changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files --dirstat=10 --dirstat=cumulative --dirstat=changes --dirstat=noncumulative -X3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -454,6 +489,24 @@ test_expect_success '--dirstat=0 --cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=0,cumulative' '\n+\tgit diff --dirstat=0,cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success '-X0,cumulative' '\n+\tgit diff -X0,cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff -X0,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff -X0,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -496,6 +549,15 @@ test_expect_success '--dirstat-by-file' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files' '\n+\tgit diff --dirstat=files HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -530,6 +592,15 @@ test_expect_success '--dirstat-by-file=10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files,10' '\n+\tgit diff --dirstat=files,10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -582,4 +653,54 @@ test_expect_success '--dirstat-by-file --cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=files,cumulative' '\n+\tgit diff --dirstat=files,cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+  27.2% dst/copy/\n+  27.2% dst/move/\n+  54.5% dst/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  14.2% changed/\n+  14.2% dst/copy/changed/\n+  14.2% dst/copy/rearranged/\n+  14.2% dst/copy/unchanged/\n+  42.8% dst/copy/\n+  14.2% dst/move/changed/\n+  14.2% dst/move/rearranged/\n+  28.5% dst/move/\n+  71.4% dst/\n+  14.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  33.3% dst/copy/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  33.3% dst/move/\n+  66.6% dst/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=files,cumulative,10' '\n+\tgit diff --dirstat=files,cumulative,10 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,10 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,10 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166722","messageId":"1304069783-22739-5-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1304069783-22739-1-git-send-email-johan@herland.net","subject":"[PATCHv6 4/8] Add config variable for specifying default --dirstat behavior","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-29T09:36:19Z","receivedAt":"2011-04-29T09:36:19Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"The new diff.dirstat config variable takes the same arguments as\n'--dirstat=<args>', and specifies the default arguments for --dirstat.\nThe config is obviously overridden by --dirstat arguments passed on the\ncommand line.\n\nWhen not specified, the --dirstat defaults are 'changes,noncumulative,3'.\n\nThe patch also adds several tests verifying the interaction between the\ndiff.dirstat config variable, and the --dirstat command line option.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/config.txt       |   36 ++++++++++++++++++++\n Documentation/diff-options.txt |    2 +\n diff.c                         |   10 +++++-\n t/t4047-diff-dirstat.sh        |   72 ++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 119 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 6babbc7..c18dd5a 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -822,6 +822,42 @@ diff.autorefreshindex::\n \taffects only 'git diff' Porcelain, and not lower level\n \t'diff' commands such as 'git diff-files'.\n \n+diff.dirstat::\n+\tA comma separated list of `--dirstat` parameters specifying the\n+\tdefault behavior of the `--dirstat` option to linkgit:git-diff[1]`\n+\tand friends. The defaults can be overridden on the command line\n+\t(using `--dirstat=<param1,param2,...>`). The fallback defaults\n+\t(when not changed by `diff.dirstat`) are `changes,noncumulative,3`.\n+\tThe following parameters are available:\n++\n+--\n+`changes`;;\n+\tCompute the dirstat numbers by counting the lines that have been\n+\tremoved from the source, or added to the destination. This ignores\n+\tthe amount of pure code movements within a file.  In other words,\n+\trearranging lines in a file is not counted as much as other changes.\n+\tThis is the default behavior when no parameter is given.\n+`files`;;\n+\tCompute the dirstat numbers by counting the number of files changed.\n+\tEach changed file counts equally in the dirstat analysis. This is\n+\tthe computationally cheapest `--dirstat` behavior, since it does\n+\tnot have to look at the file contents at all.\n+`cumulative`;;\n+\tCount changes in a child directory for the parent directory as well.\n+\tNote that when using `cumulative`, the sum of the percentages\n+\treported may exceed 100%. The default (non-cumulative) behavior can\n+\tbe specified with the `noncumulative` parameter.\n+<limit>;;\n+\tAn integer parameter specifies a cut-off percent (3% by default).\n+\tDirectories contributing less than this percentage of the changes\n+\tare not shown in the output.\n+--\n++\n+Example: The following will count changed files, while ignoring\n+directories with less than 10% of the total amount of changed files,\n+and accumulating child directory counts in the parent directories:\n+`files,10,cumulative`.\n+\n diff.external::\n \tIf this config variable is set, diff generation is not\n \tperformed using the internal diff machinery, but using the\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 6a3a9c1..4ad50b9 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -70,6 +70,8 @@ endif::git-format-patch[]\n \tOutput the distribution of relative amount of changes for each\n \tsub-directory. The behavior of `--dirstat` can be customized by\n \tpassing it a comma separated list of parameters.\n+\tThe defaults are controlled by the `diff.dirstat` configuration\n+\tvariable (see linkgit:git-config[1]).\n \tThe following parameters are available:\n +\n --\ndiff --git a/diff.c b/diff.c\nindex 0e4a510..92508b0 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -31,6 +31,7 @@ static const char *external_diff_cmd_cfg;\n int diff_auto_refresh_index = 1;\n static int diff_mnemonic_prefix;\n static int diff_no_prefix;\n+static int diff_dirstat_percent_default = 3;\n static struct diff_options default_diff_options;\n \n static char diff_colors[][COLOR_MAXLEN] = {\n@@ -180,6 +181,13 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"diff.dirstat\")) {\n+\t\tdefault_diff_options.dirstat_percent = diff_dirstat_percent_default;\n+\t\t(void) parse_dirstat_params(&default_diff_options, value);\n+\t\tdiff_dirstat_percent_default = default_diff_options.dirstat_percent;\n+\t\treturn 0;\n+\t}\n+\n \tif (!prefixcmp(var, \"submodule.\"))\n \t\treturn parse_submodule_config_option(var, value);\n \n@@ -2921,7 +2929,7 @@ void diff_setup(struct diff_options *options)\n \toptions->line_termination = '\\n';\n \toptions->break_opt = -1;\n \toptions->rename_limit = -1;\n-\toptions->dirstat_percent = 3;\n+\toptions->dirstat_percent = diff_dirstat_percent_default;\n \toptions->context = 3;\n \n \toptions->change = diff_change;\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nindex d0ed62c..c1b3697 100755\n--- a/t/t4047-diff-dirstat.sh\n+++ b/t/t4047-diff-dirstat.sh\n@@ -386,6 +386,15 @@ test_expect_success 'later options override earlier options:' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'non-defaults in config overridden by explicit defaults on command line' '\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=files,cumulative,50 diff --dirstat=changes,noncumulative,3 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -437,6 +446,15 @@ test_expect_success '-X0' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=0' '\n+\tgit -c diff.dirstat=0 diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0 diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0 diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    2.1% changed/\n   10.8% dst/copy/changed/\n@@ -507,6 +525,24 @@ test_expect_success '-X0,cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=0,cumulative' '\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0,cumulative diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=0 & --dirstat=cumulative' '\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0 diff --dirstat=cumulative -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -558,6 +594,15 @@ test_expect_success '--dirstat=files' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=files' '\n+\tgit -c diff.dirstat=files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -601,6 +646,15 @@ test_expect_success '--dirstat=files,10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=10,files' '\n+\tgit -c diff.dirstat=10,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=10,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=10,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n    9.0% changed/\n    9.0% dst/copy/changed/\n@@ -662,6 +716,15 @@ test_expect_success '--dirstat=files,cumulative' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=cumulative,files' '\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n cat <<EOF >expect_diff_dirstat\n   27.2% dst/copy/\n   27.2% dst/move/\n@@ -703,4 +766,13 @@ test_expect_success '--dirstat=files,cumulative,10' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success 'diff.dirstat=10,cumulative,files' '\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=10,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166721","messageId":"1304069783-22739-6-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1304069783-22739-1-git-send-email-johan@herland.net","subject":"[PATCHv6 5/8] Allow specifying --dirstat cut-off percentage as a floating point number","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-29T09:36:20Z","receivedAt":"2011-04-29T09:36:20Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Only the first digit after the decimal point is kept, as the dirstat\ncalculations all happen in permille.\n\nSelftests verifying floating-point percentage input has been added.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nImproved-by: Linus Torvalds <torvalds@linux-foundation.org>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n diff.c                  |   26 +++++++++++-------\n diff.h                  |    2 +-\n t/t4047-diff-dirstat.sh |   64 +++++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 81 insertions(+), 11 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 92508b0..e0de4fa 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -31,7 +31,7 @@ static const char *external_diff_cmd_cfg;\n int diff_auto_refresh_index = 1;\n static int diff_mnemonic_prefix;\n static int diff_no_prefix;\n-static int diff_dirstat_percent_default = 3;\n+static int diff_dirstat_permille_default = 30;\n static struct diff_options default_diff_options;\n \n static char diff_colors[][COLOR_MAXLEN] = {\n@@ -85,8 +85,15 @@ static int parse_dirstat_params(struct diff_options *options, const char *params\n \t\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n \t\t} else if (isdigit(*p)) {\n \t\t\tchar *end;\n-\t\t\toptions->dirstat_percent = strtoul(p, &end, 10);\n+\t\t\toptions->dirstat_permille = strtoul(p, &end, 10) * 10;\n \t\t\tp = end;\n+\t\t\tif (*p == '.' && isdigit(*++p)) {\n+\t\t\t\t/* only use first digit */\n+\t\t\t\toptions->dirstat_permille += *p - '0';\n+\t\t\t\t/* .. and ignore any further digits */\n+\t\t\t\twhile (isdigit(*++p))\n+\t\t\t\t\t; /* nothing */\n+\t\t\t}\n \t\t} else\n \t\t\treturn error(\"Unknown --dirstat parameter '%s'\", p);\n \n@@ -182,9 +189,9 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)\n \t}\n \n \tif (!strcmp(var, \"diff.dirstat\")) {\n-\t\tdefault_diff_options.dirstat_percent = diff_dirstat_percent_default;\n+\t\tdefault_diff_options.dirstat_permille = diff_dirstat_permille_default;\n \t\t(void) parse_dirstat_params(&default_diff_options, value);\n-\t\tdiff_dirstat_percent_default = default_diff_options.dirstat_percent;\n+\t\tdiff_dirstat_permille_default = default_diff_options.dirstat_permille;\n \t\treturn 0;\n \t}\n \n@@ -1496,7 +1503,7 @@ struct dirstat_file {\n \n struct dirstat_dir {\n \tstruct dirstat_file *files;\n-\tint alloc, nr, percent, cumulative;\n+\tint alloc, nr, permille, cumulative;\n };\n \n static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n@@ -1545,10 +1552,9 @@ static long gather_dirstat(struct diff_options *opt, struct dirstat_dir *dir,\n \tif (baselen && sources != 1) {\n \t\tif (this_dir) {\n \t\t\tint permille = this_dir * 1000 / changed;\n-\t\t\tint percent = permille / 10;\n-\t\t\tif (percent >= dir->percent) {\n+\t\t\tif (permille >= dir->permille) {\n \t\t\t\tfprintf(opt->file, \"%s%4d.%01d%% %.*s\\n\", line_prefix,\n-\t\t\t\t\tpercent, permille % 10, baselen, base);\n+\t\t\t\t\tpermille / 10, permille % 10, baselen, base);\n \t\t\t\tif (!dir->cumulative)\n \t\t\t\t\treturn 0;\n \t\t\t}\n@@ -1574,7 +1580,7 @@ static void show_dirstat(struct diff_options *options)\n \tdir.files = NULL;\n \tdir.alloc = 0;\n \tdir.nr = 0;\n-\tdir.percent = options->dirstat_percent;\n+\tdir.permille = options->dirstat_permille;\n \tdir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);\n \n \tchanged = 0;\n@@ -2929,7 +2935,7 @@ void diff_setup(struct diff_options *options)\n \toptions->line_termination = '\\n';\n \toptions->break_opt = -1;\n \toptions->rename_limit = -1;\n-\toptions->dirstat_percent = diff_dirstat_percent_default;\n+\toptions->dirstat_permille = diff_dirstat_permille_default;\n \toptions->context = 3;\n \n \toptions->change = diff_change;\ndiff --git a/diff.h b/diff.h\nindex 0083d92..08b4fe0 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -111,7 +111,7 @@ struct diff_options {\n \tint rename_score;\n \tint rename_limit;\n \tint warn_on_too_large_rename;\n-\tint dirstat_percent;\n+\tint dirstat_permille;\n \tint setup;\n \tint abbrev;\n \tconst char *prefix;\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nindex c1b3697..4b25e10 100755\n--- a/t/t4047-diff-dirstat.sh\n+++ b/t/t4047-diff-dirstat.sh\n@@ -775,4 +775,68 @@ test_expect_success 'diff.dirstat=10,cumulative,files' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+cat <<EOF >expect_diff_dirstat\n+  27.2% dst/copy/\n+  27.2% dst/move/\n+  54.5% dst/\n+  27.2% src/move/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+  42.8% dst/copy/\n+  28.5% dst/move/\n+  71.4% dst/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  33.3% dst/copy/\n+  33.3% dst/move/\n+  66.6% dst/\n+EOF\n+\n+test_expect_success '--dirstat=files,cumulative,16.7' '\n+\tgit diff --dirstat=files,cumulative,16.7 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,16.7 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,16.7 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=16.7,cumulative,files' '\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=16.7,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=16.70,cumulative,files' '\n+\tgit -c diff.dirstat=16.70,cumulative,files diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=16.70,cumulative,files diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=16.70,cumulative,files diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success '--dirstat=files,cumulative,27.2' '\n+\tgit diff --dirstat=files,cumulative,27.2 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,27.2 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,27.2 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success '--dirstat=files,cumulative,27.09' '\n+\tgit diff --dirstat=files,cumulative,27.09 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=files,cumulative,27.09 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=files,cumulative,27.09 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166719","messageId":"1304069783-22739-7-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1304069783-22739-1-git-send-email-johan@herland.net","subject":"[PATCHv6 6/8] New --dirstat=lines mode, doing dirstat analysis based on diffstat","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-29T09:36:21Z","receivedAt":"2011-04-29T09:36:21Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"This patch adds an alternative implementation of show_dirstat(), called\nshow_dirstat_by_line(), which uses the more expensive diffstat analysis\n(as opposed to show_dirstat()'s own (relatively inexpensive) analysis)\nto derive the numbers from which the --dirstat output is computed.\n\nThe alternative implementation is controlled by the new \"lines\" parameter\nto the --dirstat option (or the diff.dirstat config variable).\n\nFor binary files, the diffstat analysis counts bytes instead of lines,\nso to prevent binary files from dominating the dirstat results, the\nbyte counts for binary files are divided by 64 before being compared to\ntheir textual/line-based counterparts. This is a stupid and ugly - but\nvery cheap - heuristic.\n\nIn linux-2.6.git, running the three different --dirstat modes:\n\n  time git diff v2.6.20..v2.6.30 --dirstat=changes > /dev/null\nvs.\n  time git diff v2.6.20..v2.6.30 --dirstat=lines > /dev/null\nvs.\n  time git diff v2.6.20..v2.6.30 --dirstat=files > /dev/null\n\nyields the following average runtimes on my machine:\n\n - \"changes\" (default): ~6.0 s\n - \"lines\":             ~9.6 s\n - \"files\":             ~0.1 s\n\nSo, as expected, there's a considerable performance hit (~60%) by going\nthrough the full diffstat analysis as compared to the default \"changes\"\nanalysis (obviously, \"files\" is much faster than both). As such, the\n\"lines\" mode is probably only useful if you really need the --dirstat\nnumbers to be consistent with the numbers returned from the other\n--*stat options.\n\nThe patch also includes documentation and tests for the new dirstat mode.\n\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n Documentation/config.txt       |    8 +++\n Documentation/diff-options.txt |    8 +++\n diff.c                         |   61 +++++++++++++++++++++++-\n diff.h                         |    1 +\n t/t4047-diff-dirstat.sh        |  100 ++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 176 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex c18dd5a..0cad75c 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -837,6 +837,14 @@ diff.dirstat::\n \tthe amount of pure code movements within a file.  In other words,\n \trearranging lines in a file is not counted as much as other changes.\n \tThis is the default behavior when no parameter is given.\n+`lines`;;\n+\tCompute the dirstat numbers by doing the regular line-based diff\n+\tanalysis, and summing the removed/added line counts. (For binary\n+\tfiles, count 64-byte chunks instead, since binary files have no\n+\tnatural concept of lines). This is a more expensive `--dirstat`\n+\tbehavior than the `changes` behavior, but it does count rearranged\n+\tlines within a file as much as other changes. The resulting output\n+\tis consistent with what you get from the other `--*stat` options.\n `files`;;\n \tCompute the dirstat numbers by counting the number of files changed.\n \tEach changed file counts equally in the dirstat analysis. This is\ndiff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt\nindex 4ad50b9..327d10a 100644\n--- a/Documentation/diff-options.txt\n+++ b/Documentation/diff-options.txt\n@@ -81,6 +81,14 @@ endif::git-format-patch[]\n \tthe amount of pure code movements within a file.  In other words,\n \trearranging lines in a file is not counted as much as other changes.\n \tThis is the default behavior when no parameter is given.\n+`lines`;;\n+\tCompute the dirstat numbers by doing the regular line-based diff\n+\tanalysis, and summing the removed/added line counts. (For binary\n+\tfiles, count 64-byte chunks instead, since binary files have no\n+\tnatural concept of lines). This is a more expensive `--dirstat`\n+\tbehavior than the `changes` behavior, but it does count rearranged\n+\tlines within a file as much as other changes. The resulting output\n+\tis consistent with what you get from the other `--*stat` options.\n `files`;;\n \tCompute the dirstat numbers by counting the number of files changed.\n \tEach changed file counts equally in the dirstat analysis. This is\ndiff --git a/diff.c b/diff.c\nindex e0de4fa..8703763 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -73,9 +73,15 @@ static int parse_dirstat_params(struct diff_options *options, const char *params\n \twhile (*p) {\n \t\tif (!prefixcmp(p, \"changes\")) {\n \t\t\tp += 7;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_LINE);\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n+\t\t} else if (!prefixcmp(p, \"lines\")) {\n+\t\t\tp += 5;\n+\t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n \t\t} else if (!prefixcmp(p, \"files\")) {\n \t\t\tp += 5;\n+\t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n \t\t} else if (!prefixcmp(p, \"noncumulative\")) {\n \t\t\tp += 13;\n@@ -1669,6 +1675,50 @@ found_damage:\n \tgather_dirstat(options, &dir, changed, \"\", 0);\n }\n \n+static void show_dirstat_by_line(struct diffstat_t *data, struct diff_options *options)\n+{\n+\tint i;\n+\tunsigned long changed;\n+\tstruct dirstat_dir dir;\n+\n+\tif (data->nr == 0)\n+\t\treturn;\n+\n+\tdir.files = NULL;\n+\tdir.alloc = 0;\n+\tdir.nr = 0;\n+\tdir.permille = options->dirstat_permille;\n+\tdir.cumulative = DIFF_OPT_TST(options, DIRSTAT_CUMULATIVE);\n+\n+\tchanged = 0;\n+\tfor (i = 0; i < data->nr; i++) {\n+\t\tstruct diffstat_file *file = data->files[i];\n+\t\tunsigned long damage = file->added + file->deleted;\n+\t\tif (file->is_binary)\n+\t\t\t/*\n+\t\t\t * binary files counts bytes, not lines. Must find some\n+\t\t\t * way to normalize binary bytes vs. textual lines.\n+\t\t\t * The following heuristic assumes that there are 64\n+\t\t\t * bytes per \"line\".\n+\t\t\t * This is stupid and ugly, but very cheap...\n+\t\t\t */\n+\t\t\tdamage = (damage + 63) / 64;\n+\t\tALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);\n+\t\tdir.files[dir.nr].name = file->name;\n+\t\tdir.files[dir.nr].changed = damage;\n+\t\tchanged += damage;\n+\t\tdir.nr++;\n+\t}\n+\n+\t/* This can happen even with many files, if everything was renames */\n+\tif (!changed)\n+\t\treturn;\n+\n+\t/* Show all directories with more than x% of the changes */\n+\tqsort(dir.files, dir.nr, sizeof(dir.files[0]), dirstat_compare);\n+\tgather_dirstat(options, &dir, changed, \"\", 0);\n+}\n+\n static void free_diffstat_info(struct diffstat_t *diffstat)\n {\n \tint i;\n@@ -4058,6 +4108,7 @@ void diff_flush(struct diff_options *options)\n \tstruct diff_queue_struct *q = &diff_queued_diff;\n \tint i, output_format = options->output_format;\n \tint separator = 0;\n+\tint dirstat_by_line = 0;\n \n \t/*\n \t * Order: raw, stat, summary, patch\n@@ -4078,7 +4129,11 @@ void diff_flush(struct diff_options *options)\n \t\tseparator++;\n \t}\n \n-\tif (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {\n+\tif (output_format & DIFF_FORMAT_DIRSTAT && DIFF_OPT_TST(options, DIRSTAT_BY_LINE))\n+\t\tdirstat_by_line = 1;\n+\n+\tif (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT) ||\n+\t    dirstat_by_line) {\n \t\tstruct diffstat_t diffstat;\n \n \t\tmemset(&diffstat, 0, sizeof(struct diffstat_t));\n@@ -4093,10 +4148,12 @@ void diff_flush(struct diff_options *options)\n \t\t\tshow_stats(&diffstat, options);\n \t\tif (output_format & DIFF_FORMAT_SHORTSTAT)\n \t\t\tshow_shortstats(&diffstat, options);\n+\t\tif (output_format & DIFF_FORMAT_DIRSTAT)\n+\t\t\tshow_dirstat_by_line(&diffstat, options);\n \t\tfree_diffstat_info(&diffstat);\n \t\tseparator++;\n \t}\n-\tif (output_format & DIFF_FORMAT_DIRSTAT)\n+\tif ((output_format & DIFF_FORMAT_DIRSTAT) && !dirstat_by_line)\n \t\tshow_dirstat(options);\n \n \tif (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {\ndiff --git a/diff.h b/diff.h\nindex 08b4fe0..1a8b685 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -78,6 +78,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)\n #define DIFF_OPT_IGNORE_UNTRACKED_IN_SUBMODULES (1 << 25)\n #define DIFF_OPT_IGNORE_DIRTY_SUBMODULES (1 << 26)\n #define DIFF_OPT_OVERRIDE_SUBMODULE_CONFIG (1 << 27)\n+#define DIFF_OPT_DIRSTAT_BY_LINE     (1 << 28)\n \n #define DIFF_OPT_TST(opts, flag)    ((opts)->flags & DIFF_OPT_##flag)\n #define DIFF_OPT_SET(opts, flag)    ((opts)->flags |= DIFF_OPT_##flag)\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nindex 4b25e10..b8ad92a 100755\n--- a/t/t4047-diff-dirstat.sh\n+++ b/t/t4047-diff-dirstat.sh\n@@ -839,4 +839,104 @@ test_expect_success '--dirstat=files,cumulative,27.09' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+cat <<EOF >expect_diff_dirstat\n+  10.6% dst/copy/changed/\n+  10.6% dst/copy/rearranged/\n+  10.6% dst/copy/unchanged/\n+  10.6% dst/move/changed/\n+  10.6% dst/move/rearranged/\n+  10.6% dst/move/unchanged/\n+  10.6% src/move/changed/\n+  10.6% src/move/rearranged/\n+  10.6% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.2% changed/\n+  26.3% dst/copy/changed/\n+  26.3% dst/copy/rearranged/\n+  26.3% dst/copy/unchanged/\n+   5.2% dst/move/changed/\n+   5.2% dst/move/rearranged/\n+   5.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=lines' '\n+\tgit diff --dirstat=lines HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=lines -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=lines -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=lines' '\n+\tgit -c diff.dirstat=lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+cat <<EOF >expect_diff_dirstat\n+   2.1% changed/\n+  10.6% dst/copy/changed/\n+  10.6% dst/copy/rearranged/\n+  10.6% dst/copy/unchanged/\n+  10.6% dst/move/changed/\n+  10.6% dst/move/rearranged/\n+  10.6% dst/move/unchanged/\n+   2.1% rearranged/\n+  10.6% src/move/changed/\n+  10.6% src/move/rearranged/\n+  10.6% src/move/unchanged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_M\n+   5.2% changed/\n+  26.3% dst/copy/changed/\n+  26.3% dst/copy/rearranged/\n+  26.3% dst/copy/unchanged/\n+   5.2% dst/move/changed/\n+   5.2% dst/move/rearranged/\n+   5.2% rearranged/\n+EOF\n+\n+cat <<EOF >expect_diff_dirstat_CC\n+  16.6% changed/\n+  16.6% dst/copy/changed/\n+  16.6% dst/copy/rearranged/\n+  16.6% dst/move/changed/\n+  16.6% dst/move/rearranged/\n+  16.6% rearranged/\n+EOF\n+\n+test_expect_success '--dirstat=lines,0' '\n+\tgit diff --dirstat=lines,0 HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit diff --dirstat=lines,0 -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit diff --dirstat=lines,0 -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n+test_expect_success 'diff.dirstat=0,lines' '\n+\tgit -c diff.dirstat=0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgit -c diff.dirstat=0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgit -c diff.dirstat=0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166723","messageId":"1304069783-22739-8-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1304069783-22739-1-git-send-email-johan@herland.net","subject":"[PATCHv6 7/8] Improve error handling when parsing dirstat parameters","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-29T09:36:22Z","receivedAt":"2011-04-29T09:36:22Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"When encountering errors or unknown tokens while parsing parameters to the\n--dirstat option, it makes sense to die() with an error message informing\nthe user of which parameter did not make sense. However, when parsing the\ndiff.dirstat config variable, we cannot simply die(), but should instead\n(after warning the user) ignore the erroneous or unrecognized parameter.\nAfter all, future Git versions might add more dirstat parameters, and\nusing two different Git versions on the same repo should not cripple the\nolder Git version just because of a parameter that is only understood by\na more recent Git version.\n\nThis patch fixes the issue by refactoring the dirstat parameter parsing\nso that parse_dirstat_params() keeps on parsing parameters, even if an\nearlier parameter was not recognized. When parsing has finished, it returns\nzero if all parameters were successfully parsed, and non-zero if one or\nmore parameters were not recognized (with appropriate error messages\nappended to the 'errmsg' argument).\n\nThe parse_dirstat_params() callers then decide (based on the return value\nfrom parse_dirstat_params()) whether to warn and ignore (in case of\ndiff.dirstat), or to warn and die (in case of --dirstat).\n\nThe patch also adds a couple of tests verifying the correct behavior of\n--dirstat and diff.dirstat in the face of unknown (possibly future) dirstat\nparameters.\n\nSuggested-by: Junio C Hamano <gitster@pobox.com>\nImproved-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n diff.c                  |   71 +++++++++++++++++++++++++++--------------------\n t/t4047-diff-dirstat.sh |   37 ++++++++++++++++++++++++\n 2 files changed, 78 insertions(+), 30 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 8703763..b290543 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -67,52 +67,56 @@ static int parse_diff_color_slot(const char *var, int ofs)\n \treturn -1;\n }\n \n-static int parse_dirstat_params(struct diff_options *options, const char *params)\n+static int parse_dirstat_params(struct diff_options *options, const char *params,\n+\t\t\t\tstruct strbuf *errmsg)\n {\n \tconst char *p = params;\n+\tint p_len, ret = 0;\n+\n \twhile (*p) {\n-\t\tif (!prefixcmp(p, \"changes\")) {\n-\t\t\tp += 7;\n+\t\tp_len = strchrnul(p, ',') - p;\n+\t\tif (!memcmp(p, \"changes\", p_len)) {\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n-\t\t} else if (!prefixcmp(p, \"lines\")) {\n-\t\t\tp += 5;\n+\t\t} else if (!memcmp(p, \"lines\", p_len)) {\n \t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_FILE);\n-\t\t} else if (!prefixcmp(p, \"files\")) {\n-\t\t\tp += 5;\n+\t\t} else if (!memcmp(p, \"files\", p_len)) {\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_BY_LINE);\n \t\t\tDIFF_OPT_SET(options, DIRSTAT_BY_FILE);\n-\t\t} else if (!prefixcmp(p, \"noncumulative\")) {\n-\t\t\tp += 13;\n+\t\t} else if (!memcmp(p, \"noncumulative\", p_len)) {\n \t\t\tDIFF_OPT_CLR(options, DIRSTAT_CUMULATIVE);\n-\t\t} else if (!prefixcmp(p, \"cumulative\")) {\n-\t\t\tp += 10;\n+\t\t} else if (!memcmp(p, \"cumulative\", p_len)) {\n \t\t\tDIFF_OPT_SET(options, DIRSTAT_CUMULATIVE);\n \t\t} else if (isdigit(*p)) {\n \t\t\tchar *end;\n-\t\t\toptions->dirstat_permille = strtoul(p, &end, 10) * 10;\n-\t\t\tp = end;\n-\t\t\tif (*p == '.' && isdigit(*++p)) {\n+\t\t\tint permille = strtoul(p, &end, 10) * 10;\n+\t\t\tif (*end == '.' && isdigit(*++end)) {\n \t\t\t\t/* only use first digit */\n-\t\t\t\toptions->dirstat_permille += *p - '0';\n+\t\t\t\tpermille += *end - '0';\n \t\t\t\t/* .. and ignore any further digits */\n-\t\t\t\twhile (isdigit(*++p))\n+\t\t\t\twhile (isdigit(*++end))\n \t\t\t\t\t; /* nothing */\n \t\t\t}\n-\t\t} else\n-\t\t\treturn error(\"Unknown --dirstat parameter '%s'\", p);\n-\n-\t\tif (*p) {\n-\t\t\t/* more parameters, swallow separator */\n-\t\t\tif (*p != ',')\n-\t\t\t\treturn error(\"Missing comma separator at char \"\n-\t\t\t\t\t\"%\"PRIuMAX\" of '%s'\",\n-\t\t\t\t\t(uintmax_t) (p - params), params);\n-\t\t\tp++;\n+\t\t\tif (end - p == p_len)\n+\t\t\t\toptions->dirstat_permille = permille;\n+\t\t\telse {\n+\t\t\t\tstrbuf_addf(errmsg, \"  Failed to parse dirstat cut-off percentage '%.*s'\\n\",\n+\t\t\t\t\t    p_len, p);\n+\t\t\t\tret++;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tstrbuf_addf(errmsg, \"  Unknown dirstat parameter '%.*s'\\n\",\n+\t\t\t\t    p_len, p);\n+\t\t\tret++;\n \t\t}\n+\n+\t\tp += p_len;\n+\n+\t\tif (*p)\n+\t\t\tp++; /* more parameters, swallow separator */\n \t}\n-\treturn 0;\n+\treturn ret;\n }\n \n static int git_config_rename(const char *var, const char *value)\n@@ -195,8 +199,12 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)\n \t}\n \n \tif (!strcmp(var, \"diff.dirstat\")) {\n+\t\tstruct strbuf errmsg = STRBUF_INIT;\n \t\tdefault_diff_options.dirstat_permille = diff_dirstat_permille_default;\n-\t\t(void) parse_dirstat_params(&default_diff_options, value);\n+\t\tif (parse_dirstat_params(&default_diff_options, value, &errmsg))\n+\t\t\twarning(\"Found errors in 'diff.dirstat' config variable:\\n%s\",\n+\t\t\t\terrmsg.buf);\n+\t\tstrbuf_release(&errmsg);\n \t\tdiff_dirstat_permille_default = default_diff_options.dirstat_permille;\n \t\treturn 0;\n \t}\n@@ -3245,8 +3253,11 @@ static int stat_opt(struct diff_options *options, const char **av)\n \n static int parse_dirstat_opt(struct diff_options *options, const char *params)\n {\n-\tif (parse_dirstat_params(options, params))\n-\t\tdie(\"Failed to parse --dirstat/-X option parameter\");\n+\tstruct strbuf errmsg = STRBUF_INIT;\n+\tif (parse_dirstat_params(options, params, &errmsg))\n+\t\tdie(\"Failed to parse --dirstat/-X option parameter:\\n%s\",\n+\t\t    errmsg.buf);\n+\tstrbuf_release(&errmsg);\n \t/*\n \t * The caller knows a dirstat-related option is given from the command\n \t * line; allow it to say \"return this_function();\"\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nindex b8ad92a..cc947fd 100755\n--- a/t/t4047-diff-dirstat.sh\n+++ b/t/t4047-diff-dirstat.sh\n@@ -939,4 +939,41 @@ test_expect_success 'diff.dirstat=0,lines' '\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC\n '\n \n+test_expect_success '--dirstat=future_param,lines,0 should fail loudly' '\n+\ttest_must_fail git diff --dirstat=future_param,lines,0 HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&\n+\ttest_debug \"cat actual_error\" &&\n+\ttest_cmp /dev/null actual_diff_dirstat &&\n+\tgrep -q \"future_param\" actual_error &&\n+\tgrep -q \"\\--dirstat\" actual_error\n+'\n+\n+test_expect_success '--dirstat=dummy1,cumulative,2dummy should report both unrecognized parameters' '\n+\ttest_must_fail git diff --dirstat=dummy1,cumulative,2dummy HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&\n+\ttest_debug \"cat actual_error\" &&\n+\ttest_cmp /dev/null actual_diff_dirstat &&\n+\tgrep -q \"dummy1\" actual_error &&\n+\tgrep -q \"2dummy\" actual_error &&\n+\tgrep -q \"\\--dirstat\" actual_error\n+'\n+\n+test_expect_success 'diff.dirstat=future_param,0,lines should warn, but still work' '\n+\tgit -c diff.dirstat=future_param,0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&\n+\ttest_debug \"cat actual_error\" &&\n+\ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n+\tgrep -q \"future_param\" actual_error &&\n+\tgrep -q \"diff\\\\.dirstat\" actual_error &&\n+\n+\tgit -c diff.dirstat=future_param,0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M 2>actual_error &&\n+\ttest_debug \"cat actual_error\" &&\n+\ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n+\tgrep -q \"future_param\" actual_error &&\n+\tgrep -q \"diff\\\\.dirstat\" actual_error &&\n+\n+\tgit -c diff.dirstat=future_param,0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC 2>actual_error &&\n+\ttest_debug \"cat actual_error\" &&\n+\ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC &&\n+\tgrep -q \"future_param\" actual_error &&\n+\tgrep -q \"diff\\\\.dirstat\" actual_error\n+'\n+\n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"},{"id":"166724","messageId":"1304069783-22739-9-git-send-email-johan@herland.net","threadId":"27015","inReplyTo":"1304069783-22739-1-git-send-email-johan@herland.net","subject":"[PATCHv6 8/8] Mark dirstat error messages for translation","fromName":"Johan Herland","fromEmail":"johan@herland.net","sentAt":"2011-04-29T09:36:23Z","receivedAt":"2011-04-29T09:36:23Z","isPatch":false,"sender":{"key":"johan@herland.net","avatar":"https://avatars.githubusercontent.com/u/547031?v=4"},"body":"Suggested-by: Junio C Hamano <gitster@pobox.com>\nSigned-off-by: Johan Herland <johan@herland.net>\n---\n\nThis patch WILL NOT COMPILE until the topic branch is merged with an\ni18n/gettext-aware branch (i.e. where _() and test_i18ngrep is present).\n\n diff.c                  |    8 ++++----\n t/t4047-diff-dirstat.sh |   22 +++++++++++-----------\n 2 files changed, 15 insertions(+), 15 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex b290543..0f65413 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -101,12 +101,12 @@ static int parse_dirstat_params(struct diff_options *options, const char *params\n \t\t\tif (end - p == p_len)\n \t\t\t\toptions->dirstat_permille = permille;\n \t\t\telse {\n-\t\t\t\tstrbuf_addf(errmsg, \"  Failed to parse dirstat cut-off percentage '%.*s'\\n\",\n+\t\t\t\tstrbuf_addf(errmsg, _(\"  Failed to parse dirstat cut-off percentage '%.*s'\\n\"),\n \t\t\t\t\t    p_len, p);\n \t\t\t\tret++;\n \t\t\t}\n \t\t} else {\n-\t\t\tstrbuf_addf(errmsg, \"  Unknown dirstat parameter '%.*s'\\n\",\n+\t\t\tstrbuf_addf(errmsg, _(\"  Unknown dirstat parameter '%.*s'\\n\"),\n \t\t\t\t    p_len, p);\n \t\t\tret++;\n \t\t}\n@@ -202,7 +202,7 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)\n \t\tstruct strbuf errmsg = STRBUF_INIT;\n \t\tdefault_diff_options.dirstat_permille = diff_dirstat_permille_default;\n \t\tif (parse_dirstat_params(&default_diff_options, value, &errmsg))\n-\t\t\twarning(\"Found errors in 'diff.dirstat' config variable:\\n%s\",\n+\t\t\twarning(_(\"Found errors in 'diff.dirstat' config variable:\\n%s\"),\n \t\t\t\terrmsg.buf);\n \t\tstrbuf_release(&errmsg);\n \t\tdiff_dirstat_permille_default = default_diff_options.dirstat_permille;\n@@ -3255,7 +3255,7 @@ static int parse_dirstat_opt(struct diff_options *options, const char *params)\n {\n \tstruct strbuf errmsg = STRBUF_INIT;\n \tif (parse_dirstat_params(options, params, &errmsg))\n-\t\tdie(\"Failed to parse --dirstat/-X option parameter:\\n%s\",\n+\t\tdie(_(\"Failed to parse --dirstat/-X option parameter:\\n%s\"),\n \t\t    errmsg.buf);\n \tstrbuf_release(&errmsg);\n \t/*\ndiff --git a/t/t4047-diff-dirstat.sh b/t/t4047-diff-dirstat.sh\nindex cc947fd..29e80a5 100755\n--- a/t/t4047-diff-dirstat.sh\n+++ b/t/t4047-diff-dirstat.sh\n@@ -943,37 +943,37 @@ test_expect_success '--dirstat=future_param,lines,0 should fail loudly' '\n \ttest_must_fail git diff --dirstat=future_param,lines,0 HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&\n \ttest_debug \"cat actual_error\" &&\n \ttest_cmp /dev/null actual_diff_dirstat &&\n-\tgrep -q \"future_param\" actual_error &&\n-\tgrep -q \"\\--dirstat\" actual_error\n+\ttest_i18ngrep -q \"future_param\" actual_error &&\n+\ttest_i18ngrep -q \"\\--dirstat\" actual_error\n '\n \n test_expect_success '--dirstat=dummy1,cumulative,2dummy should report both unrecognized parameters' '\n \ttest_must_fail git diff --dirstat=dummy1,cumulative,2dummy HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&\n \ttest_debug \"cat actual_error\" &&\n \ttest_cmp /dev/null actual_diff_dirstat &&\n-\tgrep -q \"dummy1\" actual_error &&\n-\tgrep -q \"2dummy\" actual_error &&\n-\tgrep -q \"\\--dirstat\" actual_error\n+\ttest_i18ngrep -q \"dummy1\" actual_error &&\n+\ttest_i18ngrep -q \"2dummy\" actual_error &&\n+\ttest_i18ngrep -q \"\\--dirstat\" actual_error\n '\n \n test_expect_success 'diff.dirstat=future_param,0,lines should warn, but still work' '\n \tgit -c diff.dirstat=future_param,0,lines diff --dirstat HEAD^..HEAD >actual_diff_dirstat 2>actual_error &&\n \ttest_debug \"cat actual_error\" &&\n \ttest_cmp expect_diff_dirstat actual_diff_dirstat &&\n-\tgrep -q \"future_param\" actual_error &&\n-\tgrep -q \"diff\\\\.dirstat\" actual_error &&\n+\ttest_i18ngrep -q \"future_param\" actual_error &&\n+\ttest_i18ngrep -q \"diff\\\\.dirstat\" actual_error &&\n \n \tgit -c diff.dirstat=future_param,0,lines diff --dirstat -M HEAD^..HEAD >actual_diff_dirstat_M 2>actual_error &&\n \ttest_debug \"cat actual_error\" &&\n \ttest_cmp expect_diff_dirstat_M actual_diff_dirstat_M &&\n-\tgrep -q \"future_param\" actual_error &&\n-\tgrep -q \"diff\\\\.dirstat\" actual_error &&\n+\ttest_i18ngrep -q \"future_param\" actual_error &&\n+\ttest_i18ngrep -q \"diff\\\\.dirstat\" actual_error &&\n \n \tgit -c diff.dirstat=future_param,0,lines diff --dirstat -C -C HEAD^..HEAD >actual_diff_dirstat_CC 2>actual_error &&\n \ttest_debug \"cat actual_error\" &&\n \ttest_cmp expect_diff_dirstat_CC actual_diff_dirstat_CC &&\n-\tgrep -q \"future_param\" actual_error &&\n-\tgrep -q \"diff\\\\.dirstat\" actual_error\n+\ttest_i18ngrep -q \"future_param\" actual_error &&\n+\ttest_i18ngrep -q \"diff\\\\.dirstat\" actual_error\n '\n \n test_done\n-- \n1.7.5.rc1.3.g4d7b\n"}]}