rbong / vim-flog

A blazingly fast, stunningly beautiful, exceptionally powerful git branch viewer for Vim/Neovim.
750 stars 22 forks source link

How do you navigate to the parent / child commit? #45

Closed TamaMcGlinn closed 1 year ago

TamaMcGlinn commented 3 years ago

In GitExtensions, I can press Cntrl-P to select the (first) parent commit. If I press Cntrl-N, it selects a child commit, and prefers the most recently visited commits in that, which is an extra fancy feature because then if you press Cntrl-P X times and then Cntrl-N X times, you always end up at the same commit; this stability makes it really nice to quickly move up and down branches.

Is there such a binding in vim-flog, or do we need to write our own?

rbong commented 3 years ago

No such binding currently exists but I would not necessarily be opposed to adding it if someone made an MR. I think it could get too messy to warrant the change though. Because Flog is mostly based around running arbitrary commands against the commit graph, checking which commits were actually visited could be hard, unless if this functionality is already built into Git and I just don't know about it.

Also CTRL-N/P are already taken. I'm not sure what the default binding should be, probably [<char>/]<char> but I'm not sure what the easiest to remember char is.

TamaMcGlinn commented 3 years ago

I think I will go with [c and ]c which will override unimpaired's "next/previous change" for the flog buffer.

TamaMcGlinn commented 3 years ago

Ok, the "parent" part was surprisingly easy, while even just getting the "child" commits is an unsolved problem so far on SO. Basically:

fu! StartsWith(longer, shorter) abort
  return strpart(a:longer, 0, len(a:shorter)) == a:shorter
endfunction

function! FindItem(object, Fn) abort
    return get(filter(copy(a:object), "a:Fn(v:val)"), 0, v:null)
endfunction

fu! JumpToCommit(commit_hash) abort
  let commit = FindItem(state.commits, {item -> StartsWith(commit_hash, item.short_commit_hash)})
  let index = index(state.commits, commit)
  let index = min([max([index, 0]), len(state.commits) - 1])
  let line = index(state.line_commits, state.commits[index]) + 1
  if line >= 0
    exec line
  endif
endfunction

fu! GetCurrentShortCommitHash() abort
  let state = flog#get_state()
  let current_commit = flog#get_commit_at_line()
  if type(current_commit) != v:t_dict
    return
  endif
  return current_commit.short_commit_hash
endfunction

fu! GotoParent() abort
  let current_commit = GetCurrentShortCommitHash()
  let parent_commit = system("git rev-parse " . current_commit . "~1")
  JumpToCommit(parent_commit)
endfunction

augroup flog
  autocmd FileType floggraph nno <buffer> <silent> ]c :<C-U>call GotoParent()<CR>
augroup END
rbong commented 2 years ago

Now that with v2 we're going to be building the graph ourselves, we can jump to numbered parent and child commits without really worrying if we're getting it visually correct. We can also do other things with the graph.

I haven't really used graph-based movement in branch viewers before so it's something I have to explore. I want to keep it fairly simple and intuitive while still being useful and generalized, but given how messy graphs can get I'm not sure how yet.

I'm also going to have to be careful about how much data in the graph I actually store. It would be too easy to make performance worse than it is now. I am banking on graph building optimizations being possible down the line so I don't have to make any major changes when this is added later, but on the other hand Git needs to keep a cached commit graph to keep speed high so I don't know how much we'll be able to do.

rbong commented 1 year ago

2 years to the day since this suggestion. This has been the longest and most difficult issue to implement right.

Finally, Flog has the ability to navigate to the parent/child commit. Press } to jump to the parent. Use <count>} to jump to the nth parent given by the count. Similarly, { jumps to the child.

Note that parent counts are determined by the visual horizontal position of the parent (left to right), and child counts are determined by the visual vertical position of the child (closest first). So pressing }{ isn't necessarily going to take you to the parent and back.

Instead of preferring most recently visited child commits, I rely on Vim's jump history, so you can use <CTRL-o> to jump to a previous jump location and <CTRL-i> to jump to a future jump location. Unfortunately, jump history can't really persist over graph buffer updates, so similar to commit marks, I am going to have to implement a Flog-specific alternative.

I think the solution is nice because it's always visually consistent and relies on a vim-like feature, but I am open to feedback.

Please post a new issue if you have any issues or feedback concerning this new feature. I want to close this issue after so long.

rbong commented 1 year ago

I've implemented commit history now, so it should be easy to get back from commits when you jump to parents/children.

Use CTRL-O and CTRL-I to go back/forwards in commit jump history.