@@ -188,44 +188,143 @@ M.git_log_grepper_on_file = function(bufnr)
188
188
end , git_log_entry_maker )
189
189
end
190
190
191
- local determine_historic_file_name = function (commit_hash , bufnr )
192
- local current_file_name = file .relative_path (bufnr )
191
+ local previous_commit_hash = function (commit_hash )
192
+ local command = " git rev-parse " .. commit_hash .. " ~"
193
+ local handle = io.popen (command )
194
+ local output = handle :read (" *a" )
195
+ handle :close ()
193
196
194
- local command = " git log -M --diff-filter=R --follow --name-status --summary "
195
- .. commit_hash
196
- .. " .. -- "
197
- .. current_file_name
198
- .. " | grep ^R | tail -1 | cut -f2,2"
197
+ output = string.gsub (output , " \n " , " " )
198
+ return output
199
+ end
200
+
201
+ local all_commit_hashes = function ()
202
+ local command = " git rev-list --all"
203
+ local handle = io.popen (command )
204
+ local output = handle :read (" *a" )
205
+ handle :close ()
199
206
207
+ return utils .split_string (output , " \n " )
208
+ end
209
+
210
+ local all_commit_hashes_touching_file = function (git_relative_file_path )
211
+ local command = " cd "
212
+ .. file .git_dir ()
213
+ .. " && git log --all --follow --pretty=format:'%H' -- "
214
+ .. git_relative_file_path
215
+ local handle = io.popen (command )
216
+ local output = handle :read (" *a" )
217
+ handle :close ()
218
+
219
+ output = utils .split_string (output , " \n " )
220
+ return output
221
+ end
222
+
223
+ local file_exists_on_commit = function (commit_hash , git_relative_file_path )
224
+ local command = " cd "
225
+ .. file .git_dir ()
226
+ .. " && git ls-tree --name-only "
227
+ .. commit_hash
228
+ .. " -- "
229
+ .. git_relative_file_path
200
230
local handle = io.popen (command )
201
231
local output = handle :read (" *a" )
202
232
handle :close ()
203
233
204
234
output = string.gsub (output , " \n " , " " )
205
- if output == " " then
206
- output = file .git_relative_path (bufnr )
235
+
236
+ return output ~= " "
237
+ end
238
+
239
+ local file_name_on_commit = function (commit_hash , git_relative_file_path )
240
+ if file_exists_on_commit (commit_hash , git_relative_file_path ) then
241
+ return git_relative_file_path
242
+ end
243
+
244
+ -- FIXME: this is a very naive implementation, but it always returns the
245
+ -- correct filename for each commit (even if the commit didn't touch the file)
246
+
247
+ -- first find index in all_commit_hashes
248
+ local all_hashes = all_commit_hashes ()
249
+ local index = 0
250
+ for i , hash in ipairs (all_hashes ) do
251
+ -- compare on first 7 chars
252
+ if string.sub (hash , 1 , 7 ) == string.sub (commit_hash , 1 , 7 ) then
253
+ index = i
254
+ break
255
+ end
256
+ end
257
+
258
+ -- then find the first commit that has a different file name
259
+ local touched_hashes =
260
+ all_commit_hashes_touching_file (git_relative_file_path )
261
+ local last_touched_hash = nil
262
+ for i = index , # all_hashes do
263
+ local hash = all_hashes [i ]
264
+ -- search the hash in touched_hashes
265
+ for _ , touched_hash in ipairs (touched_hashes ) do
266
+ if string.sub (touched_hash , 1 , 7 ) == string.sub (hash , 1 , 7 ) then
267
+ last_touched_hash = touched_hash
268
+ break
269
+ end
270
+ end
271
+
272
+ -- print("searching next")
273
+ if last_touched_hash ~= nil then
274
+ break
275
+ end
207
276
end
208
277
209
- -- output is relative to git root
278
+ if last_touched_hash == nil then
279
+ return nil
280
+ end
281
+
282
+ local command = " cd "
283
+ .. file .git_dir ()
284
+ .. " && "
285
+ .. " git --no-pager log -M --follow --pretty=format:'%H' --name-only "
286
+ .. last_touched_hash
287
+ .. " ~.. -- "
288
+ .. git_relative_file_path
289
+ .. " | tail -1"
290
+
291
+ local handle = io.popen (command )
292
+ local output = handle :read (" *a" )
293
+ handle :close ()
294
+
295
+ output = string.gsub (output , " \n " , " " )
296
+
210
297
return output
211
298
end
212
299
213
300
M .git_diff_previewer_file = function (bufnr )
214
301
return previewers .new_termopen_previewer ({
215
302
get_command = function (entry )
303
+ local filename_on_head = file .git_relative_path (bufnr )
304
+
216
305
local commit_hash = entry .opts .commit_hash
217
306
218
- local prev_commit = string.format (" %s~" , commit_hash )
219
- return git_diff_command ({
220
- " git" ,
221
- " diff" ,
222
- prev_commit
223
- .. " :"
224
- .. determine_historic_file_name (prev_commit , bufnr ),
225
- commit_hash
226
- .. " :"
227
- .. determine_historic_file_name (commit_hash , bufnr ),
228
- })
307
+ local prev_commit = previous_commit_hash (commit_hash )
308
+ local curr_name = file_name_on_commit (commit_hash , filename_on_head )
309
+ local prev_name = file_name_on_commit (prev_commit , filename_on_head )
310
+
311
+ if prev_name ~= nil then
312
+ return git_diff_command ({
313
+ " git" ,
314
+ " diff" ,
315
+ prev_commit .. " :" .. prev_name ,
316
+ commit_hash .. " :" .. curr_name ,
317
+ })
318
+ else
319
+ return git_diff_command ({
320
+ " git" ,
321
+ " diff" ,
322
+ prev_commit ,
323
+ commit_hash ,
324
+ " --" ,
325
+ file .git_relative_path_to_relative_path (curr_name ),
326
+ })
327
+ end
229
328
end ,
230
329
})
231
330
end
@@ -282,7 +381,7 @@ M.current_branch = function()
282
381
return output
283
382
end
284
383
285
- M .determine_historic_file_name = determine_historic_file_name
384
+ M .file_name_on_commit = file_name_on_commit
286
385
M .git_diff_command = git_diff_command
287
386
288
387
return M
0 commit comments