Skip to content

Commit a9b2d42

Browse files
committed
blame: correctly handle a path that used to be a directory
When trying to see if the same path exists in the parent, we ran "diff-tree" with pathspec set to the path we are interested in with the parent, and expect either to have exactly one resulting filepair (either "changed from the parent", "created when there was none") or nothing (when there is no change from the parent). If the path used to be a directory, however, we will also see unbounded number of entries that talk about the files that used to exist underneath the directory in question. Correctly pick only the entry that describes the path we are interested in in such a case (namely, the creation of the path as a regular file). Noticed by Ben Willard. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 8dc3a47 commit a9b2d42

File tree

2 files changed

+33
-8
lines changed

2 files changed

+33
-8
lines changed

builtin-blame.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -362,18 +362,28 @@ static struct origin *find_origin(struct scoreboard *sb,
362362
"", &diff_opts);
363363
diffcore_std(&diff_opts);
364364

365-
/* It is either one entry that says "modified", or "created",
366-
* or nothing.
367-
*/
368365
if (!diff_queued_diff.nr) {
369366
/* The path is the same as parent */
370367
porigin = get_origin(sb, parent, origin->path);
371368
hashcpy(porigin->blob_sha1, origin->blob_sha1);
372-
}
373-
else if (diff_queued_diff.nr != 1)
374-
die("internal error in blame::find_origin");
375-
else {
376-
struct diff_filepair *p = diff_queued_diff.queue[0];
369+
} else {
370+
/*
371+
* Since origin->path is a pathspec, if the parent
372+
* commit had it as a directory, we will see a whole
373+
* bunch of deletion of files in the directory that we
374+
* do not care about.
375+
*/
376+
int i;
377+
struct diff_filepair *p = NULL;
378+
for (i = 0; i < diff_queued_diff.nr; i++) {
379+
const char *name;
380+
p = diff_queued_diff.queue[i];
381+
name = p->one->path ? p->one->path : p->two->path;
382+
if (!strcmp(name, origin->path))
383+
break;
384+
}
385+
if (!p)
386+
die("internal error in blame::find_origin");
377387
switch (p->status) {
378388
default:
379389
die("internal error in blame::find_origin (%c)",

t/t8003-blame.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,4 +129,19 @@ test_expect_success 'blame wholesale copy and more' '
129129
130130
'
131131

132+
test_expect_success 'blame path that used to be a directory' '
133+
mkdir path &&
134+
echo A A A A A >path/file &&
135+
echo B B B B B >path/elif &&
136+
git add path &&
137+
test_tick &&
138+
git commit -m "path was a directory" &&
139+
rm -fr path &&
140+
echo A A A A A >path &&
141+
git add path &&
142+
test_tick &&
143+
git commit -m "path is a regular file" &&
144+
git blame HEAD^.. -- path
145+
'
146+
132147
test_done

0 commit comments

Comments
 (0)