--dirstat-by-file: Make it faster and more correct
authorJohan Herland <johan@herland.net>
Sun, 10 Apr 2011 22:48:51 +0000 (00:48 +0200)
committerJunio C Hamano <gitster@pobox.com>
Mon, 11 Apr 2011 17:12:24 +0000 (10:12 -0700)
Currently, when using --dirstat-by-file, it first does the full --dirstat
analysis (using diffcore_count_changes()), and then resets 'damage' to 1,
if any damage was found by diffcore_count_changes().

But --dirstat-by-file is not interested in the file damage per se. It only
cares if the file changed at all. In that sense it only cares if the blob
object for a file has changed. We therefore only need to compare the
object names of each file pair in the diff queue and we can skip the
entire --dirstat analysis and simply set 'damage' to 1 for each entry
where the object name has changed.

This makes --dirstat-by-file faster, and also bypasses --dirstat's practice
of ignoring rearranged lines within a file.

The patch also contains an added testcase verifying that --dirstat-by-file
now detects changes that only rearrange lines within a file.

Signed-off-by: Johan Herland <johan@herland.net>
Acked-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
diff.c
t/t4013-diff-various.sh
t/t4013/diff.diff_--dirstat-by-file_initial_rearrange [new file with mode: 0644]

diff --git a/diff.c b/diff.c
index 3fd9e0c70315727f835f39d9a0230ee9f24e7ea8..4f5270b8dbc05c820c24075b499fe26afacf52a8 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1539,9 +1539,27 @@ static void show_dirstat(struct diff_options *options)
                struct diff_filepair *p = q->queue[i];
                const char *name;
                unsigned long copied, added, damage;
+               int content_changed;
 
                name = p->one->path ? p->one->path : p->two->path;
 
+               if (p->one->sha1_valid && p->two->sha1_valid)
+                       content_changed = hashcmp(p->one->sha1, p->two->sha1);
+               else
+                       content_changed = 1;
+
+               if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE)) {
+                       /*
+                        * In --dirstat-by-file mode, we don't really need to
+                        * look at the actual file contents at all.
+                        * The fact that the SHA1 changed is enough for us to
+                        * add this file to the list of results
+                        * (with each file contributing equal damage).
+                        */
+                       damage = content_changed ? 1 : 0;
+                       goto found_damage;
+               }
+
                if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
                        diff_populate_filespec(p->one, 0);
                        diff_populate_filespec(p->two, 0);
@@ -1564,14 +1582,11 @@ static void show_dirstat(struct diff_options *options)
                /*
                 * Original minus copied is the removed material,
                 * added is the new material.  They are both damages
-                * made to the preimage. In --dirstat-by-file mode, count
-                * damaged files, not damaged lines. This is done by
-                * counting only a single damaged line per file.
+                * made to the preimage.
                 */
                damage = (p->one->size - copied) + added;
-               if (DIFF_OPT_TST(options, DIRSTAT_BY_FILE) && damage > 0)
-                       damage = 1;
 
+found_damage:
                ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
                dir.files[dir.nr].name = name;
                dir.files[dir.nr].changed = damage;
index 3b1b392a56b292aedef08653334a1ccafe19e45c..6428a905ab76696c6d7d9e20084b3368ac855a14 100755 (executable)
@@ -302,6 +302,8 @@ diff master master^ side
 diff --dirstat master~1 master~2
 # --dirstat doesn't notice changes that simply rearrange existing lines
 diff --dirstat initial rearrange
+# ...but --dirstat-by-file does notice changes that only rearrange lines
+diff --dirstat-by-file initial rearrange
 EOF
 
 test_expect_success 'log -S requires an argument' '
diff --git a/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange b/t/t4013/diff.diff_--dirstat-by-file_initial_rearrange
new file mode 100644 (file)
index 0000000..e48e33f
--- /dev/null
@@ -0,0 +1,3 @@
+$ git diff --dirstat-by-file initial rearrange
+ 100.0% dir/
+$