Skip to content

Commit

Permalink
refactor: amend & empty commit msg
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisgrieser committed Sep 20, 2023
1 parent fdde5cd commit 41a2b88
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 73 deletions.
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ indent_size = 3
tab_width = 3
trim_trailing_whitespace = true

[*.lua]
max_line_length = 80

[*.{yml,yaml}]
max_line_length = 120
indent_style = space
Expand Down
84 changes: 67 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,102 @@
<!-- LTeX: enabled=false -->
# nvim-tinygit <!-- LTeX: enabled=true -->
<a href="https://dotfyle.com/plugins/chrisgrieser/nvim-tinygit"><img src="https://dotfyle.com/plugins/chrisgrieser/nvim-tinygit/shield" /></a>
<!-- TODO uncomment shields when available in dotfyle.com -->
<!-- <a href="https://dotfyle.com/plugins/chrisgrieser/nvim-tinygit"><img src="https://dotfyle.com/plugins/chrisgrieser/nvim-tinygit/shield" /></a> -->

A minimalistic git client for nvim.
Lightweight git client for nvim for quick commits and other quality of life improvements.

<!--toc:start-->
- [Features](#features)
- [Features](#main-features)
- [Installation](#installation)
- [Usage](#usage)
- [Configuration](#configuration)
- [Limitations](#limitations)
- [Credits](#credits)
<!--toc:end-->

## Features
## Main Features
- Smart-Commit: Open a popup to enter a commit message. If there are no staged changed, stages all changes before doing so (`git add -A`).
- Commit Messages have syntax highlighting, indicators for [commit message overlength](https://stackoverflow.com/questions/2290016/git-commit-messages-50-72-formatting), and optionally enforce conventional commits keywords.
- Option to run `git push` in a non-blocking manner after committing.
- Quick amends.
- Search issues & PRs. Open the selected issue or PR in the browser.
- Open the GitHub URL of the current file or selection.
- Non-Goal: Become [neogit](https://github.com/TimUntersberger/neogit) or [gitsigns.nvim](https://github.com/lewis6991/gitsigns.nvim). `tinygit` is intended to complement those plugins with some simple commands, not replace them.

## Installation

```lua
-- lazy.nvim
{
"chrisgrieser/nvim-tinygit",
opts = {

},
dependencies = "stevearc/dressing.nvim",
},

-- packer
use {
"chrisgrieser/nvim-tinygit",
config = function ()
require("tinygit").setup ({

})
end,
requires = "stevearc/dressing.nvim",
}
```

Optionally, install the Treesitter parser for git commits for some syntax highlighting of your commit messages, like for example emphasized conventional commit keywords: `TSInstall gitcommit`

## Usage

```lua
-- Open a commit popup. If there are no staged changes, stage all changes (`git add -A`) before the commit. Optionally runs `git push` afterwards.
-- 💡 You can use gitsigns.nvim's `add_hunk` command to stage changes.
require("tinygit").smartCommit({ push = false }) -- options default to `false`

-- Quick amends. `noedit = false` will open a commit message popup.
-- Optionally runs `git push --force` afterwards (only recommended for single-person repos).
require("tinygit").amend ({ forcePush = false, noedit = false }) -- options default to `false`

-- Search issues & PRs.
-- (Uses telescope, if you configure dressing.nvim to use telescope as selector.)
require("tinygit").issuesAndPrs("all") -- all|closed|open (default: all)

-- Open at GitHub and copy the URL to the system clipboard.
-- Normal mode: the current file, visual mode: the current selection.
require("tinygit").githubUrl("file") -- file|repo (default: file)

-- `git push`
require("tinygit").push({ pullBefore = false, force = false }) -- options default to `false`
```

## Configuration

```lua
-- default values
opts = {
local defaultConfig = {
-- Why 72-50? see https://stackoverflow.com/q/2290016/22114136
commitMaxLen = 72,
smallCommitMaxLen = 50,

-- when conforming the commit message popup with an empty message,
-- fill in this message. (Set to `false` to disallow empty commit messages.)
emptyCommitMsgFillIn = "squash", -- string|false

-- deny commit messages without a conventinal commit keyword
enforceConvCommits = {
enabled = true,
keywords = {
"chore", "build", "test", "fix", "feat", "refactor", "perf",
"style", "revert", "ci", "docs", "break", "improv",
},
},
-- icons for the issue/PR search
issueIcons = {
closedIssue = "🟣",
openIssue = "🟢",
openPR = "🟦",
mergedPR = "🟨",
closedPR = "🟥",
},

-- confirmation sounds on finished async operations (like push)
confirmationSoundsOnMacOs = true,
}
```

## Limitations

## Credits
<!-- vale Google.FirstPerson = NO -->
__About Me__
Expand Down
129 changes: 73 additions & 56 deletions lua/tinygit.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ local fn = vim.fn

--------------------------------------------------------------------------------
local defaultConfig = {
commitMaxLen = 72, -- https://stackoverflow.com/q/2290016/22114136
smallCommitMaxLen = 50,
useSoundOnMacOs = true,
commitMaxLen = 72,
smallCommitMaxLen = 50, -- https://stackoverflow.com/q/2290016/22114136
emptyCommitMsgFillIn = "chore",
enforceConvCommits = {
enabled = true,
-- stylua: ignore
keywords = { "chore", "build", "test", "fix", "feat", "refactor", "perf", "style", "revert", "ci", "docs", "improv", "break" },
keywords = {
"chore", "build", "test", "fix", "feat", "refactor", "perf",
"style", "revert", "ci", "docs", "break", "improv",
},
},
issueIcons = {
closedIssue = "🟣",
Expand All @@ -18,10 +21,11 @@ local defaultConfig = {
mergedPR = "🟨",
closedPR = "🟥",
},
confirmationSoundsOnMacOs = true,
}

-- set values if setup call is not run
local config = defaultConfig
local config = defaultConfig

function M.setup(userConf) config = vim.tbl_extend("force", defaultConfig, userConf) end

Expand Down Expand Up @@ -75,48 +79,10 @@ end
---@param soundFilepath string
local function playSoundMacOS(soundFilepath)
local onMacOs = fn.has("macunix") == 1
if not onMacOs or not config.useSoundOnMacOs then return end
if not onMacOs or not config.confirmationSoundsOnMacOs then return end
fn.system(("afplay %q &"):format(soundFilepath))
end

local gitShellOpts = {
stdout_buffered = true,
stderr_buffered = true,
detach = true, -- finish even when quitting nvim
on_stdout = function(_, data)
if data[1] == "" and #data == 1 then return end
local output = vim.trim(table.concat(data, "\n"))

-- no need to notify that the pull in `git pull ; git push` yielded no update
if output:find("Current branch .* is up to date") then return end

notify(output)
playSoundMacOS(
"/System/Library/Components/CoreAudio.component/Contents/SharedSupport/SystemSounds/siri/jbl_confirm.caf" -- codespell-ignore
)
end,
on_stderr = function(_, data)
if data[1] == "" and #data == 1 then return end
local output = vim.trim(table.concat(data, "\n"))

-- git often puts non-errors into STDERR, therefore checking here again
-- whether it is actually an error or not
local logLevel = "info"
local sound =
"/System/Library/Components/CoreAudio.component/Contents/SharedSupport/SystemSounds/siri/jbl_confirm.caf" -- codespell-ignore
if output:lower():find("error") then
logLevel = "error"
sound = "/System/Library/Sounds/Basso.aiff"
elseif output:lower():find("warning") then
logLevel = "warn"
sound = "/System/Library/Sounds/Basso.aiff"
end

notify(output, logLevel)
playSoundMacOS(sound)
end,
}

---process a commit message: length, not empty, adheres to conventional commits
---@param commitMsg string
---@nodiscard
Expand All @@ -129,7 +95,12 @@ local function processCommitMsg(commitMsg)
local shortenedMsg = commitMsg:sub(1, config.commitMaxLen)
return false, shortenedMsg
elseif commitMsg == "" then
return true, "chore"
if not config.emptyCommitMsgFillIn then
notify("Commit Message empty.", "warn")
return false, ""
else
return true, config.emptyCommitMsgFillIn
end
end

if config.enforceConvCommits then
Expand Down Expand Up @@ -186,18 +157,15 @@ end
--------------------------------------------------------------------------------

---@param opts? object
function M.amendNoEdit(opts)
if not opts then opts = {} end
vim.cmd("silent update")
if notInGitRepo() then return end

local function amendNoEdit(opts)
-- show the message of the last commit
local lastCommitMsg = vim.trim(fn.system("git log -1 --pretty=%B"))
notify('󰊢 Amend-No-Edit & Force Push…\n"' .. lastCommitMsg .. '"')
notify('󰊢 Amend-No-Edit\n"' .. lastCommitMsg .. '"')

local stderr = fn.system("git add -A && git commit --amend --no-edit")
if nonZeroExit(stderr) then return end

if opts.forcePush then fn.jobstart("git push --force", gitShellOpts) end
if opts.forcePush then M.push { force = true } end
end

---@param opts? object
Expand All @@ -207,6 +175,11 @@ function M.amend(opts, prefillMsg)
vim.cmd("silent update")
if notInGitRepo() then return end

if opts.noedit then
amendNoEdit(opts)
return
end

if not prefillMsg then
local lastCommitMsg = vim.trim(fn.system("git log -1 --pretty=%B"))
prefillMsg = lastCommitMsg
Expand All @@ -225,10 +198,12 @@ function M.amend(opts, prefillMsg)
local stderr = fn.system("git add -A && git commit --amend -m '" .. newMsg .. "'")
if nonZeroExit(stderr) then return end

if opts.forcePush then fn.jobstart("git push --force", gitShellOpts) end
if opts.forcePush then M.push { force = true } end
end)
end

--------------------------------------------------------------------------------

---If there are staged changes, commit them.
---If there aren't, add all changes (`git add -A`) and then commit.
---@param prefillMsg? string
Expand All @@ -254,17 +229,59 @@ function M.smartCommit(opts, prefillMsg)
local stderr = fn.system { "git", "add", "-A" }
if nonZeroExit(stderr) then return end
end
notify('󰊢 Smart Commit\n"' .. newMsg .. '"')
notify('󰊢 Smart Commit\n"' .. newMsg .. '"')

local stderr = fn.system { "git", "commit", "-m", newMsg }
if nonZeroExit(stderr) then return end

if opts.push then M.push() end
if opts.push then M.push { pullBefore = true } end
end)
end

-- pull before to avoid conflicts
function M.push() fn.jobstart("git pull ; git push", gitShellOpts) end
---@param opts? object
function M.push(opts)
if not opts then opts = {} end
local shellCmd = opts.pullBefore and "git pull ; git push" or "git push"
if opts.force then shellCmd = shellCmd .. " --force" end
fn.jobstart(shellCmd, {
stdout_buffered = true,
stderr_buffered = true,
detach = true, -- finish even when quitting nvim
on_stdout = function(_, data)
if data[1] == "" and #data == 1 then return end
local output = vim.trim(table.concat(data, "\n"))

-- no need to notify that the pull in `git pull ; git push` yielded no update
if output:find("Current branch .* is up to date") then return end

notify(output)
playSoundMacOS(
"/System/Library/Components/CoreAudio.component/Contents/SharedSupport/SystemSounds/siri/jbl_confirm.caf" -- codespell-ignore
)
end,
on_stderr = function(_, data)
if data[1] == "" and #data == 1 then return end
local output = vim.trim(table.concat(data, "\n"))

-- git often puts non-errors into STDERR, therefore checking here again
-- whether it is actually an error or not
local logLevel = "info"
local sound =
"/System/Library/Components/CoreAudio.component/Contents/SharedSupport/SystemSounds/siri/jbl_confirm.caf" -- codespell-ignore
if output:lower():find("error") then
logLevel = "error"
sound = "/System/Library/Sounds/Basso.aiff"
elseif output:lower():find("warning") then
logLevel = "warn"
sound = "/System/Library/Sounds/Basso.aiff"
end

notify(output, logLevel)
playSoundMacOS(sound)
end,
})
end

--------------------------------------------------------------------------------

Expand Down

0 comments on commit 41a2b88

Please sign in to comment.