Skip to content

Commit c899588

Browse files
Fix first commit in preview (#27)
1 parent ce416cd commit c899588

File tree

3 files changed

+139
-25
lines changed

3 files changed

+139
-25
lines changed

lua/advanced_git_search/git_utils.lua

Lines changed: 121 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -188,44 +188,143 @@ M.git_log_grepper_on_file = function(bufnr)
188188
end, git_log_entry_maker)
189189
end
190190

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()
193196

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()
199206

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
200230
local handle = io.popen(command)
201231
local output = handle:read("*a")
202232
handle:close()
203233

204234
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
207276
end
208277

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+
210297
return output
211298
end
212299

213300
M.git_diff_previewer_file = function(bufnr)
214301
return previewers.new_termopen_previewer({
215302
get_command = function(entry)
303+
local filename_on_head = file.git_relative_path(bufnr)
304+
216305
local commit_hash = entry.opts.commit_hash
217306

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
229328
end,
230329
})
231330
end
@@ -282,7 +381,7 @@ M.current_branch = function()
282381
return output
283382
end
284383

285-
M.determine_historic_file_name = determine_historic_file_name
384+
M.file_name_on_commit = file_name_on_commit
286385
M.git_diff_command = git_diff_command
287386

288387
return M

lua/advanced_git_search/init.lua

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ M.diff_branch_file = function()
108108

109109
gu.open_diff_view(
110110
branch,
111-
gu.determine_historic_file_name(branch, file_name)
111+
gu.file_name_on_commit(branch, file_name)
112112
)
113113
end)
114114

@@ -300,8 +300,10 @@ M.diff_commit_file = function()
300300
actions.close(prompt_bufnr)
301301
local selection = action_state.get_selected_entry()
302302
local commit_hash = selection.opts.commit_hash
303-
local old_file_name =
304-
gu.determine_historic_file_name(commit_hash, bufnr)
303+
local old_file_name = gu.file_name_on_commit(
304+
commit_hash,
305+
file.git_relative_path(bufnr)
306+
)
305307

306308
gu.open_diff_view(commit_hash, old_file_name)
307309
end)

lua/advanced_git_search/utils/file.lua

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ M.relative_path = function(bufnr)
88
return vim.fn.expand("#" .. bufnr .. ":~:.")
99
end
1010

11+
M.git_dir = function()
12+
return M.find_first_ancestor_dir_or_file(vim.fn.getcwd(), ".git")
13+
end
14+
1115
M.file_name = function(bufnr)
1216
return vim.fn.expand("#" .. bufnr .. ":t")
1317
end
@@ -47,6 +51,15 @@ M.git_relative_path = function(bufnr)
4751
end
4852
end
4953

54+
M.git_relative_path_to_relative_path = function(git_relative_path)
55+
local git_dir = M.find_first_ancestor_dir_or_file(vim.fn.getcwd(), ".git")
56+
local project_dir = vim.fn.getcwd()
57+
58+
local absolute_path = git_dir .. "/" .. git_relative_path
59+
project_dir = utils.escape_chars(project_dir .. "/")
60+
return string.gsub(absolute_path, project_dir, "")
61+
end
62+
5063
M.path = (function()
5164
local is_windows = uv.os_uname().version:match("Windows")
5265

0 commit comments

Comments
 (0)