Skip to content

Non-sticky range diff #3862

@stefanhaller

Description

@stefanhaller

I often want to see the diff of a range of commits. The two main use cases are:

  • I want to see a combined diff of all the commits in my branch to quickly get an overview over which files I touched.
  • I'm considering squashing two or more commits, but I'm not sure yet if it's really better; I want to see the resulting diff of both commits to help decide. As a workaround, I could simply squash them, and when I'm not happy with the result, hit z to undo; however, that doesn't work well with stacked branches.

Both of these can be solved today using diffing mode (navigate down to one end of the range (one after the end, that is), hit shift-W, navigate up to the other end). This results in exactly what I want to see (including being able to hit enter to see the files of the diff, even being able to create a custom patch from it). However, this is too many key-presses, it's not very easy to discover, and it's too sticky (it happens too often that I forget I'm in this mode, and then I move the selection around looking at diffs that make absolutely no sense).

I would find it totally intuitive if, whenever I range-select several commits (either a sticky range using v, or a non-sticky range using shift-arrow), I would see a diff of the range of those commits. This would perfectly match the general concept of "show me details of whatever is selected in the side panel", and it would solve the use cases above perfectly for me. I also want to be able to hit enter to work on the files of this range diff, like in diffing mode.

Other git clients have a similar feature, but it works slightly differently:

  • Fork requires you to make a non-contiguous multiselection of two commits (using command-click, something that lazygit doesn't support). If you select more than two, including a contiguous range of commits, it shows an error, asking you to select exactly two. Also, it requires you to select one commit below the beginning of the range (like lazygit's shift-W mode), which I find unintuitive.
  • Sublime Merge is like Fork, except that it also allows a contiguous range of commits. This is more convenient than Fork because it allows selecting with the keyboard (using shift-down), whereas Fork's non-contiguous selections can only be made with the mouse. But like Fork, it requires selecting one below the beginning of the range.

I understand that selecting one commit below the start of the range makes sense because it is closer to what you do on the command line. If you have commits A, B and C, then git diff A..C displays the combined diff of B and C, but doesn't include A's changes. That's what Fork and Sublime Merge (and shift-W in lazygit) try to emulate, it seems. But for a range selection of commits I find this very unintuitive: I want to see the combined diff of all the commits that I have selected, so I'm strongly proposing to do a git diff A^..C if A, B, and C are selected.

Note that I'm not proposing to replace diffing mode with this new non-sticky diff mode; diffing mode is probably still useful for diffing two things from different panels, e.g. a tag against a commit, or a commit against a branch.

I'm pretty far along implementing this, but I have a few questions that I'm unsure about:

  1. It is unclear to me what to do if a range of reflog entries or stash entries is selected. These types of objects don't form a linear sequence, so it doesn't make sense to display a range diff for them. We could either keep the behavior of master for these cases (i.e. show the diff for the free end of the selection), or show a message such as "Cannot display diff for range of stash entries".
  2. A similar situation arises when selecting a range of rebase todos in an interactive rebase. Since "pick" entries can be reordered, we'd be showing a diff for the range of those commits in their original position before the rebase, which could be very confusing. Same options as in 1., either use the single selection, or show a message that we can't show this diff.
  3. Finally, what if there is a range selection when we are also in diffing mode? It gets very confusing here. It might be best to keep the master behavior in this case, and diff the free end of the selection against the diffing mode anchor. But again, we could consider showing an error message instead.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions