Skip to content

Commit 90d43b0

Browse files
peffgitster
authored andcommitted
teach diffcore-rename to optionally ignore empty content
Our rename detection is a heuristic, matching pairs of removed and added files with similar or identical content. It's unlikely to be wrong when there is actual content to compare, and we already take care not to do inexact rename detection when there is not enough content to produce good results. However, we always do exact rename detection, even when the blob is tiny or empty. It's easy to get false positives with an empty blob, simply because it is an obvious content to use as a boilerplate (e.g., when telling git that an empty directory is worth tracking via an empty .gitignore). This patch lets callers specify whether or not they are interested in using empty files as rename sources and destinations. The default is "yes", keeping the original behavior. It works by detecting the empty-blob sha1 for rename sources and destinations. One more flexible alternative would be to allow the caller to specify a minimum size for a blob to be "interesting" for rename detection. But that would catch small boilerplate files, not large ones (e.g., if you had the GPL COPYING file in many directories). A better alternative would be to allow a "-rename" gitattribute to allow boilerplate files to be marked as such. I'll leave the complexity of that solution until such time as somebody actually wants it. The complaints we've seen so far revolve around empty files, so let's start with the simple thing. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f8582ca commit 90d43b0

File tree

3 files changed

+12
-1
lines changed

3 files changed

+12
-1
lines changed

diff.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3136,6 +3136,7 @@ void diff_setup(struct diff_options *options)
31363136
options->rename_limit = -1;
31373137
options->dirstat_permille = diff_dirstat_permille_default;
31383138
options->context = 3;
3139+
DIFF_OPT_SET(options, RENAME_EMPTY);
31393140

31403141
options->change = diff_change;
31413142
options->add_remove = diff_addremove;
@@ -3506,6 +3507,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
35063507
}
35073508
else if (!strcmp(arg, "--no-renames"))
35083509
options->detect_rename = 0;
3510+
else if (!strcmp(arg, "--rename-empty"))
3511+
DIFF_OPT_SET(options, RENAME_EMPTY);
3512+
else if (!strcmp(arg, "--no-rename-empty"))
3513+
DIFF_OPT_CLR(options, RENAME_EMPTY);
35093514
else if (!strcmp(arg, "--relative"))
35103515
DIFF_OPT_SET(options, RELATIVE_NAME);
35113516
else if (!prefixcmp(arg, "--relative=")) {

diff.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ typedef struct strbuf *(*diff_prefix_fn_t)(struct diff_options *opt, void *data)
6060
#define DIFF_OPT_SILENT_ON_REMOVE (1 << 5)
6161
#define DIFF_OPT_FIND_COPIES_HARDER (1 << 6)
6262
#define DIFF_OPT_FOLLOW_RENAMES (1 << 7)
63-
/* (1 << 8) unused */
63+
#define DIFF_OPT_RENAME_EMPTY (1 << 8)
6464
/* (1 << 9) unused */
6565
#define DIFF_OPT_HAS_CHANGES (1 << 10)
6666
#define DIFF_OPT_QUICK (1 << 11)

diffcore-rename.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,9 +512,15 @@ void diffcore_rename(struct diff_options *options)
512512
else if (options->single_follow &&
513513
strcmp(options->single_follow, p->two->path))
514514
continue; /* not interested */
515+
else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
516+
is_empty_blob_sha1(p->two->sha1))
517+
continue;
515518
else
516519
locate_rename_dst(p->two, 1);
517520
}
521+
else if (!DIFF_OPT_TST(options, RENAME_EMPTY) &&
522+
is_empty_blob_sha1(p->one->sha1))
523+
continue;
518524
else if (!DIFF_PAIR_UNMERGED(p) && !DIFF_FILE_VALID(p->two)) {
519525
/*
520526
* If the source is a broken "delete", and

0 commit comments

Comments
 (0)