When the conversion filter for a file is changed, files may get listed as modified even though the user has not made any changes to them. This patch adds a configuration option 'core.refilterOnDiff', which performs an extra renormalization pass to filter out such files. Signed-off-by: Henrik Grubbström <grubba@xxxxxxxxxx> --- The typical reason to enable this option is when you have lots of files that have been affected by a configuration change (eg crlf convention or ident expansion), but don't want to recommit the otherwise unchanged files just to get them on canonic form in the repository. Documentation/config.txt | 6 ++++++ cache.h | 1 + config.c | 5 +++++ diff.c | 42 ++++++++++++++++++++++++++++++++++++++++++ environment.c | 1 + t/t0021-conversion.sh | 25 +++++++++++++++++++++++++ 6 files changed, 80 insertions(+), 0 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 06b2f82..4eb3ab3 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -535,6 +535,12 @@ core.sparseCheckout:: Enable "sparse checkout" feature. See section "Sparse checkout" in linkgit:git-read-tree[1] for more information. +core.refilterOnDiff:: + Enable "refilter on diff" feature. This causes source files that + have only changed from the committed version as a side effect of + a conversion filter change to be filtered from the output of eg + linkgit:git-status[1] and linkgit:git-diff[1]. + add.ignore-errors:: Tells 'git add' to continue adding files when some files cannot be added due to indexing errors. Equivalent to the '--ignore-errors' diff --git a/cache.h b/cache.h index 6dcb100..cd2bca4 100644 --- a/cache.h +++ b/cache.h @@ -552,6 +552,7 @@ extern int read_replace_refs; extern int fsync_object_files; extern int core_preload_index; extern int core_apply_sparse_checkout; +extern int core_refilter_on_diff; enum safe_crlf { SAFE_CRLF_FALSE = 0, diff --git a/config.c b/config.c index 6963fbe..4954797 100644 --- a/config.c +++ b/config.c @@ -523,6 +523,11 @@ static int git_default_core_config(const char *var, const char *value) return 0; } + if(!strcmp(var, "core.refilterondiff")) { + core_refilter_on_diff = git_config_bool(var, value); + return 0; + } + /* Add other config variables here and to Documentation/config.txt. */ return 0; } diff --git a/diff.c b/diff.c index 2daa732..b2d8e6d 100644 --- a/diff.c +++ b/diff.c @@ -8,6 +8,8 @@ #include "delta.h" #include "xdiff-interface.h" #include "color.h" +#include "cache.h" +#include "object.h" #include "attr.h" #include "run-command.h" #include "utf8.h" @@ -3097,6 +3099,46 @@ int diff_unmodified_pair(struct diff_filepair *p) return 1; /* no change */ if (!one->sha1_valid && !two->sha1_valid) return 1; /* both look at the same file on the filesystem. */ + if (one->dirty_submodule || two->dirty_submodule) + return 0; /* Known to differ. */ + /* The hashes differ, but this might be due to either of them + * not having been normalized (eg due to later .gitattributes + * changes. + */ + if (core_refilter_on_diff) { + unsigned char one_sha1_norm[20]; + unsigned char two_sha1_norm[20]; + struct strbuf nbuf = STRBUF_INIT; + unsigned long buflen = 0; + void *buf; + + diff_fill_sha1_info(one); + diff_fill_sha1_info(two); + memcpy(one_sha1_norm, one->sha1, 20); + memcpy(two_sha1_norm, two->sha1, 20); + + buf = read_object_with_reference(one->sha1, typename(OBJ_BLOB), + &buflen, one_sha1_norm); + if (buf && convert_to_git(one->path, buf, buflen, + &nbuf, safe_crlf)) + hash_sha1_file(nbuf.buf, nbuf.len, + typename(OBJ_BLOB), one_sha1_norm); + if (buf) + free(buf); + + buf = read_object_with_reference(two->sha1, typename(OBJ_BLOB), + &buflen, two_sha1_norm); + if (buf && convert_to_git(two->path, buf, buflen, + &nbuf, safe_crlf)) + hash_sha1_file(nbuf.buf, nbuf.len, + typename(OBJ_BLOB), two_sha1_norm); + if (buf) + free(buf); + + strbuf_release(&nbuf); + if (!hashcmp(one_sha1_norm, two_sha1_norm)) + return 1; /* Same hash after normalization. */ + } return 0; } diff --git a/environment.c b/environment.c index 876c5e5..1b52bed 100644 --- a/environment.c +++ b/environment.c @@ -52,6 +52,7 @@ enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE; char *notes_ref_name; int grafts_replace_parents = 1; int core_apply_sparse_checkout; +int core_refilter_on_diff; /* Parallel index stat data preload? */ int core_preload_index = 0; diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index 828e35b..48ae8bb 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -93,4 +93,29 @@ test_expect_success expanded_in_repo ' cmp expanded-keywords expected-output ' +# Check that files containing keywords with proper markup aren't marked +# as modified on checkout when core.refilterOnDiff is set. +test_expect_success keywords_not_modified ' + { + echo "File with foreign keywords" + echo "\$Id\$" + echo "\$Id: NoTerminatingSymbol" + echo "\$Id: Foreign Commit With Spaces \$" + echo "\$Id: GitCommitId \$" + echo "\$Id: NoTerminatingSymbolAtEOF" + } > expanded-keywords2 && + + git add expanded-keywords2 && + git commit -m "File with keywords expanded" && + + echo "expanded-keywords2 ident" >> .gitattributes && + + rm -f expanded-keywords2 && + git checkout -- expanded-keywords2 && + test "x`git status --porcelain -- expanded-keywords2`" = \ + "x M expanded-keywords2" && + git config --add core.refilterondiff true && + test "x`git status --porcelain -- expanded-keywords2`" = x +' + test_done -- 1.7.0.3.316.g33b5e -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html