blame: allow to blame paths freshly added to the index

When blaming files, changes in the work tree are taken into account and displayed as being "Not Committed Yet". However, when blaming a file that is not known to the current HEAD, git blame fails with `no such path 'foo' in HEAD`, even when the file was git add'ed. Allowing such a blame is useful when the new file added to the index (not yet committed) was created by renaming an existing file. It also is useful when the new file was created from pieces already in HEAD, moved or copied from other files and blaming with copy detection (i.e. "-C"). Signed-off-by: Mike Hommey <mh@glandium.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Mike Hommey committed Jul 16, 2016 at 08:23 UTC 3b75ee93278179004bc2f117fcfe3d5d76a0a2fa
2 files changed +54 -1
builtin/blame.c
+9 -1
@@ -2230,6 +2230,7 @@ static int git_blame_config(const char *var, const char *value, void *cb)
2230 static void verify_working_tree_path(struct commit *work_tree, const char *path)
2231 {
2232 struct commit_list *parents;
2233 + int pos;
2234
2235 for (parents = work_tree->parents; parents; parents = parents->next) {
2236 const unsigned char *commit_sha1 = parents->item->object.oid.hash;
@@ -2240,7 +2241,14 @@ static void verify_working_tree_path(struct commit *work_tree, const char *path)
2241 sha1_object_info(blob_sha1, NULL) == OBJ_BLOB)
2242 return;
2243 }
2243 - die("no such path '%s' in HEAD", path);
2244 +
2245 + pos = cache_name_pos(path, strlen(path));
2246 + if (pos >= 0)
2247 + ; /* path is in the index */
2248 + else if (!strcmp(active_cache[-1 - pos]->name, path))
2249 + ; /* path is in the index, unmerged */
2250 + else
2251 + die("no such path '%s' in HEAD", path);
2252 }
2253
2254 static struct commit_list **append_parent(struct commit_list **tail, const unsigned char *sha1)
t/t8003-blame-corner-cases.sh
+45
@@ -137,6 +137,51 @@ test_expect_success 'blame wholesale copy and more' '
137
138 '
139
140 +test_expect_success 'blame wholesale copy and more in the index' '
141 +
142 + cat >horse <<-\EOF &&
143 + ABC
144 + DEF
145 + XXXX
146 + YYYY
147 + GHIJK
148 + EOF
149 + git add horse &&
150 + test_when_finished "git rm -f horse" &&
151 + git blame -f -C -C1 -- horse | sed -e "$pick_fc" >current &&
152 + cat >expected <<-\EOF &&
153 + mouse-Initial
154 + mouse-Second
155 + cow-Fifth
156 + horse-Not
157 + mouse-Third
158 + EOF
159 + test_cmp expected current
160 +
161 +'
162 +
163 +test_expect_success 'blame during cherry-pick with file rename conflict' '
164 +
165 + test_when_finished "git reset --hard && git checkout master" &&
166 + git checkout HEAD~3 &&
167 + echo MOUSE >> mouse &&
168 + git mv mouse rodent &&
169 + git add rodent &&
170 + GIT_AUTHOR_NAME=Rodent git commit -m "rodent" &&
171 + git checkout --detach master &&
172 + (git cherry-pick HEAD@{1} || test $? -eq 1) &&
173 + git show HEAD@{1}:rodent > rodent &&
174 + git add rodent &&
175 + git blame -f -C -C1 rodent | sed -e "$pick_fc" >current &&
176 + cat current &&
177 + cat >expected <<-\EOF &&
178 + mouse-Initial
179 + mouse-Second
180 + rodent-Not
181 + EOF
182 + test_cmp expected current
183 +'
184 +
185 test_expect_success 'blame path that used to be a directory' '
186 mkdir path &&
187 echo A A A A A >path/file &&