Bundle of commands focused on swift and streamlined git operations.
dressing.nvim
and nvim-notify
are no longer dependencies.telescope.nvim
is now a required dependency.commit.insertIssuesOnHashSign
feature has been removed. Better issue
insertion via plugins like cmp-git or
blink-cmp-git now work there.smartCommit
was overhauled. Among other improvements, it now supports a
commit body.[!NOTE] If you want to keep using the previous version, without these breaking changes, you can pin the tag
v0.9
:-- lazy.nvim { "chrisgrieser/nvim-tinygit", tag = "v0.9" dependencies = "stevearc/dressing.nvim", },
git add -A
). Optionally trigger
a git push
afterward.git blame
of a file and branch state.Requirements
curl
for GitHub-related featuresTSInstall gitcommit
-- lazy.nvim (automatically takes care of dependencies)
{ "chrisgrieser/nvim-tinygit" },
-- packer
use {
"chrisgrieser/nvim-tinygit",
requires = "nvim-telescope/telescope.nvim",
}
The setup
call is optional.
-- default config
require("tinygit").setup {
stage = { -- requires `telescope.nvim`
contextSize = 1, -- larger values "merge" hunks. 0 is not supported.
stagedIndicator = "",
keymaps = { -- insert & normal mode
stagingToggle = "<Space>", -- stage/unstage hunk
gotoHunk = "<CR>",
resetHunk = "<C-r>",
},
moveToNextHunkOnStagingToggle = false,
-- accepts the common telescope picker config
telescopeOpts = {
layout_strategy = "horizontal",
layout_config = {
horizontal = {
preview_width = 0.65,
height = { 0.7, min = 20 },
},
},
},
},
commit = {
keepAbortedMsgSecs = 300,
border = "single",
spellcheck = false, -- vim's builtin spellcheck
wrap = "hard", ---@type "hard"|"soft"|"none"
keymaps = {
normal = { abort = "q", confirm = "<CR>" },
insert = { confirm = "<C-CR>" },
},
subject = {
-- automatically apply formatting to the subject line
autoFormat = function(subject) ---@type nil|fun(subject: string): string
subject = subject:gsub("%.$", "") -- remove trailing dot https://commitlint.js.org/reference/rules.html#body-full-stop
return subject
end,
-- disallow commits that do not use an allowed type
enforceType = false,
-- stylua: ignore
types = {
"fix", "feat", "chore", "docs", "refactor", "build", "test",
"perf", "style", "revert", "ci", "break",
},
},
body = {
enforce = false,
},
},
push = {
preventPushingFixupCommits = true,
confirmationSound = true, -- currently macOS only, PRs welcome
-- If pushed commits contain references to issues, open them in the browser
-- (not used when using force-push).
openReferencedIssues = false,
},
github = {
icons = {
openIssue = "🟢",
closedIssue = "🟣",
notPlannedIssue = "⚪",
openPR = "🟩",
mergedPR = "🟪",
draftPR = "⬜",
closedPR = "🟥",
},
},
history = {
diffPopup = {
width = 0.8, -- between 0-1
height = 0.8,
border = "single",
},
autoUnshallowIfNeeded = false,
},
appearance = {
mainIcon = "",
backdrop = {
enabled = true,
blend = 40, -- 0-100
},
},
statusline = {
blame = {
ignoreAuthors = {}, -- hide component if from these authors (useful for bots)
hideAuthorNames = {}, -- show component, but hide names (useful for your own name)
maxMsgLen = 40,
icon = "ﰖ",
},
branchState = {
icons = {
ahead = "",
behind = "",
diverge = "",
},
},
},
}
All commands are available via lua function or sub-command of :Tinygit
, for
example require("tinygit").interactiveStaging()
and :Tinygit interactiveStaging
. However, do note that the lua function is preferable,
since the :Tinygit
does not accept command-specific options and does not
trigger visual-mode specific changes to the commands.
git add -p
.<Space>
to (un)stage the hunk, <CR>
to go to the hunk, or <C-r
to
reset the hunk (mappings customizable). Your regular telescope
mappings also
apply.staging.contextSize
.
Larger context size is going to "merge" changes that are close to one another
into one hunk. (As such, the hunks displayed are not 1:1 the same as the hunks
from gitsigns.nvim
.) A context size between 1 and 4 is recommended.contextSize=0
(= no merging at all) is not supported.require("tinygit").interactiveStaging()
git add --all
) before the
commit. Optionally run git push
if the repo is clean after committing.git push
is triggered afterward, so there are
no surprises.-- values shown are the defaults
require("tinygit").smartCommit { pushIfClean = false, pullBeforePush = true }
Example workflow
Assuming these keybindings:
vim.keymap.set("n", "<leader>ga", function() require("tinygit").interactiveStaging() end, { desc = "git add" })
vim.keymap.set("n", "<leader>gc", function() require("tinygit").smartCommit() end, { desc = "git commit" })
vim.keymap.set("n", "<leader>gp", function() require("tinygit").push() end, { desc = "git push" })
<leader>ga
.<leader>gc
to enter a commit message.<leader>gp
to push the commits.Using pushIfClean = true
allows you to combine staging, committing, and
pushing into a single step, when it is the last commit you intend to make.
Amending
amendOnlyMsg
just opens the commit popup to change the last commit message,
and does not stage any changes.amendNoEdit
keeps the last commit message; if there are no staged changes,
stages all changes (git add --all
), like smartCommit
.git push --force-with-lease
afterward, if the branch has
diverged (that is, the amended commit was already pushed).-- values shown are the defaults
require("tinygit").amendOnlyMsg { forcePushIfDiverged = false }
require("tinygit").amendNoEdit { forcePushIfDiverged = false, stageAllIfNothingStaged = true }
Fixup commits
fixupCommit
lets you select a commit from the last X commits and runs git commit --fixup
on the selected commit.git add --all
), like
smartCommit
.autoRebase = true
automatically runs rebase with --autosquash
and
--autostash
afterward, confirming all fixups and squashes without opening a
rebase to do editor. Note that this can potentially result in conflicts.-- values shown are the defaults
require("tinygit").fixupCommit {
selectFromLastXCommits = 15,
autoRebase = false,
}
require("tinygit").undoLastCommitOrAmend()
git reset --mixed
.)push
operation done as a followup (such as .smartCommit { pushIfClean = false }
), the last commit is not undone.Search issues & PRs
curl
.-- state: all|closed|open (default: all)
-- type: all|issue|pr (default: all)
require("tinygit").issuesAndPrs { type = "all", state = "all" }
-- alternative: if the word under the cursor is of the form `#123`,
-- open that issue/PR
require("tinygit").openIssueUnderCursor()
GitHub URL
Creates a permalink to the current file/lines at GitHub. The link is opened in
the browser and copied to the system clipboard. In normal mode, uses the current
file, in visual mode, uses the selected lines. (Note that visual mode detection
requires you to use the lua function below instead of the :Tinygit
ex-command.)
"file"
: code view"blame"
: blame view"repo"
: repo root-- file|repo|blame (default: file)
require("tinygit").githubUrl("file")
push
can be combined with other actions, depending on the options.createGitHubPr
opens a PR from the current branch browser. (This requires the
repo to be a fork with sufficient information on the remote.)-- values shown are the defaults
require("tinygit").push {
pullBefore = false,
forceWithLease = false,
createGitHubPr = false,
}
require("tinygit").createGitHubPr()
Search the git history of the current file. Select from the matching commits to open a popup with a diffview of the changes.
If the config history.autoUnshallowIfNeeded
is set to true
, will also
automatically un-shallow the repo if needed.
require("tinygit").fileHistory()
The type of history search depends on the mode .fileHistory
is called from:
git log -G
)git log -L
).git
uses heuristics to determine the enclosing function of
a change, so this is not
100% perfect and has varying reliability across languages.git log -L
).Note that visual mode detection requires you to use the lua function above
instead of the :Tinygit
ex-command.
Simple wrappers around git stash push
and git stash pop
.
require("tinygit").stashPush()
require("tinygit").stashPop()
Shows the message and date (git blame
) of the last commit that changed the
current file (not line).
require("tinygit.statusline").blame()
[!TIP] Some status line plugins also allow you to put components into the tab line or win bar. If your status line is too crowded, you can add the blame-component to one of those bars instead.
The component can be configured with the statusline.blame
options in the plugin
configuration.
Shows whether the local branch is ahead or behind of its remote counterpart.
(Note that this component does not run git fetch
for performance reasons, so
the component may not be up-to-date with remote changes.)
require("tinygit.statusline").branchState()
In my day job, I am a sociologist studying the social mechanisms underlying the digital economy. For my PhD project, I investigate the governance of the app economy and how software ecosystems manage the tension between innovation and compatibility. If you are interested in this subject, feel free to get in touch.
I also occasionally blog about vim: Nano Tips for Vim