@@ -55,6 +55,21 @@ local function postCommitNotif(notifTitle, doStageAllChanges, commitTitle, extra
5555 u .notify (table.concat (lines , " \n " ), " info" , { title = notifTitle })
5656end
5757
58+ --- @return boolean
59+ local function repoHasPrecommitHook ()
60+ local out = vim .system ({ " git" , " config" , " --get" , " core.hooksPath" }):wait ()
61+ local hasHookConfig = out .code == 0
62+ if not hasHookConfig then return false end
63+
64+ local currentHookPath = vim .fs .normalize (vim .trim (out .stdout ))
65+ local gitRoot = vim .fs .root (0 , " .git" )
66+
67+ local isRelative = not vim .startswith (currentHookPath , " /" )
68+ if isRelative then currentHookPath = vim .fs .normalize (gitRoot .. " /" .. currentHookPath ) end
69+ local hookPathExists = vim .uv .fs_stat (currentHookPath ) ~= nil
70+ return hookPathExists
71+ end
72+
5873---- ----------------------------------------------------------------------------
5974
6075--- @param opts ? { pushIfClean ?: boolean , pullBeforePush ?: boolean }
@@ -67,45 +82,68 @@ function M.smartCommit(opts)
6782 local doStageAllChanges = hasNoStagedChanges ()
6883 local cleanAfterCommit = hasNoUnstagedChanges () or doStageAllChanges
6984
70- local prompt = " Commit"
71- if doStageAllChanges then prompt = " Stage all · " .. prompt :lower () end
72- if cleanAfterCommit and opts .pushIfClean then prompt = prompt .. " · push" end
73-
74- local inputMode = doStageAllChanges and " stage-all-and-commit" or " commit"
75-
76- -- check if pre-commit would pass before opening message input
77- local preCommitResult = vim .system ({ " git" , " hook" , " run" , " --ignore-missing" , " pre-commit" })
78- :wait ()
79- if u .nonZeroExit (preCommitResult ) then return end
80-
81- require (" tinygit.commands.commit.msg-input" ).new (inputMode , prompt , function (title , body )
82- -- stage
83- if doStageAllChanges then
84- local result = vim .system ({ " git" , " add" , " --all" }):wait ()
85+ local function startCommit ()
86+ -- params
87+ local input = require (" tinygit.commands.commit.msg-input" )
88+ local prompt = " Commit"
89+ if doStageAllChanges then prompt = " Stage all · " .. prompt :lower () end
90+ if cleanAfterCommit and opts .pushIfClean then prompt = prompt .. " · push" end
91+ local inputMode = doStageAllChanges and " stage-all-and-commit" or " commit"
92+
93+ input .new (inputMode , prompt , function (title , body )
94+ -- stage
95+ if doStageAllChanges then
96+ local result = vim .system ({ " git" , " add" , " --all" }):wait ()
97+ if u .nonZeroExit (result ) then return end
98+ end
99+
100+ -- commit
101+ -- (using `--no-verify`, since we checked the pre-commit earlier already)
102+ local commitArgs = { " git" , " commit" , " --no-verify" , " --message=" .. title }
103+ if body then table.insert (commitArgs , " --message=" .. body ) end
104+ local result = vim .system (commitArgs ):wait ()
85105 if u .nonZeroExit (result ) then return end
86- end
87106
88- -- commit
89- -- (using `--no-verify`, since we checked the pre-commit earlier already)
90- local commitArgs = { " git " , " commit " , " --no-verify " , " --message= " .. title }
91- if body then table.insert ( commitArgs , " --message= " .. body ) end
92- local result = vim . system ( commitArgs ): wait ()
93- if u . nonZeroExit ( result ) then return end
107+ -- notification
108+ local extra = " "
109+ if opts . pushIfClean then
110+ extra = cleanAfterCommit and " Pushing… " or " Not pushing since repo still dirty. "
111+ end
112+ postCommitNotif ( " Smart commit " , doStageAllChanges , title , extra )
94113
95- -- notification
96- local extra = " "
97- if opts .pushIfClean then
98- extra = cleanAfterCommit and " Pushing…" or " Not pushing since repo still dirty."
99- end
100- postCommitNotif (" Smart commit" , doStageAllChanges , title , extra )
114+ -- push
115+ if opts .pushIfClean and cleanAfterCommit then
116+ require (" tinygit.commands.push-pull" ).push ({ pullBefore = opts .pullBeforePush }, true )
117+ end
101118
102- -- push
103- if opts .pushIfClean and cleanAfterCommit then
104- require (" tinygit.commands.push-pull" ).push ({ pullBefore = opts .pullBeforePush }, true )
105- end
119+ require (" tinygit.statusline" ).updateAllComponents ()
120+ end )
121+ end
106122
107- require (" tinygit.statusline" ).updateAllComponents ()
108- end )
123+ if repoHasPrecommitHook () then
124+ u .notify (" Running pre-commit hook…" )
125+
126+ -- open commit window async, in case of slow precommit hook (#44)
127+ vim .system (
128+ { " git" , " hook" , " run" , " pre-commit" },
129+ vim .schedule_wrap (function (hookResult )
130+ -- hide info notification in case of snacks.nvim
131+ if package.loaded [" snacks" ] then require (" snacks" ).notifier .hide () end
132+
133+ -- don't open input if hook fails
134+ if hookResult .code ~= 0 then
135+ local msg = (hookResult .stdout or " " ) .. (hookResult .stderr or " " )
136+ msg = " [Pre-commit hook failed]\n\n " .. msg
137+ u .notify (msg , " error" )
138+ return
139+ end
140+
141+ startCommit ()
142+ end )
143+ )
144+ else
145+ startCommit ()
146+ end
109147end
110148
111149--- @param opts ? { forcePushIfDiverged ?: boolean , stageAllIfNothingStaged ?: boolean }
0 commit comments