{"thread":{"id":"15265","subject":"Fix '--dirstat' with cross-directory renaming","startedAt":"2008-08-28T23:19:08Z","lastAt":"2008-08-28T23:19:08Z","messageCount":1,"participants":["Linus Torvalds"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"89037","messageId":"alpine.LFD.1.10.0808281608580.3300@nehalem.linux-foundation.org","threadId":"15265","inReplyTo":null,"subject":"Fix '--dirstat' with cross-directory renaming","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-08-28T23:19:08Z","receivedAt":"2008-08-28T23:19:08Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThe dirstat code depends on the fact that we always generate diffs with \nthe names sorted, since it then just does a single-pass walk-over of the \nsorted list of names and how many changes there were. The sorting means \nthat all files are nicely grouped by directory.\n\nThat all works fine.\n\nExcept when we have rename detection, and suddenly the nicely sorted list \nof pathnames isn't all that sorted at all. And now the single-pass dirstat \nwalk gets all confused, and you can get results like this:\n\n  [torvalds@nehalem linux]$ git diff --dirstat=2 -M v2.6.27-rc4..v2.6.27-rc5\n     3.0% arch/powerpc/configs/\n     6.8% arch/arm/configs/\n     2.7% arch/powerpc/configs/\n     4.2% arch/arm/configs/\n     5.6% arch/powerpc/configs/\n     8.4% arch/arm/configs/\n     5.5% arch/powerpc/configs/\n    23.3% arch/arm/configs/\n     8.6% arch/powerpc/configs/\n     4.0% arch/\n     4.4% drivers/usb/musb/\n     4.0% drivers/watchdog/\n     7.6% drivers/\n     3.5% fs/\n\nThe trivial fix is to add a sorting pass, fixing it to:\n\n  [torvalds@nehalem linux]$ git diff --dirstat=2 -M v2.6.27-rc4..v2.6.27-rc5\n    43.0% arch/arm/configs/\n    25.5% arch/powerpc/configs/\n     5.3% arch/\n     4.4% drivers/usb/musb/\n     4.0% drivers/watchdog/\n     7.6% drivers/\n     3.5% fs/\n\nSpot the difference. In case anybody wonders: it's because of a ton of \nrenames from {include/asm-blackfin => arch/blackfin/include/asm} that just \ntotally messed up the file ordering in between arch/arm and arch/powerpc.\n\nSigned-off-by: Linus Torvalds <torvalds@linux-foundation.org>\n---\n diff.c |    8 ++++++++\n 1 files changed, 8 insertions(+), 0 deletions(-)\n\ndiff --git a/diff.c b/diff.c\nindex 18fa7a7..135dec4 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -1060,6 +1060,13 @@ static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long ch\n \treturn this_dir;\n }\n \n+static int dirstat_compare(const void *_a, const void *_b)\n+{\n+\tconst struct dirstat_file *a = _a;\n+\tconst struct dirstat_file *b = _b;\n+\treturn strcmp(a->name, b->name);\n+}\n+\n static void show_dirstat(struct diff_options *options)\n {\n \tint i;\n@@ -1119,6 +1126,7 @@ static void show_dirstat(struct diff_options *options)\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->file, &dir, changed, \"\", 0);\n }\n \n"}]}