Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions Documentation/config/blame.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,21 @@ blame.markUnblamableLines::
blame.markIgnoredLines::
Mark lines that were changed by an ignored revision that we attributed to
another commit with a '?' in the output of linkgit:git-blame[1].

blame.renames::
If set to `false`, disable rename following in
linkgit:git-blame[1]. This option defaults to `true`.

blame.renameThreshold::
The minimum similarity threshold for rename detection in
linkgit:git-blame[1]; equivalent to the `-M` option of
linkgit:git-diff[1]. The value is a percentage (e.g. `50%`),
or a fraction between 0 and 1 (e.g. `0.5`). If not set, the
default is 50%. To limit blame to only follow exact renames,
set `blame.renameThreshold = 100%`.

blame.renameLimit::
The number of files to consider when performing rename
detection in linkgit:git-blame[1]; equivalent to the `-l`
option of linkgit:git-diff[1]. If not set, the default
value is currently 1000.
4 changes: 0 additions & 4 deletions Documentation/config/diff.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,6 @@ endif::git-diff[]
percentage (e.g. `50%`), or a fraction between 0 and 1
(e.g. `0.5`). If not set, the default is 50%. This setting
has no effect if rename detection is turned off.
+
This config setting is also respected by linkgit:git-blame[1];
to limit blame to only consider exact renames, for example, set
`diff.renameThreshold = 100%`.

`diff.renames`::
Whether and how Git detects renames. If set to `false`,
Expand Down
10 changes: 5 additions & 5 deletions Documentation/git-blame.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ When specified one or more times, `-L` restricts annotation to the requested
lines.

The origin of lines is automatically followed across whole-file
renames (currently there is no option to turn the rename-following
off, but the minimum similarity threshold can be adjusted with
`diff.renameThreshold`; see linkgit:git-diff[1]). To follow lines
moved from one file to another, or to follow lines that were copied
and pasted from another file, etc., see the `-C` and `-M` options.
renames. This can be disabled with `blame.renames`, and the minimum
similarity threshold can be adjusted with `blame.renameThreshold`
(see linkgit:git-config[1]). To follow lines moved from one file to
another, or to follow lines that were copied and pasted from another
file, etc., see the `-C` and `-M` options.

The report does not tell you anything about lines which have been deleted or
replaced; you need to use a tool such as `git diff` or the "pickaxe"
Expand Down
27 changes: 26 additions & 1 deletion builtin/blame.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ static int incremental;
static int xdl_opts;
static int abbrev = -1;
static int no_whole_file_rename;
static int blame_detect_rename = -1;
Comment thread
tyrielv marked this conversation as resolved.
static int show_progress;
static char repeated_meta_color[COLOR_MAXLEN];
static int coloring_mode;
Expand Down Expand Up @@ -780,6 +781,27 @@ static int git_blame_config(const char *var, const char *value,
}
}

if (!strcmp(var, "blame.renames")) {
blame_detect_rename = git_config_bool(var, value);
return 0;
}

/*
* Blame does not use git_diff_basic_config in its config
* chain, so diff_rename_score_default is not normally loaded.
* Forward blame.renameThreshold as diff.renameThreshold to
* set the global that repo_diff_setup() copies into
* diff_options.rename_score.
*/
if (!strcmp(var, "blame.renamethreshold"))
return git_diff_basic_config("diff.renamethreshold",
value, ctx, cb);

/* Same approach for blame.renameLimit; see above. */
if (!strcmp(var, "blame.renamelimit"))
return git_diff_basic_config("diff.renamelimit",
value, ctx, cb);

if (!strcmp(var, "diff.algorithm")) {
long diff_algorithm;
if (!value)
Expand Down Expand Up @@ -1028,7 +1050,10 @@ int cmd_blame(int argc,
}
parse_done:
revision_opts_finish(&revs);
no_whole_file_rename = !revs.diffopt.flags.follow_renames;
if (blame_detect_rename >= 0)
no_whole_file_rename = !blame_detect_rename;
if (!revs.diffopt.flags.follow_renames)
no_whole_file_rename = 1;
xdl_opts |= revs.diffopt.xdl_opts & XDF_INDENT_HEURISTIC;
revs.diffopt.flags.follow_renames = 0;
argc = parse_options_end(&ctx);
Expand Down
19 changes: 10 additions & 9 deletions diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -399,15 +399,6 @@ int git_diff_ui_config(const char *var, const char *value,
diff_detect_rename_default = git_config_rename(var, value);
return 0;
}
if (!strcmp(var, "diff.renamethreshold")) {
const char *arg = value;
if (!value)
return config_error_nonbool(var);
diff_rename_score_default = parse_rename_score(&arg);
if (*arg)
return error(_("invalid value for '%s': '%s'"), var, value);
return 0;
}
if (!strcmp(var, "diff.autorefreshindex")) {
diff_auto_refresh_index = git_config_bool(var, value);
return 0;
Expand Down Expand Up @@ -494,6 +485,16 @@ int git_diff_basic_config(const char *var, const char *value,
return 0;
}

if (!strcmp(var, "diff.renamethreshold")) {
const char *arg = value;
if (!value)
return config_error_nonbool(var);
diff_rename_score_default = parse_rename_score(&arg);
if (*arg)
return error(_("invalid value for '%s': '%s'"), var, value);
return 0;
}

if (userdiff_config(var, value) < 0)
return -1;

Expand Down
1 change: 1 addition & 0 deletions t/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,7 @@ integration_tests = [
't8013-blame-ignore-revs.sh',
't8014-blame-ignore-fuzzy.sh',
't8015-blame-diff-algorithm.sh',
't8016-blame-rename-config.sh',
't8020-last-modified.sh',
't8100-git-survey.sh',
't9001-send-email.sh',
Expand Down
51 changes: 51 additions & 0 deletions t/t8016-blame-rename-config.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/sh

test_description='git blame rename configuration options'

. ./test-lib.sh

test_expect_success 'setup' '
test_write_lines line1 line2 line3 >v1-before-inexact.txt &&
test_write_lines other1 other2 other3 >unrelated.txt &&
git add v1-before-inexact.txt unrelated.txt &&
GIT_AUTHOR_NAME=Original git commit -m "add files" &&

test_write_lines changed1 line2 line3 >v2-before-exact.txt &&
git rm v1-before-inexact.txt &&
git rm unrelated.txt &&
git add v2-before-exact.txt &&
GIT_AUTHOR_NAME=Inexact git commit -m "inexact rename with content change" &&

git mv v2-before-exact.txt v3.txt &&
GIT_AUTHOR_NAME=Exact git commit -m "exact rename"
'

test_expect_success 'blame follows renames by default' '
git blame --porcelain v3.txt >output &&
grep "^filename v1-before-inexact.txt" output
'

test_expect_success 'blame.renames=false disables rename following' '
git -c blame.renames=false blame --porcelain v3.txt >output &&
! grep "^filename v1-before-inexact.txt" output &&
! grep "^filename v2-before-exact.txt" output
'

test_expect_success 'blame.renameThreshold=100% allows exact but skips inexact renames' '
git -c blame.renameThreshold=100% blame --porcelain v3.txt >output &&
grep "^filename v2-before-exact.txt" output &&
! grep "^filename v1-before-inexact.txt" output
'

test_expect_success 'blame.renameLimit=1 skips when sources*destinations exceeds limit' '
git -c blame.renameLimit=1 blame --porcelain v3.txt >output &&
grep "^filename v2-before-exact.txt" output &&
! grep "^filename v1-before-inexact.txt" output
'

test_expect_success 'blame.renameLimit=2 detects with two sources' '
git -c blame.renameLimit=2 blame --porcelain v3.txt >output &&
grep "^filename v1-before-inexact.txt" output
'

test_done
Loading