Merge branch 'sg/line-log-tree-diff-optim'
Optimize unnecessary full-tree diff away from "git log -L" machinery. * sg/line-log-tree-diff-optim: line-log: avoid unnecessary full tree diffs line-log: extract pathspec parsing from line ranges into a helper function
This commit is contained in:
69
line-log.c
69
line-log.c
@ -737,6 +737,38 @@ static struct line_log_data *lookup_line_range(struct rev_info *revs,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int same_paths_in_pathspec_and_range(struct pathspec *pathspec,
|
||||||
|
struct line_log_data *range)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct line_log_data *r;
|
||||||
|
|
||||||
|
for (i = 0, r = range; i < pathspec->nr && r; i++, r = r->next)
|
||||||
|
if (strcmp(pathspec->items[i].match, r->path))
|
||||||
|
return 0;
|
||||||
|
if (i < pathspec->nr || r)
|
||||||
|
/* different number of pathspec items and ranges */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_pathspec_from_ranges(struct pathspec *pathspec,
|
||||||
|
struct line_log_data *range)
|
||||||
|
{
|
||||||
|
struct line_log_data *r;
|
||||||
|
struct argv_array array = ARGV_ARRAY_INIT;
|
||||||
|
const char **paths;
|
||||||
|
|
||||||
|
for (r = range; r; r = r->next)
|
||||||
|
argv_array_push(&array, r->path);
|
||||||
|
paths = argv_array_detach(&array);
|
||||||
|
|
||||||
|
parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL, "", paths);
|
||||||
|
/* strings are now owned by pathspec */
|
||||||
|
free(paths);
|
||||||
|
}
|
||||||
|
|
||||||
void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args)
|
void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args)
|
||||||
{
|
{
|
||||||
struct commit *commit = NULL;
|
struct commit *commit = NULL;
|
||||||
@ -746,20 +778,7 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list
|
|||||||
range = parse_lines(rev->diffopt.repo, commit, prefix, args);
|
range = parse_lines(rev->diffopt.repo, commit, prefix, args);
|
||||||
add_line_range(rev, commit, range);
|
add_line_range(rev, commit, range);
|
||||||
|
|
||||||
if (!rev->diffopt.detect_rename) {
|
parse_pathspec_from_ranges(&rev->diffopt.pathspec, range);
|
||||||
struct line_log_data *r;
|
|
||||||
struct argv_array array = ARGV_ARRAY_INIT;
|
|
||||||
const char **paths;
|
|
||||||
|
|
||||||
for (r = range; r; r = r->next)
|
|
||||||
argv_array_push(&array, r->path);
|
|
||||||
paths = argv_array_detach(&array);
|
|
||||||
|
|
||||||
parse_pathspec(&rev->diffopt.pathspec, 0,
|
|
||||||
PATHSPEC_PREFER_FULL, "", paths);
|
|
||||||
/* strings are now owned by pathspec */
|
|
||||||
free(paths);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void move_diff_queue(struct diff_queue_struct *dst,
|
static void move_diff_queue(struct diff_queue_struct *dst,
|
||||||
@ -817,14 +836,28 @@ static void queue_diffs(struct line_log_data *range,
|
|||||||
struct diff_queue_struct *queue,
|
struct diff_queue_struct *queue,
|
||||||
struct commit *commit, struct commit *parent)
|
struct commit *commit, struct commit *parent)
|
||||||
{
|
{
|
||||||
|
struct object_id *tree_oid, *parent_tree_oid;
|
||||||
|
|
||||||
assert(commit);
|
assert(commit);
|
||||||
|
|
||||||
|
tree_oid = get_commit_tree_oid(commit);
|
||||||
|
parent_tree_oid = parent ? get_commit_tree_oid(parent) : NULL;
|
||||||
|
|
||||||
|
if (opt->detect_rename &&
|
||||||
|
!same_paths_in_pathspec_and_range(&opt->pathspec, range)) {
|
||||||
|
clear_pathspec(&opt->pathspec);
|
||||||
|
parse_pathspec_from_ranges(&opt->pathspec, range);
|
||||||
|
}
|
||||||
DIFF_QUEUE_CLEAR(&diff_queued_diff);
|
DIFF_QUEUE_CLEAR(&diff_queued_diff);
|
||||||
diff_tree_oid(parent ? get_commit_tree_oid(parent) : NULL,
|
diff_tree_oid(parent_tree_oid, tree_oid, "", opt);
|
||||||
get_commit_tree_oid(commit), "", opt);
|
if (opt->detect_rename && diff_might_be_rename()) {
|
||||||
if (opt->detect_rename) {
|
/* must look at the full tree diff to detect renames */
|
||||||
|
clear_pathspec(&opt->pathspec);
|
||||||
|
DIFF_QUEUE_CLEAR(&diff_queued_diff);
|
||||||
|
|
||||||
|
diff_tree_oid(parent_tree_oid, tree_oid, "", opt);
|
||||||
|
|
||||||
filter_diffs_for_paths(range, 1);
|
filter_diffs_for_paths(range, 1);
|
||||||
if (diff_might_be_rename())
|
|
||||||
diffcore_std(opt);
|
diffcore_std(opt);
|
||||||
filter_diffs_for_paths(range, 0);
|
filter_diffs_for_paths(range, 0);
|
||||||
}
|
}
|
||||||
|
@ -132,4 +132,86 @@ test_expect_success '--raw is forbidden' '
|
|||||||
test_must_fail git log -L1,24:b.c --raw
|
test_must_fail git log -L1,24:b.c --raw
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'setup for checking fancy rename following' '
|
||||||
|
git checkout --orphan moves-start &&
|
||||||
|
git reset --hard &&
|
||||||
|
|
||||||
|
printf "%s\n" 12 13 14 15 b c d e >file-1 &&
|
||||||
|
printf "%s\n" 22 23 24 25 B C D E >file-2 &&
|
||||||
|
git add file-1 file-2 &&
|
||||||
|
test_tick &&
|
||||||
|
git commit -m "Add file-1 and file-2" &&
|
||||||
|
oid_add_f1_f2=$(git rev-parse --short HEAD) &&
|
||||||
|
|
||||||
|
git checkout -b moves-main &&
|
||||||
|
printf "%s\n" 11 12 13 14 15 b c d e >file-1 &&
|
||||||
|
git commit -a -m "Modify file-1 on main" &&
|
||||||
|
oid_mod_f1_main=$(git rev-parse --short HEAD) &&
|
||||||
|
|
||||||
|
printf "%s\n" 21 22 23 24 25 B C D E >file-2 &&
|
||||||
|
git commit -a -m "Modify file-2 on main #1" &&
|
||||||
|
oid_mod_f2_main_1=$(git rev-parse --short HEAD) &&
|
||||||
|
|
||||||
|
git mv file-1 renamed-1 &&
|
||||||
|
git commit -m "Rename file-1 to renamed-1 on main" &&
|
||||||
|
|
||||||
|
printf "%s\n" 11 12 13 14 15 b c d e f >renamed-1 &&
|
||||||
|
git commit -a -m "Modify renamed-1 on main" &&
|
||||||
|
oid_mod_r1_main=$(git rev-parse --short HEAD) &&
|
||||||
|
|
||||||
|
printf "%s\n" 21 22 23 24 25 B C D E F >file-2 &&
|
||||||
|
git commit -a -m "Modify file-2 on main #2" &&
|
||||||
|
oid_mod_f2_main_2=$(git rev-parse --short HEAD) &&
|
||||||
|
|
||||||
|
git checkout -b moves-side moves-start &&
|
||||||
|
printf "%s\n" 12 13 14 15 16 b c d e >file-1 &&
|
||||||
|
git commit -a -m "Modify file-1 on side #1" &&
|
||||||
|
oid_mod_f1_side_1=$(git rev-parse --short HEAD) &&
|
||||||
|
|
||||||
|
printf "%s\n" 22 23 24 25 26 B C D E >file-2 &&
|
||||||
|
git commit -a -m "Modify file-2 on side" &&
|
||||||
|
oid_mod_f2_side=$(git rev-parse --short HEAD) &&
|
||||||
|
|
||||||
|
git mv file-2 renamed-2 &&
|
||||||
|
git commit -m "Rename file-2 to renamed-2 on side" &&
|
||||||
|
|
||||||
|
printf "%s\n" 12 13 14 15 16 a b c d e >file-1 &&
|
||||||
|
git commit -a -m "Modify file-1 on side #2" &&
|
||||||
|
oid_mod_f1_side_2=$(git rev-parse --short HEAD) &&
|
||||||
|
|
||||||
|
printf "%s\n" 22 23 24 25 26 A B C D E >renamed-2 &&
|
||||||
|
git commit -a -m "Modify renamed-2 on side" &&
|
||||||
|
oid_mod_r2_side=$(git rev-parse --short HEAD) &&
|
||||||
|
|
||||||
|
git checkout moves-main &&
|
||||||
|
git merge moves-side &&
|
||||||
|
oid_merge=$(git rev-parse --short HEAD)
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'fancy rename following #1' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
$oid_merge Merge branch '\''moves-side'\'' into moves-main
|
||||||
|
$oid_mod_f1_side_2 Modify file-1 on side #2
|
||||||
|
$oid_mod_f1_side_1 Modify file-1 on side #1
|
||||||
|
$oid_mod_r1_main Modify renamed-1 on main
|
||||||
|
$oid_mod_f1_main Modify file-1 on main
|
||||||
|
$oid_add_f1_f2 Add file-1 and file-2
|
||||||
|
EOF
|
||||||
|
git log -L1:renamed-1 --oneline --no-patch >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'fancy rename following #2' '
|
||||||
|
cat >expect <<-EOF &&
|
||||||
|
$oid_merge Merge branch '\''moves-side'\'' into moves-main
|
||||||
|
$oid_mod_r2_side Modify renamed-2 on side
|
||||||
|
$oid_mod_f2_side Modify file-2 on side
|
||||||
|
$oid_mod_f2_main_2 Modify file-2 on main #2
|
||||||
|
$oid_mod_f2_main_1 Modify file-2 on main #1
|
||||||
|
$oid_add_f1_f2 Add file-1 and file-2
|
||||||
|
EOF
|
||||||
|
git log -L1:renamed-2 --oneline --no-patch >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Reference in New Issue
Block a user