Git in Neovim — Using vgit.nvim and nvim-tinygit
Published on Feb 28, 2026
If you have been following the Neovim configuration series on CodingBrush, you know that we have been steadily building out our editor with plugins that make the development experience feel more complete. So far we have set up a plugin manager with Lazy.nvim, added a statusline, and configured options to make Neovim feel like home. Now it is time to bring Git right into the editor — no more jumping between the terminal and your code just to stage a hunk or check who wrote a particular line.
In this post we will be setting up two plugins that complement each other really well: vgit.nvim for visual git feedback and diff previews inside your buffers, and nvim-tinygit for streamlined commit and push operations. Together they cover the full day-to-day git workflow without ever leaving Neovim.
Prerequisites
Before we begin, make sure you have:
- Neovim installed (0.8 or later recommended)
- Git installed and available in your PATH
- Lazy.nvim set up as your plugin manager (see the Plugin Manager post)
nvim-lua/plenary.nvim— a dependency used by both pluginsnvim-tree/nvim-web-devicons— for icons in the UInvim-telescope/telescope.nvim— required by nvim-tinygit for its interactive pickers
If you are missing any of these, refer back to the earlier posts in the series to get them installed first.
What Each Plugin Does
vgit.nvim is a visual Git plugin by tanvirtin. It overlays git information directly onto your buffers — think inline blame annotations, hunk highlights in the gutter, diff previews in floating windows, and a project-wide diff view. It is all about giving you visual feedback about the state of your code in relation to Git, without you having to think about it.
nvim-tinygit by chrisgrieser is a lightweight Git client focused on the action side of things. Committing, pushing, amending, interactive staging — all of these are handled through a compact set of commands with a Telescope-powered interface. It is not trying to replicate every git command. Instead it targets the 20% of git operations you use 80% of the time and makes those as fast as possible.
Installing the Plugins
In the plugins folder of your Neovim config, create a new file called git.lua. Following the pattern from the Plugin Manager post, every plugin file returns a table spec that Lazy.nvim can read.
-- lua/plugins/git.lua
return {
-- Visual git feedback in buffers
{
"tanvirtin/vgit.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"nvim-tree/nvim-web-devicons",
},
-- Lazy loading on VimEnter is necessary for vgit
event = "VimEnter",
config = function()
require("vgit").setup()
end,
},
-- Streamlined git operations
{
"chrisgrieser/nvim-tinygit",
dependencies = {
"stevearc/dressing.nvim",
"nvim-telescope/telescope.nvim",
"nvim-lua/plenary.nvim",
},
},
}
Save the file and open Neovim. Lazy.nvim will detect the new specs and prompt you to install. You can also run :Lazy sync to trigger the install manually.
Configuring vgit.nvim
The default require("vgit").setup() call works out of the box, but we can go further by setting keymaps and tuning some settings. Let’s update the config function:
config = function()
require("vgit").setup({
keymaps = {
-- Navigate between hunks
["n <C-k>"] = function() require("vgit").hunk_up() end,
["n <C-j>"] = function() require("vgit").hunk_down() end,
-- Stage or reset a hunk under the cursor
["n <leader>gs"] = function() require("vgit").buffer_hunk_stage() end,
["n <leader>gr"] = function() require("vgit").buffer_hunk_reset() end,
-- Preview the hunk in a floating window
["n <leader>gp"] = function() require("vgit").buffer_hunk_preview() end,
-- Blame the current line
["n <leader>gb"] = "buffer_blame_preview",
-- Show the full diff of the current buffer vs index
["n <leader>gf"] = function() require("vgit").buffer_diff_preview() end,
-- Show the commit history of the current buffer
["n <leader>gh"] = function() require("vgit").buffer_history_preview() end,
-- Reset the entire buffer to the index version
["n <leader>gu"] = function() require("vgit").buffer_reset() end,
-- Project-wide diff view (all changed files)
["n <leader>gd"] = function() require("vgit").project_diff_preview() end,
},
settings = {
live_blame = {
enabled = true,
format = function(blame, git_config)
local config_author = git_config["user.name"]
local author = blame.author
if config_author == author then
author = "You"
end
local time = os.difftime(os.time(), blame.author_time)
/ (60 * 60 * 24 * 30 * 12)
local time_divisions = {
{ 1, "years" },
{ 12, "months" },
{ 30, "days" },
{ 24, "hours" },
{ 60, "minutes" },
{ 60, "seconds" },
}
local counter = 1
local time_divs_length = #time_divisions
while counter <= time_divs_length do
local time_division = time_divisions[counter]
local time_boundary = time_division[1]
local time_text = time_division[2]
if time < time_boundary then
time = time * time_boundary
if counter ~= 1 then
time_text = time_divisions[counter - 1][2]
end
if time < 1 then
return string.format(
" %s • Last changed just now",
author
)
end
return string.format(
" %s • %s %s ago",
author,
math.floor(time),
time_text
)
end
counter = counter + 1
end
end,
},
live_gutter = {
enabled = true,
},
authorship_code_lens = {
enabled = true,
},
},
})
end,
This gives you a solid set of shortcuts all grouped under <leader>g. You can navigate hunks with Ctrl+k / Ctrl+j, preview diffs in a floating window, view blame on any line, and get a birds-eye project diff.
Configuring nvim-tinygit
nvim-tinygit works best when you add explicit keymaps for the operations you use most. Create or update your keymaps section, either in the plugin spec or in a separate keymaps.lua file under lua/config/:
-- Inside the nvim-tinygit plugin spec, or in lua/config/keymaps.lua
-- Interactive staging (like git add -p, powered by Telescope)
vim.keymap.set("n", "<leader>ga", function()
require("tinygit").interactiveStaging()
end, { desc = "Git: interactive stage" })
-- Smart commit — stages all if nothing staged, then opens commit popup
vim.keymap.set("n", "<leader>gc", function()
require("tinygit").smartCommit()
end, { desc = "Git: smart commit" })
-- Smart commit + push when branch is clean
vim.keymap.set("n", "<leader>gC", function()
require("tinygit").smartCommit({ pushIfClean = true })
end, { desc = "Git: commit and push" })
-- Push
vim.keymap.set("n", "<leader>gP", function()
require("tinygit").push()
end, { desc = "Git: push" })
-- Amend last commit message only (no new changes)
vim.keymap.set("n", "<leader>gm", function()
require("tinygit").amendOnlyMsg()
end, { desc = "Git: amend message" })
-- Amend last commit without editing message (add staged changes)
vim.keymap.set("n", "<leader>gA", function()
require("tinygit").amendNoEdit()
end, { desc = "Git: amend no edit" })
-- Fixup commit — pick a recent commit to attach a fixup to
vim.keymap.set("n", "<leader>gx", function()
require("tinygit").fixupCommit({ autoRebase = true })
end, { desc = "Git: fixup commit" })
-- Browse issues and PRs from GitHub
vim.keymap.set("n", "<leader>gi", function()
require("tinygit").issuesAndPrs({ type = "all", state = "open" })
end, { desc = "Git: issues and PRs" })
A Typical Workflow
With both plugins configured, here is how a typical coding session looks:
1. Checking your changes. As soon as you edit a file that is tracked by Git, vgit.nvim lights up the gutter with colored signs showing added, removed, and changed lines. You can jump between hunks with <C-j> / <C-k> and hit <leader>gp to see a floating diff of just that hunk — without leaving the file.
2. Staging selectively. When you are ready to commit, press <leader>ga to open nvim-tinygit’s interactive staging view, powered by Telescope. This is roughly equivalent to git add -p. Use <Space> to toggle a hunk in or out of the staging area and <CR> to jump to it in context. You can also stage individual hunks directly from vgit with <leader>gs.
3. Committing. Hit <leader>gc to open the smartCommit popup. Type your commit message — nvim-tinygit automatically counts characters to help you stay within the conventional 72-character limit for the subject line. Press <Enter> to confirm.
4. Pushing. Either use <leader>gP for a standalone push, or skip a step entirely by using <leader>gC (commit + push when clean). If you need to pull before pushing, smartCommit has pullBeforePush = true enabled by default.
5. Reviewing history. Press <leader>gh to open vgit’s buffer history preview — a scrollable log of every commit that touched the current file. Select any entry to see a full diff of what changed in that commit.
6. Fixing up. Made a small follow-up change that belongs in the last commit? Stage it and press <leader>gA for amendNoEdit. Or use <leader>gx to pick any recent commit as a fixup target, with autoRebase = true to squash everything automatically.
Tip: Combining with Which-Key
If you use folke/which-key.nvim in your config (which is covered in the Which-Key post on CodingBrush), adding a group label for <leader>g makes the git keymaps easy to discover:
require("which-key").add({
{ "<leader>g", group = "Git" },
})
Now pressing <leader>g will show a popup with all your git actions labeled and ready to go.
Wrapping Up
Between vgit.nvim and nvim-tinygit, the full git loop — spot changes, stage hunks, commit, push, review history — is covered without leaving Neovim. vgit handles the visual and inspection side while nvim-tinygit handles the action side, and the two feel natural side by side under the same <leader>g namespace.
As always, the configuration above is a starting point. Both plugins have extensive documentation on their GitHub pages if you want to dig deeper into available commands or tweak the UI to your taste.