Skip to main content
This configuration provides a complete Git workflow through three main plugins:

Gitsigns

Inline git blame, hunk operations, and change indicators

Neogit

Full interactive Git UI for staging, committing, and complex operations

CodeDiff

Side-by-side diff viewer with file history and staging

Gitsigns

Gitsigns provides git decorations and hunk-level operations directly in your buffers.

Visual indicators

Changes are shown in the sign column and with number highlighting:
lua/plugins/git.lua
signs = {
  add = { text = "┃" },
  change = { text = "┃" },
  delete = { text = "_" },
  topdelete = { text = "‾" },
  changedelete = { text = "~" },
  untracked = { text = "┆" },
}
Both staged and unstaged changes are shown with separate sign columns (signs_staged_enable = true).

Hunk navigation

KeyActionDescription
[gPrevious hunkJump to previous changed block
]gNext hunkJump to next changed block
<leader>gpPreview inlineShow diff inline in buffer
<leader>gPPreview popupShow diff in floating window
lua/plugins/git.lua
{
  "[g",
  function()
    require("gitsigns").nav_hunk("prev", { wrap = true, target = "all" })
  end,
  desc = "<- hunk",
}

Git blame

Multiple ways to view blame information:
<leader>gb - Show commit info for current line in a popup
function()
  require("gitsigns").blame_line()
end

Custom blame formatter

The blame formatter shows author, message, and formatted date:
lua/plugins/git.lua
current_line_blame_formatter = function(name, blame_info, opts)
  if blame_info.author == "Not Committed Yet" then
    return { { " Not committed yet", "GitSignsCurrentLineBlame" } }
  end
  -- Replace your name with "You"
  local author = blame_info.author
  local git_user = vim.fn.system("git config user.name"):gsub("\n", "")
  if author == git_user then
    author = "You"
  end
  local date = os.date("%d %b %Y, %H:%M", tonumber(blame_info.author_time))
  local text = string.format(" %s, %s - %s", author, blame_info.summary, date)
  return { { text, "GitSignsCurrentLineBlame" } }
end

Staging operations

1

Stage hunk

<leader>ga - Stage the current hunk (git add this hunk)
function()
  require("gitsigns").stage_hunk()
end
2

Stage buffer

<leader>gA - Stage all changes in the current buffer
function()
  require("gitsigns").stage_buffer()
end
3

Unstage hunk

<leader>gu - Toggle stage state for current hunk (unstage if staged)
function()
  require("gitsigns").stage_hunk()
end

Reset operations

Reset operations discard changes permanently. Use with caution.
KeyActionDescription
<leader>grReset hunkDiscard changes in current hunk
<leader>gRReset bufferDiscard ALL changes in buffer
<leader>gDDiff HEADOpen split diff against last commit

Stash management

lua/plugins/git.lua
{
  "<leader>gS",
  function()
    Snacks.picker.git_stash()
  end,
  desc = "[S]tash (Snacks)",
}
Stash picker allows you to view, apply, and drop stashes interactively.

CodeDiff

CodeDiff provides a VSCode-like side-by-side diff interface with staging support.

Main features

Side-by-side diff

Compare files with move detection like VSCode

File staging

Stage/unstage files directly in the explorer

File history

Browse commit history with diffs

Merge-base diffs

PR-like diffs since branching point

Key bindings

KeyCommandDescription
<leader>dd:CodeDiffToggle diff explorer
<leader>dh:CodeDiff historyBrowse all commit history
<leader>d.:CodeDiff history %History for current file
<leader>df:CodeDiff file HEADDiff current file vs HEAD
<leader>dm:CodeDiff origin/main...HEADMerge-base diff vs origin/main
<leader>dMCustomPrompt for branch to compare

Merge-base diff example

lua/plugins/git.lua
{
  "<leader>dm",
  "<cmd>CodeDiff origin/main...HEAD<cr>",
  desc = "[m]erge-base diff vs origin/main",
}
This shows only changes introduced since your branch diverged from origin/main, perfect for reviewing PRs.

Custom branch comparison

lua/plugins/git.lua
{
  "<leader>dM",
  function()
    local branch = vim.fn.input("Compare with branch: ", "origin/")
    if branch ~= "" and branch ~= "origin/" then
      vim.cmd("CodeDiff " .. branch .. "...HEAD")
    end
  end,
  desc = "[M]erge-base diff vs specific branch",
}

Configuration

lua/plugins/git.lua
opts = {
  diff = {
    compute_moves = true, -- show moved lines similar to vscode
  },
  explorer = {
    flatten_dirs = false,
  },
  keymaps = {
    view = {
      align_move = "gm", -- align cursor with corresponding line in other pane
    },
  },
}
Press g? inside CodeDiff for a full list of available keybindings.

Neogit

Neogit provides a Magit-like interface for complex Git operations.

Opening Neogit

KeyCommandDescription
<leader>gn:NeogitOpen Neogit status panel
<leader>gc:Neogit commitOpen commit popup directly

Neogit features

Interactive staging

Stage/unstage files and hunks with single keypress

Commit interface

Write commits with syntax highlighting and spell check

Push/pull

Push, pull, fetch with progress indicators

Rebase operations

Interactive rebase, squash, reword commits

Branch management

Create, switch, delete branches

Stash operations

Create and apply stashes from the z menu

Configuration

lua/plugins/git.lua
opts = {
  integrations = {
    codediff = true,
    snacks = true,
  },
  diff_viewer = "codediff",
  graph_style = vim.g.neovide and "ascii" or "kitty",
  process_spinner = true,
}
Neogit uses CodeDiff for viewing diffs and Snacks for pickers, providing a seamless integrated experience.

Common workflows

  1. Open Neogit with <leader>gn
  2. Navigate to unstaged files with j/k
  3. Press s to stage a file or hunk
  4. Press c then c to open commit editor
  5. Write message and save (:wq)
Press ? inside Neogit to see all available keybindings for the current context.

Atone

Time-based blame visualization plugin:
lua/plugins/git.lua
{
  "XXiaoA/atone.nvim",
  cmd = "Atone",
  opts = {},
}
Use :Atone to visualize code age with color gradients.

Git workflow summary

1

Quick hunk operations

Use Gitsigns ([g, ]g, <leader>ga, <leader>gr) for quick inline changes
2

Review changes

Use CodeDiff (<leader>dd) for side-by-side review before staging
3

Complex operations

Use Neogit (<leader>gn) for commits, pushes, rebases, and branch management
4

Line history

Use Snacks picker (<leader>gl) to see which commits modified specific lines
5

PR review

Use merge-base diff (<leader>dm) to review changes since branching

Integration with Snacks

Additional Git commands from Snacks.nvim:
KeyCommandDescription
<leader>glGit log lineCommits that modified current line
<leader>goGit browseOpen file on GitHub/GitLab in browser
<leader>gdGit diffBrowse all changed hunks in picker
<leader>gSGit stashBrowse and manage stashes
See the Snacks.nvim documentation for more details.

Build docs developers (and LLMs) love