Skip to content

Commit 230ff3e

Browse files
committed
Merge branch 'jc/blame-ignore-fix'
"git blame --ignore-rev/--ignore-revs-file" failed to validate their input are valid revision, and failed to take into account that the user may want to give an annotated tag instead of a commit, which has been corrected. * jc/blame-ignore-fix: blame: validate and peel the object names on the ignore list t8013: minimum preparatory clean-up
2 parents 86cca37 + 610e2b9 commit 230ff3e

File tree

4 files changed

+81
-25
lines changed

4 files changed

+81
-25
lines changed

builtin/blame.c

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "object-store.h"
2828
#include "blame.h"
2929
#include "refs.h"
30+
#include "tag.h"
3031

3132
static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
3233

@@ -803,6 +804,26 @@ static int is_a_rev(const char *name)
803804
return OBJ_NONE < oid_object_info(the_repository, &oid, NULL);
804805
}
805806

807+
static int peel_to_commit_oid(struct object_id *oid_ret, void *cbdata)
808+
{
809+
struct repository *r = ((struct blame_scoreboard *)cbdata)->repo;
810+
struct object_id oid;
811+
812+
oidcpy(&oid, oid_ret);
813+
while (1) {
814+
struct object *obj;
815+
int kind = oid_object_info(r, &oid, NULL);
816+
if (kind == OBJ_COMMIT) {
817+
oidcpy(oid_ret, &oid);
818+
return 0;
819+
}
820+
if (kind != OBJ_TAG)
821+
return -1;
822+
obj = deref_tag(r, parse_object(r, &oid), NULL, 0);
823+
oidcpy(&oid, &obj->oid);
824+
}
825+
}
826+
806827
static void build_ignorelist(struct blame_scoreboard *sb,
807828
struct string_list *ignore_revs_file_list,
808829
struct string_list *ignore_rev_list)
@@ -815,10 +836,12 @@ static void build_ignorelist(struct blame_scoreboard *sb,
815836
if (!strcmp(i->string, ""))
816837
oidset_clear(&sb->ignore_list);
817838
else
818-
oidset_parse_file(&sb->ignore_list, i->string);
839+
oidset_parse_file_carefully(&sb->ignore_list, i->string,
840+
peel_to_commit_oid, sb);
819841
}
820842
for_each_string_list_item(i, ignore_rev_list) {
821-
if (get_oid_committish(i->string, &oid))
843+
if (get_oid_committish(i->string, &oid) ||
844+
peel_to_commit_oid(&oid, sb))
822845
die(_("cannot find revision %s to ignore"), i->string);
823846
oidset_insert(&sb->ignore_list, &oid);
824847
}

oidset.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ int oidset_size(struct oidset *set)
4242
}
4343

4444
void oidset_parse_file(struct oidset *set, const char *path)
45+
{
46+
oidset_parse_file_carefully(set, path, NULL, NULL);
47+
}
48+
49+
void oidset_parse_file_carefully(struct oidset *set, const char *path,
50+
oidset_parse_tweak_fn fn, void *cbdata)
4551
{
4652
FILE *fp;
4753
struct strbuf sb = STRBUF_INIT;
@@ -66,7 +72,8 @@ void oidset_parse_file(struct oidset *set, const char *path)
6672
if (!sb.len)
6773
continue;
6874

69-
if (parse_oid_hex(sb.buf, &oid, &p) || *p != '\0')
75+
if (parse_oid_hex(sb.buf, &oid, &p) || *p != '\0' ||
76+
(fn && fn(&oid, cbdata)))
7077
die("invalid object name: %s", sb.buf);
7178
oidset_insert(set, &oid);
7279
}

oidset.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,15 @@ void oidset_clear(struct oidset *set);
7373
*/
7474
void oidset_parse_file(struct oidset *set, const char *path);
7575

76+
/*
77+
* Similar to the above, but with a callback which can (1) return non-zero to
78+
* signal displeasure with the object and (2) replace object ID with something
79+
* else (meant to be used to "peel").
80+
*/
81+
typedef int (*oidset_parse_tweak_fn)(struct object_id *, void *);
82+
void oidset_parse_file_carefully(struct oidset *set, const char *path,
83+
oidset_parse_tweak_fn fn, void *cbdata);
84+
7685
struct oidset_iter {
7786
kh_oid_set_t *set;
7887
khiter_t iter;

t/t8013-blame-ignore-revs.sh

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ test_expect_success setup '
2121
test_tick &&
2222
git commit -m X &&
2323
git tag X &&
24+
git tag -a -m "X (annotated)" XT &&
2425
2526
git blame --line-porcelain file >blame_raw &&
2627
@@ -31,20 +32,36 @@ test_expect_success setup '
3132
grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
3233
git rev-parse X >expect &&
3334
test_cmp expect actual
35+
'
36+
37+
# Ensure bogus --ignore-rev requests are caught
38+
test_expect_success 'validate --ignore-rev' '
39+
test_must_fail git blame --ignore-rev X^{tree} file
40+
'
41+
42+
# Ensure bogus --ignore-revs-file requests are caught
43+
test_expect_success 'validate --ignore-revs-file' '
44+
git rev-parse X^{tree} >ignore_x &&
45+
test_must_fail git blame --ignore-revs-file ignore_x file
46+
'
47+
48+
for I in X XT
49+
do
50+
# Ignore X (or XT), make sure A is blamed for line 1 and B for line 2.
51+
# Giving X (i.e. commit) and XT (i.e. annotated tag to commit) should
52+
# produce the same result.
53+
test_expect_success "ignore_rev_changing_lines ($I)" '
54+
git blame --line-porcelain --ignore-rev $I file >blame_raw &&
55+
56+
grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
57+
git rev-parse A >expect &&
58+
test_cmp expect actual &&
59+
60+
grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
61+
git rev-parse B >expect &&
62+
test_cmp expect actual
3463
'
35-
36-
# Ignore X, make sure A is blamed for line 1 and B for line 2.
37-
test_expect_success ignore_rev_changing_lines '
38-
git blame --line-porcelain --ignore-rev X file >blame_raw &&
39-
40-
grep -E "^[0-9a-f]+ [0-9]+ 1" blame_raw | sed -e "s/ .*//" >actual &&
41-
git rev-parse A >expect &&
42-
test_cmp expect actual &&
43-
44-
grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
45-
git rev-parse B >expect &&
46-
test_cmp expect actual
47-
'
64+
done
4865

4966
# For ignored revs that have added 'unblamable' lines, attribute those to the
5067
# ignored commit.
@@ -67,7 +84,7 @@ test_expect_success ignore_rev_adding_unblamable_lines '
6784
6885
grep -E "^[0-9a-f]+ [0-9]+ 4" blame_raw | sed -e "s/ .*//" >actual &&
6986
test_cmp expect actual
70-
'
87+
'
7188

7289
# Ignore X and Y, both in separate files. Lines 1 == A, 2 == B.
7390
test_expect_success ignore_revs_from_files '
@@ -82,7 +99,7 @@ test_expect_success ignore_revs_from_files '
8299
grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
83100
git rev-parse B >expect &&
84101
test_cmp expect actual
85-
'
102+
'
86103

87104
# Ignore X from the config option, Y from a file.
88105
test_expect_success ignore_revs_from_configs_and_files '
@@ -96,7 +113,7 @@ test_expect_success ignore_revs_from_configs_and_files '
96113
grep -E "^[0-9a-f]+ [0-9]+ 2" blame_raw | sed -e "s/ .*//" >actual &&
97114
git rev-parse B >expect &&
98115
test_cmp expect actual
99-
'
116+
'
100117

101118
# Override blame.ignoreRevsFile (ignore_x) with an empty string. X should be
102119
# blamed now for lines 1 and 2, since we are no longer ignoring X.
@@ -120,7 +137,7 @@ test_expect_success bad_files_and_revs '
120137
echo NOREV >ignore_norev &&
121138
test_must_fail git blame file --ignore-revs-file ignore_norev 2>err &&
122139
test_i18ngrep "invalid object name: NOREV" err
123-
'
140+
'
124141

125142
# For ignored revs that have added 'unblamable' lines, mark those lines with a
126143
# '*'
@@ -138,7 +155,7 @@ test_expect_success mark_unblamable_lines '
138155
139156
sed -n "4p" blame_raw | cut -c1 >actual &&
140157
test_cmp expect actual
141-
'
158+
'
142159

143160
# Commit Z will touch the first two lines. Y touched all four.
144161
# A--B--X--Y--Z
@@ -171,7 +188,7 @@ test_expect_success mark_ignored_lines '
171188
172189
sed -n "4p" blame_raw | cut -c1 >actual &&
173190
! test_cmp expect actual
174-
'
191+
'
175192

176193
# For ignored revs that added 'unblamable' lines and more recent commits changed
177194
# the blamable lines, mark the unblamable lines with a
@@ -190,7 +207,7 @@ test_expect_success mark_unblamable_lines_intermediate '
190207
191208
sed -n "4p" blame_raw | cut -c1 >actual &&
192209
test_cmp expect actual
193-
'
210+
'
194211

195212
# The heuristic called by guess_line_blames() tries to find the size of a
196213
# blame_entry 'e' in the parent's address space. Those calculations need to
@@ -227,7 +244,7 @@ test_expect_success ignored_chunk_negative_parent_size '
227244
git tag C &&
228245
229246
git blame file --ignore-rev B >blame_raw
230-
'
247+
'
231248

232249
# Resetting the repo and creating:
233250
#
@@ -269,6 +286,6 @@ test_expect_success ignore_merge '
269286
grep -E "^[0-9a-f]+ [0-9]+ 9" blame_raw | sed -e "s/ .*//" >actual &&
270287
git rev-parse C >expect &&
271288
test_cmp expect actual
272-
'
289+
'
273290

274291
test_done

0 commit comments

Comments
 (0)