{"thread":{"id":"12045","subject":"Add \"--dirstat\" for some directory statistics","startedAt":"2008-02-11T20:59:18Z","lastAt":"2008-02-11T22:25:26Z","messageCount":6,"participants":["Linus Torvalds","Johannes Schindelin"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"68440","messageId":"alpine.LFD.1.00.0802111230200.2920@woody.linux-foundation.org","threadId":"12045","inReplyTo":null,"subject":"Add \"--dirstat\" for some directory statistics","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-02-11T20:59:18Z","receivedAt":"2008-02-11T20:59:18Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThis adds a new form of diffstat output, something that I have \noccasionally ended up doing manually (and badly, because it's actually \npretty nasty to do), and that I think is very useful for an \"overview\" \nkind of thing in a project like the kernel that has a fairly deep and \nwell-separated directory structure with semantic meaning.\n\nWhat I mean by that is that it's often interesting from a patch standpoint \nto see which sub-directories are impacted by a patch, and to what degree.\n\nWhat makes that more interesting is that the \"impact\" is often \nhierarchical: in the kernel, for example, something could either have a \nvery localized impact to \"fs/ext3/\" and then it's interesting to see that \nsuch a patch changes mostly that subdirectory, but you could have another \npatch that changes some generic VFS-layer issue which affects _many_ \nsubdirectories that are all under \"fs/\", but none - or perhaps just a \ncouple of them - of the individual filesystems are interesting in \nthemselves.\n\nSo what commonly happens is that you may have big changes in a specific \nsub-subdirectory, but still also significant separate changes to the \nsubdirectory leading up to that - maybe you have significant VFS-level \nchanges, but *also* changes under that VFS layer in the NFS-specific \ndirectories, for example. In that case, you do want the low-level parts \nthat are significant to show up, but then the insignificant ones should \nshow up as under the more generic top-level directory.\n\nThis patch shows all of that with \"--dirstat\". The output can be either \nsomething simple like\n\n\tcommit 81772fe...\n\tAuthor: Thomas Gleixner <tglx@linutronix.de>\n\tDate:   Sun Feb 10 23:57:36 2008 +0100\n\n\t    x86: remove over noisy debug printk\n\t\n\t    pageattr-test.c contains a noisy debug printk that people reported.\n\t    The condition under which it prints (randomly tapping into a mem_map[]\n\t    hole and not being able to c_p_a() there) is valid behavior and not\n\t    interesting to report.\n\t\n\t    Remove it.\n\t\n\t    Signed-off-by: Thomas Gleixner <tglx@linutronix.de>\n\t    Acked-by: Ingo Molnar <mingo@elte.hu>\n\t    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>\n\t\n\t 100% arch/x86/mm/\n\t 1 files changed, 0 insertions(+), 7 deletions(-)\n\nor something much more complex like\n\n\tcommit e231c2e...\n\tAuthor: David Howells <dhowells@redhat.com>\n\tDate:   Thu Feb 7 00:15:26 2008 -0800\n\t\n\t    Convert ERR_PTR(PTR_ERR(p)) instances to ERR_CAST(p)\n\n\t  20% crypto/\n\t   7% fs/afs/\n\t   7% fs/fuse/\n\t   7% fs/gfs2/\n\t   5% fs/jffs2/\n\t   5% fs/nfs/\n\t   5% fs/nfsd/\n\t   7% fs/reiserfs/\n\t  15% fs/\n\t   7% net/rxrpc/\n\t  10% security/keys/\n\t 28 files changed, 39 insertions(+), 39 deletions(-)\n\nwhere that latter example is an example of significant work in some \nindividual fs/*/ subdirectories (like the patches to reiserfs accounting \nfor 7% of the whole), but then discounting those individual filesystems, \nthere's also 15% other things left over under fs/ in general.\n\nNote that the \"15% fs/\" mentioned above is the stuff that is under fs/ but \nthat was _not_ significant enough to report on its own. So the above does \n_not_ mean that 15% of the work was under fs/ per se, because that 15% \ndoes *not* include the already-reported 7% of afs, 7% of fuse etc.\n\nThe reporting limit has been arbitrarily set at 3%, which seems to be a \npretty good cut-off, but I made it an option variable in case anybody \nwould ever want to make it a dynamic cutoff.\n\nNOTE! The percentages are purely about the total lines added and removed, \nnot anything smarter (or dumber) than that. Also note that you should not \ngenerally expect things to add up to 100%: not only does it round down, we \ndon't report leftover scraps (they add up to the top-level change count, \nbut we don't even bother reporting that, it only reports subdirectories).\n\nQuite frankly, as a top-level manager this is really convenient for me, \nbut it's going to be very boring for git itself since there are few \nsubdirectories. Also, don't expect things to make tons of sense if you \ncombine this with \"-M\" and there are cross-directory renames etc.\n\nBut even for git itself, you can get some fun statistics. Try out\n\n\tgit log --dirstat\n\nand see the occasional mentions of things like Documentation/, git-gui/, \ngitweb/ and gitk-git/. Or try out something like\n\n\tgit diff --dirstat v1.5.0..v1.5.4\n\nwhich does kind of git an overview that shows *something*. But in general, \nthe output is more exciting for big projects with deeper structure, and \ndoing a\n\n\tgit diff --dirstat v2.6.24..v2.6.25-rc1\n\non the kernel is what I actually wrote this for!\n\nSigned-off-by: Linus Torvalds <torvalds@linux-foundation.org>\n---\nYeah, I'm proud of how small I could make this really cool feature. I had \nput off doing this because I always expected it to be harder than it was. \nBut it was easy because I think I came up with a clever way to do it.\n\n diff.c |   74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n diff.h |    2 +\n 2 files changed, 75 insertions(+), 1 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 5b8afdc..da91bdd 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -980,6 +980,71 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)\n \t}\n }\n \n+struct diffstat_dir {\n+\tstruct diffstat_file **files;\n+\tint nr, percent;\n+};\n+\n+static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen)\n+{\n+\tunsigned long this_dir = 0;\n+\n+\twhile (dir->nr) {\n+\t\tstruct diffstat_file *f = *dir->files;\n+\t\tint namelen = strlen(f->name);\n+\t\tunsigned long this;\n+\t\tchar *slash;\n+\n+\t\tif (namelen < baselen)\n+\t\t\tbreak;\n+\t\tif (memcmp(f->name, base, baselen))\n+\t\t\tbreak;\n+\t\tslash = strchr(f->name + baselen, '/');\n+\t\tif (slash) {\n+\t\t\tint newbaselen = slash + 1 - f->name;\n+\t\t\tthis = gather_dirstat(dir, changed, f->name, newbaselen);\n+\t\t} else {\n+\t\t\tthis = f->added + f->deleted;\n+\t\t\tdir->files++;\n+\t\t\tdir->nr--;\n+\t\t}\n+\t\tthis_dir += this;\n+\t}\n+\n+\tif (baselen) {\n+\t\tint percent = this_dir * 100 / changed;\n+\t\tif (percent >= dir->percent) {\n+\t\t\tprintf(\"%4d%% %.*s\\n\", percent, baselen, base);\n+\t\t\treturn 0;\n+\t\t}\n+\t}\n+\treturn this_dir;\n+}\n+\n+static void show_dirstat(struct diffstat_t *data, struct diff_options *options)\n+{\n+\tint i;\n+\tunsigned long changed;\n+\tstruct diffstat_dir dir;\n+\n+\t/* Calculate total changes */\n+\tchanged = 0;\n+\tfor (i = 0; i < data->nr; i++) {\n+\t\tchanged += data->files[i]->added;\n+\t\tchanged += data->files[i]->deleted;\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+\tdir.files = data->files;\n+\tdir.nr = data->nr;\n+\tdir.percent = options->dirstat_percent;\n+\tgather_dirstat(&dir, changed, \"\", 0);\n+}\n+\n static void free_diffstat_info(struct diffstat_t *diffstat)\n {\n \tint i;\n@@ -2043,6 +2108,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->context = 3;\n \toptions->msg_sep = \"\";\n \n@@ -2084,6 +2150,7 @@ int diff_setup_done(struct diff_options *options)\n \t\t\t\t\t    DIFF_FORMAT_NUMSTAT |\n \t\t\t\t\t    DIFF_FORMAT_DIFFSTAT |\n \t\t\t\t\t    DIFF_FORMAT_SHORTSTAT |\n+\t\t\t\t\t    DIFF_FORMAT_DIRSTAT |\n \t\t\t\t\t    DIFF_FORMAT_SUMMARY |\n \t\t\t\t\t    DIFF_FORMAT_PATCH);\n \n@@ -2095,6 +2162,7 @@ int diff_setup_done(struct diff_options *options)\n \t\t\t\t      DIFF_FORMAT_NUMSTAT |\n \t\t\t\t      DIFF_FORMAT_DIFFSTAT |\n \t\t\t\t      DIFF_FORMAT_SHORTSTAT |\n+\t\t\t\t      DIFF_FORMAT_DIRSTAT |\n \t\t\t\t      DIFF_FORMAT_SUMMARY |\n \t\t\t\t      DIFF_FORMAT_CHECKDIFF))\n \t\tDIFF_OPT_SET(options, RECURSIVE);\n@@ -2205,6 +2273,8 @@ 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 (!strcmp(arg, \"--dirstat\"))\n+\t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n \telse if (!strcmp(arg, \"--check\"))\n \t\toptions->output_format |= DIFF_FORMAT_CHECKDIFF;\n \telse if (!strcmp(arg, \"--summary\"))\n@@ -2923,7 +2993,7 @@ 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_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIRSTAT)) {\n \t\tstruct diffstat_t diffstat;\n \n \t\tmemset(&diffstat, 0, sizeof(struct diffstat_t));\n@@ -2933,6 +3003,8 @@ void diff_flush(struct diff_options *options)\n \t\t\tif (check_pair_status(p))\n \t\t\t\tdiff_flush_stat(p, options, &diffstat);\n \t\t}\n+\t\tif (output_format & DIFF_FORMAT_DIRSTAT)\n+\t\t\tshow_dirstat(&diffstat, options);\n \t\tif (output_format & DIFF_FORMAT_NUMSTAT)\n \t\t\tshow_numstat(&diffstat, options);\n \t\tif (output_format & DIFF_FORMAT_DIFFSTAT)\ndiff --git a/diff.h b/diff.h\nindex 073d5cb..3181263 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -30,6 +30,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,\n #define DIFF_FORMAT_SUMMARY\t0x0008\n #define DIFF_FORMAT_PATCH\t0x0010\n #define DIFF_FORMAT_SHORTSTAT\t0x0020\n+#define DIFF_FORMAT_DIRSTAT\t0x0020\n \n /* These override all above */\n #define DIFF_FORMAT_NAME\t0x0100\n@@ -80,6 +81,7 @@ struct diff_options {\n \tint pickaxe_opts;\n \tint rename_score;\n \tint rename_limit;\n+\tint dirstat_percent;\n \tint setup;\n \tint abbrev;\n \tconst char *msg_sep;\n"},{"id":"68444","messageId":"alpine.LFD.1.00.0802111302470.2920@woody.linux-foundation.org","threadId":"12045","inReplyTo":"alpine.LFD.1.00.0802111230200.2920@woody.linux-foundation.org","subject":"Re: Add \"--dirstat\" for some directory statistics","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-02-11T21:17:43Z","receivedAt":"2008-02-11T21:17:43Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nOn Mon, 11 Feb 2008, Linus Torvalds wrote:\n> \n> The reporting limit has been arbitrarily set at 3%, which seems to be a \n> pretty good cut-off, but I made it an option variable in case anybody \n> would ever want to make it a dynamic cutoff.\n\nSide note: that 3% isn't actually _too_ arbitrary.  I did actually test\nthings a bit. \n\nSo it seems to be a good limit that ensures that you'll basically never\nsee more than one page worth of reporting (if you did, it wouldn't be a\nvery good summary).  So in theory, if something is _just_ right and\nreally balanced out across a wide variety of directories, you can get 33\nlines of output, but in practice it's unusual to get more than 10-15\nentries. \n\nIn the kernel, the most spread out commit I've found so far is 18 entries:\n\n    CONFIG_HIGHPTE vs. sub-page page tables.\n\n   3% arch/powerpc/mm/\n   5% arch/sparc/mm/\n   6% arch/\n   5% include/asm-alpha/\n   3% include/asm-avr32/\n   3% include/asm-cris/\n   4% include/asm-ia64/\n   6% include/asm-m68k/\n   3% include/asm-mips/\n   7% include/asm-powerpc/\n   6% include/asm-sh/\n   4% include/asm-sparc64/\n   3% include/asm-um/\n   6% include/asm-x86/\n   3% include/asm-xtensa/\n   3% include/linux/\n  14% include/\n   7% mm/\n 53 files changed, 326 insertions(+), 132 deletions(-)\n\nbut it's not like I looked through all of them (and I'm sure you can get\nmore if you select just the right commit range to generate the diff).\n\nThe point being that a 4-percent cut-off would likely be so big that it\nwould have hidden the details in the above example.\n\nA 2% cut-off is worth trying, though.  It does get a bit more detail,\nand the likelihood of it actually causing a 50-line report is probably\nvanishingly small, so it might be worth playing around with.  The above\n18-entry example grows to 23 entries with a 2% limit, which is very\nborderline on a traditional 80x24 terminal, but still just barely \"one\npage\" which was my personal rule to aim for. \n\n\t\t\tLinus\n"},{"id":"68445","messageId":"alpine.LSU.1.00.0802112122300.3870@racer.site","threadId":"12045","inReplyTo":"alpine.LFD.1.00.0802111230200.2920@woody.linux-foundation.org","subject":"Re: Add \"--dirstat\" for some directory statistics","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2008-02-11T21:24:16Z","receivedAt":"2008-02-11T21:24:16Z","isPatch":false,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Mon, 11 Feb 2008, Linus Torvalds wrote:\n\n>  #define DIFF_FORMAT_SHORTSTAT\t0x0020\n> +#define DIFF_FORMAT_DIRSTAT\t0x0020\n\nCertainly you meant 0x0040 for DIRSTAT, n'est-ce pas?\n\nAlso, are you sure that you do not want to make the minimal percentage \nconfigurable, maybe with --dirstat[=<min-percent>]?  I mean, sometimes \neven 0.01% is worth showing.\n\nCiao,\nDscho\n"},{"id":"68450","messageId":"alpine.LFD.1.00.0802111338270.2920@woody.linux-foundation.org","threadId":"12045","inReplyTo":"alpine.LSU.1.00.0802112122300.3870@racer.site","subject":"Re: Add \"--dirstat\" for some directory statistics","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-02-11T21:41:14Z","receivedAt":"2008-02-11T21:41:14Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 11 Feb 2008, Johannes Schindelin wrote:\n> \n> Certainly you meant 0x0040 for DIRSTAT, n'est-ce pas?\n\nYeah, yeah.\n\n> Also, are you sure that you do not want to make the minimal percentage \n> configurable, maybe with --dirstat[=<min-percent>]?  I mean, sometimes \n> even 0.01% is worth showing.\n\nWell, maybe, maybe not. But yeah, at least in whole percentages. Here is a \nsimple patch to fix that cut-and-past'o and add a configurable (but still \nwhole-percentage-point) thing.\n\n\t\tLinus\n\n---\n diff.c |    2 +-\n diff.h |    2 +-\n 2 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex da91bdd..81948e7 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -2273,7 +2273,7 @@ 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 (!strcmp(arg, \"--dirstat\"))\n+\telse if (opt_arg(arg, 'X', \"dirstat\", &options->dirstat_percent))\n \t\toptions->output_format |= DIFF_FORMAT_DIRSTAT;\n \telse if (!strcmp(arg, \"--check\"))\n \t\toptions->output_format |= DIFF_FORMAT_CHECKDIFF;\ndiff --git a/diff.h b/diff.h\nindex 3181263..9da6601 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -30,7 +30,7 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,\n #define DIFF_FORMAT_SUMMARY\t0x0008\n #define DIFF_FORMAT_PATCH\t0x0010\n #define DIFF_FORMAT_SHORTSTAT\t0x0020\n-#define DIFF_FORMAT_DIRSTAT\t0x0020\n+#define DIFF_FORMAT_DIRSTAT\t0x0040\n \n /* These override all above */\n #define DIFF_FORMAT_NAME\t0x0100\n"},{"id":"68453","messageId":"alpine.LFD.1.00.0802111349090.2920@woody.linux-foundation.org","threadId":"12045","inReplyTo":"alpine.LSU.1.00.0802112122300.3870@racer.site","subject":"Re: Add \"--dirstat\" for some directory statistics","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-02-11T22:06:10Z","receivedAt":"2008-02-11T22:06:10Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 11 Feb 2008, Johannes Schindelin wrote:\n> \n> I mean, sometimes even 0.01% is worth showing.\n\nI tried this, and no. It's not. Not even 0.1%. If you care at that level,  \nyou're better off just looking at the diffstat itself. The noise factor \nwith the summary makes it useless with even 0.1%.\n\nThat said, the *reporting* sometimes looks better with tenths of percents. \nThis is an example of \"report in tenths of percent, limit to 1%\":\n\n   4.8% arch/um/include/\n   6.1% arch/um/os-Linux/drivers/\n   5.7% arch/um/os-Linux/include/\n   2.6% arch/um/os-Linux/skas/\n   1.7% arch/um/os-Linux/sys-i386/\n   9.2% arch/um/os-Linux/sys-x86_64/\n  69.6% arch/um/os-Linux/\n\nand maybe that format would be nicer (and more \"future proof\" in case \npeople want to make the limiting go down to fractional percentages too).\n\nIf people want to play around with it, here's a trial patch (and yes, if \nyou want it down to 0.1%, you can use \"--dirstat=0\", and it will only \nremove the ones that really round down to 0.0%, but it really isn't any \ngood for summaries any more).\n\nFor summaries, I'd suggest testing with something like\n\n\tgit diff --dirstat=? v2.6.23..v2.6.24\n\nand comparing the 0% cutoff (pretty damn useless) to 1 (already *much* \nbetter and perhaps appropriate as a \"deep summary\") to 2 (probably the \nbest if you actually want a short summary) to 3 (fairly similar, but \ndropped a few \"2.x%\" cases that were relevant).\n\nThe 1% cutoff case is already big enough that I don't think it's good as a \nsummary, and while it contains \"more information\", it's actually likely \nmuch more informative in that case to do the default 3% case, and then \nnoticing that \"drivers\" and \"include\" are pretty high, and at that point \nit makes sense to say \"ok, let's re-do it but limiting it to that \nsubdirectory\", and do a\n\n\tgit diff --dirstat=3 v2.6.23..v2.6.24 drivers/\n\nwhere now you'll actually see *more* information than for the 1% case, but \nonly as it pertains for that drivers subdirectory.\n\nSo I seriously doubt that you often want to have small percentages, and \nthat it's much more productive to give people the overview and then they \ncan \"drill down\" into that overview.\n\nAnyway, here's the patch to try out..\n\n\t\tLinus\n\n---\n diff.c |   11 ++++++++-----\n 1 files changed, 7 insertions(+), 4 deletions(-)\n\ndiff --git a/diff.c b/diff.c\n--- a/diff.c\n+++ b/diff.c\n@@ -1012,10 +1012,13 @@ static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, cons\n \t}\n \n \tif (baselen) {\n-\t\tint percent = this_dir * 100 / changed;\n-\t\tif (percent >= dir->percent) {\n-\t\t\tprintf(\"%4d%% %.*s\\n\", percent, baselen, base);\n-\t\t\treturn 0;\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\tprintf(\"%4d.%01d%% %.*s\\n\", percent, permille % 10, baselen, base);\n+\t\t\t\treturn 0;\n+\t\t\t}\n \t\t}\n \t}\n \treturn this_dir;\n"},{"id":"68457","messageId":"alpine.LFD.1.00.0802111414590.2920@woody.linux-foundation.org","threadId":"12045","inReplyTo":"alpine.LFD.1.00.0802111349090.2920@woody.linux-foundation.org","subject":"Re: Add \"--dirstat\" for some directory statistics","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-02-11T22:25:26Z","receivedAt":"2008-02-11T22:25:26Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 11 Feb 2008, Linus Torvalds wrote:\n> \n> Anyway, here's the patch to try out..\n\nOk, one more (final, I promise!) patch..\n\nThere's another issue: I think the non-cumulative percentages are more \ninteresting, but perhaps somebody disagrees. So here's a patch that allows \nyou to say\n\n\tgit diff --dirstat --cumulative v2.6.24..v2.6.25-rc1\n\nand now the percentages will accumulate into the parent directory even \nwhen they are printed out.\n\nExample difference:\n\n - without --cumulative:\n\n\t   3.2% arch/arm/\n\t   6.9% arch/powerpc/\n\t   3.2% arch/sh/\n\t   6.9% arch/x86/\n\t  11.0% arch/\n\t   3.7% drivers/media/\n\t   7.3% drivers/net/wireless/\n\t   6.9% drivers/net/\n\t  19.1% drivers/\n\t   3.5% fs/\n\t  10.3% include/\n\t   6.0% net/\n\t   3.7% sound/\n\n - with --cumulative:\n\n\t   3.2% arch/arm/\n\t   6.9% arch/powerpc/\n\t   3.2% arch/sh/\n\t   6.9% arch/x86/\n\t  31.3% arch/\n\t   3.7% drivers/media/\n\t   7.3% drivers/net/wireless/\n\t  14.3% drivers/net/\n\t  37.2% drivers/\n\t   3.5% fs/\n\t  10.3% include/\n\t   6.0% net/\n\t   3.7% sound/\n\nand note how the latter may be more \"logical\" (ie it now says \"31.3% arch\" \nwhich simply means that about a third of the changes were somewhere under \nthe arch/ subdirectory), it's obvious why I don't like it in general (even \nif it's wonderful in this case) when you start looking at other examples:\n\n - without --cumulative:\n\n\t 100.0% arch/x86/mm/\n\n - with --cumulative:\n\n\t 100.0% arch/x86/mm/\n\t 100.0% arch/x86/\n\t 100.0% arch/\n\nwhich is obviously just noise and confusion, and not helpful at all.\n\nBut here's a trial patch against my original patch (ie this contains it \nall - the trivial fixes, tenths of percent reporting, and now the \ncumulative flag too). I was too lazy to try to split it up, and I hadn't \ncommitted the previous ones in between (bad Linus!).\n\n\t\tLinus\n\n---\n diff.c |   19 +++++++++++++------\n diff.h |    3 ++-\n 2 files changed, 15 insertions(+), 7 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex da91bdd..e5b8baa 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -982,7 +982,7 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)\n \n struct diffstat_dir {\n \tstruct diffstat_file **files;\n-\tint nr, percent;\n+\tint nr, percent, cumulative;\n };\n \n static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen)\n@@ -1012,10 +1012,14 @@ static long gather_dirstat(struct diffstat_dir *dir, unsigned long changed, cons\n \t}\n \n \tif (baselen) {\n-\t\tint percent = this_dir * 100 / changed;\n-\t\tif (percent >= dir->percent) {\n-\t\t\tprintf(\"%4d%% %.*s\\n\", percent, baselen, base);\n-\t\t\treturn 0;\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\tprintf(\"%4d.%01d%% %.*s\\n\", percent, 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 \treturn this_dir;\n@@ -1042,6 +1046,7 @@ static void show_dirstat(struct diffstat_t *data, struct diff_options *options)\n \tdir.files = data->files;\n \tdir.nr = data->nr;\n \tdir.percent = options->dirstat_percent;\n+\tdir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;\n \tgather_dirstat(&dir, changed, \"\", 0);\n }\n \n@@ -2273,8 +2278,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 (!strcmp(arg, \"--dirstat\"))\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_CUMULATIVE;\n \telse if (!strcmp(arg, \"--check\"))\n \t\toptions->output_format |= DIFF_FORMAT_CHECKDIFF;\n \telse if (!strcmp(arg, \"--summary\"))\ndiff --git a/diff.h b/diff.h\nindex 3181263..8c6bb54 100644\n--- a/diff.h\n+++ b/diff.h\n@@ -30,7 +30,8 @@ typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,\n #define DIFF_FORMAT_SUMMARY\t0x0008\n #define DIFF_FORMAT_PATCH\t0x0010\n #define DIFF_FORMAT_SHORTSTAT\t0x0020\n-#define DIFF_FORMAT_DIRSTAT\t0x0020\n+#define DIFF_FORMAT_DIRSTAT\t0x0040\n+#define DIFF_FORMAT_CUMULATIVE\t0x0080\n \n /* These override all above */\n #define DIFF_FORMAT_NAME\t0x0100\n"}]}