@@ -198,83 +198,147 @@ local previous_commit_hash = function(commit_hash)
198
198
return output
199
199
end
200
200
201
- --- Determine the historic file name for a given commit hash and buffer number
202
- --- @param commit_hash string
203
- --- @param bufnr number
204
- --- @return string | nil file_path historic file name relative to git root
205
- local determine_historic_file_name = function (commit_hash , bufnr )
206
- local current_file_name = file .git_relative_path (bufnr )
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 ()
206
+
207
+ return utils .split_string (output , " \n " )
208
+ end
207
209
210
+ local all_commit_hashes_touching_file = function (git_relative_file_path )
208
211
local command = " cd "
209
212
.. file .git_dir ()
210
- .. " && "
211
- .. " git --no-pager log -M --follow --name-status --oneline "
212
- .. commit_hash
213
- .. " ~.. -- "
214
- .. current_file_name
215
- .. " | tail -2"
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
216
222
223
+ local is_file_renamed = function (git_relative_file_path )
224
+ local command = " cd "
225
+ .. file .git_dir ()
226
+ .. " && git log --all --follow --diff-filter=R --pretty=format:'%H' -- "
227
+ .. git_relative_file_path
217
228
local handle = io.popen (command )
218
229
local output = handle :read (" *a" )
219
230
handle :close ()
220
231
221
- -- first line contains the commit_hash and message
222
- -- second line contains the file status and file name (when rename, 2 filenames are present)
223
232
output = utils .split_string (output , " \n " )
233
+ return # output > 0
234
+ end
224
235
225
- if output [1 ] == nil or output [2 ] == nil then
226
- return nil
236
+ local file_exists_on_commit = function (commit_hash , git_relative_file_path )
237
+ local command = " cd "
238
+ .. file .git_dir ()
239
+ .. " && git ls-tree --name-only "
240
+ .. commit_hash
241
+ .. " -- "
242
+ .. git_relative_file_path
243
+ local handle = io.popen (command )
244
+ local output = handle :read (" *a" )
245
+ handle :close ()
246
+
247
+ output = string.gsub (output , " \n " , " " )
248
+
249
+ return output ~= " "
250
+ end
251
+
252
+ local file_name_on_commit = function (commit_hash , git_relative_file_path )
253
+ if file_exists_on_commit (commit_hash , git_relative_file_path ) then
254
+ return git_relative_file_path
255
+ end
256
+
257
+ -- FIXME: this is a very naive implementation, but it always returns the
258
+ -- correct filename for each commit (even if the commit didn't touch the file)
259
+
260
+ -- first find index in all_commit_hashes
261
+ local all_hashes = all_commit_hashes ()
262
+ local index = 0
263
+ for i , hash in ipairs (all_hashes ) do
264
+ -- compare on first 7 chars
265
+ if string.sub (hash , 1 , 7 ) == string.sub (commit_hash , 1 , 7 ) then
266
+ index = i
267
+ break
268
+ end
227
269
end
228
270
229
- local returned_hash = utils .split_string (output [1 ])[1 ]
271
+ -- then find the first commit that has a different file name
272
+ local touched_hashes =
273
+ all_commit_hashes_touching_file (git_relative_file_path )
274
+ local last_touched_hash = nil
275
+ for i = index , # all_hashes do
276
+ local hash = all_hashes [i ]
277
+ -- search the hash in touched_hashes
278
+ for _ , touched_hash in ipairs (touched_hashes ) do
279
+ if string.sub (touched_hash , 1 , 7 ) == string.sub (hash , 1 , 7 ) then
280
+ last_touched_hash = touched_hash
281
+ break
282
+ end
283
+ end
230
284
231
- -- only return the file name if the commit hash matches, otherwise return nil
232
- if string.sub (returned_hash , 1 , 7 ) == string.sub (commit_hash , 1 , 7 ) then
233
- local split_output = utils .split_string (output [2 ])
234
- return split_output [# split_output ]
235
- else
285
+ -- print("searching next")
286
+ if last_touched_hash ~= nil then
287
+ break
288
+ end
289
+ end
290
+
291
+ if last_touched_hash == nil then
236
292
return nil
237
293
end
294
+
295
+ local command = " cd "
296
+ .. file .git_dir ()
297
+ .. " && "
298
+ .. " git --no-pager log -M --follow --pretty=format:'%H' --name-only "
299
+ .. last_touched_hash
300
+ .. " ~.. -- "
301
+ .. git_relative_file_path
302
+ .. " | tail -1"
303
+
304
+ local handle = io.popen (command )
305
+ local output = handle :read (" *a" )
306
+ handle :close ()
307
+
308
+ output = string.gsub (output , " \n " , " " )
309
+
310
+ return output
238
311
end
239
312
240
313
M .git_diff_previewer_file = function (bufnr )
241
314
return previewers .new_termopen_previewer ({
242
315
get_command = function (entry )
316
+ local filename_on_head = file .git_relative_path (bufnr )
317
+
243
318
local commit_hash = entry .opts .commit_hash
244
319
245
320
local prev_commit = previous_commit_hash (commit_hash )
246
- local prev_name = determine_historic_file_name (prev_commit , bufnr )
247
- local curr_name = determine_historic_file_name (commit_hash , bufnr )
248
- if prev_name == nil and curr_name ~= nil then
321
+ local curr_name = nil
322
+ local prev_name = nil
323
+
324
+ curr_name = file_name_on_commit (commit_hash , filename_on_head )
325
+ prev_name = file_name_on_commit (prev_commit , filename_on_head )
326
+
327
+ if prev_name ~= nil then
249
328
return git_diff_command ({
250
329
" git" ,
251
330
" diff" ,
252
- prev_commit ,
253
- commit_hash ,
254
- " --" ,
255
- file .git_dir ()
256
- .. " /"
257
- .. determine_historic_file_name (commit_hash , bufnr ),
331
+ prev_commit .. " :" .. prev_name ,
332
+ commit_hash .. " :" .. curr_name ,
258
333
})
259
- elseif prev_name == nil and curr_name == nil then
334
+ else
260
335
return git_diff_command ({
261
336
" git" ,
262
337
" diff" ,
263
338
prev_commit ,
264
339
commit_hash ,
265
340
" --" ,
266
- file .git_relative_path (bufnr ),
267
- })
268
- else
269
- return git_diff_command ({
270
- " git" ,
271
- " diff" ,
272
- prev_commit
273
- .. " :"
274
- .. determine_historic_file_name (prev_commit , bufnr ),
275
- commit_hash
276
- .. " :"
277
- .. determine_historic_file_name (commit_hash , bufnr ),
341
+ file .git_relative_path_to_relative_path (curr_name ),
278
342
})
279
343
end
280
344
end ,
@@ -333,7 +397,7 @@ M.current_branch = function()
333
397
return output
334
398
end
335
399
336
- M .determine_historic_file_name = determine_historic_file_name
400
+ M .file_name_on_commit = file_name_on_commit
337
401
M .git_diff_command = git_diff_command
338
402
339
403
return M
0 commit comments