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 functionmaint
commit
de67293e74
71
line-log.c
71
line-log.c
|
@ -737,6 +737,38 @@ static struct line_log_data *lookup_line_range(struct rev_info *revs,
|
|||
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)
|
||||
{
|
||||
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);
|
||||
add_line_range(rev, commit, range);
|
||||
|
||||
if (!rev->diffopt.detect_rename) {
|
||||
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);
|
||||
}
|
||||
parse_pathspec_from_ranges(&rev->diffopt.pathspec, range);
|
||||
}
|
||||
|
||||
static void move_diff_queue(struct diff_queue_struct *dst,
|
||||
|
@ -817,15 +836,29 @@ static void queue_diffs(struct line_log_data *range,
|
|||
struct diff_queue_struct *queue,
|
||||
struct commit *commit, struct commit *parent)
|
||||
{
|
||||
struct object_id *tree_oid, *parent_tree_oid;
|
||||
|
||||
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_tree_oid(parent ? get_commit_tree_oid(parent) : NULL,
|
||||
get_commit_tree_oid(commit), "", opt);
|
||||
if (opt->detect_rename) {
|
||||
diff_tree_oid(parent_tree_oid, tree_oid, "", opt);
|
||||
if (opt->detect_rename && diff_might_be_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);
|
||||
if (diff_might_be_rename())
|
||||
diffcore_std(opt);
|
||||
diffcore_std(opt);
|
||||
filter_diffs_for_paths(range, 0);
|
||||
}
|
||||
move_diff_queue(queue, &diff_queued_diff);
|
||||
|
|
|
@ -132,4 +132,86 @@ test_expect_success '--raw is forbidden' '
|
|||
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
|
||||
|
|
Loading…
Reference in New Issue