-
Notifications
You must be signed in to change notification settings - Fork 180
/
mini-git.txt
426 lines (336 loc) · 18.2 KB
/
mini-git.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
*mini.git* Git integration
*MiniGit*
MIT License Copyright (c) 2024 Evgeni Chasnovski
==============================================================================
Features:
- Automated tracking of Git related data: root path, status, HEAD, etc.
Exposes buffer-local variables for convenient use in statusline.
See |MiniGit.enable()| and |MiniGit.get_buf_data()| for more information.
- |:Git| command for executing any `git` call inside file's repository root with
deeper current instance integration (show output as notification/buffer,
use to edit commit messages, etc.).
- Helper functions to inspect Git history:
- |MiniGit.show_range_history()| shows how certain line range evolved.
- |MiniGit.show_diff_source()| shows file state as it was at diff entry.
- |MiniGit.show_at_cursor()| shows Git related data depending on context.
What it doesn't do:
- Replace fully featured Git client. Rule of thumb: if feature does not rely
on a state of current Neovim (opened buffers, etc.), it is out of scope.
For more functionality, use either |MiniDiff| or fully featured Git client.
Sources with more details:
- |:Git|
- |MiniGit-examples|
- |MiniGit.enable()|
- |MiniGit.get_buf_data()|
# Setup ~
This module needs a setup with `require('mini.git').setup({})` (replace `{}` with
your `config` table). It will create global Lua table `MiniGit` which you can use
for scripting or manually (with `:lua MiniGit.*`).
See |MiniGit.config| for `config` structure and default values.
# Comparisons ~
- 'tpope/vim-fugitive':
- Mostly a dedicated Git client, while this module is not (by design).
- Provides buffer-local Git data only through fixed statusline component,
while this module has richer data in the form of a Lua table.
- Both provide |:Git| command with 'vim-fugitive' treating some cases
extra specially (like `:Git blame`, etc.), while this module mostly
treats all cases the same. See |MiniGit-examples| for how they can be
manually customized.
Also this module provides slightly different (usually richer)
completion suggestions.
- 'NeogitOrg/neogit':
- Similar to 'tpope/vim-fugitive', but without `:Git` command.
- 'lewis6991/gitsigns.nvim':
- Provides buffer-local Git data with emphasis on granular diff status,
while this module is more oriented towards repository and file level
data (root, HEAD, file status, etc.). Use |MiniDiff| for diff tracking.
# Disabling ~
To prevent buffer(s) from being tracked, set `vim.g.minigit_disable` (globally)
or `vim.b.minigit_disable` (for a buffer) to `true`. Considering high number of
different scenarios and customization intentions, writing exact rules for
disabling module's functionality is left to user.
See |mini.nvim-disabling-recipes| for common recipes.
------------------------------------------------------------------------------
*MiniGit-examples*
# Statusline component ~
Tracked buffer data can be used in statusline via `vim.b.minigit_summary_string`
buffer-local variable. It is expected to be used as is. To show another info,
tweak buffer-local variable directly inside `MiniGitUpdated` `User` event: >lua
-- Use only HEAD name as summary string
local format_summary = function(data)
-- Utilize buffer-local table summary
local summary = vim.b[data.buf].minigit_summary
vim.b[data.buf].minigit_summary_string = summary.head_name or ''
end
local au_opts = { pattern = 'MiniGitUpdated', callback = format_summary }
vim.api.nvim_create_autocmd('User', au_opts)
<
# Tweaking command output ~
Buffer output of |:Git| command can be tweaked inside autocommand for
`MiniGitCommandSplit` `User` event (see |MiniGit-command-events|).
For example, to make `:vertical Git blame -- %` align blame output with the
current window state, use the following code: >lua
local align_blame = function(au_data)
if au_data.data.git_subcommand ~= 'blame' then return end
-- Align blame output with source
local win_src = au_data.data.win_source
vim.wo.wrap = false
vim.fn.winrestview({ topline = vim.fn.line('w0', win_src) })
vim.api.nvim_win_set_cursor(0, { vim.fn.line('.', win_src), 0 })
-- Bind both windows so that they scroll together
vim.wo[win_src].scrollbind, vim.wo.scrollbind = true, true
end
local au_opts = { pattern = 'MiniGitCommandSplit', callback = align_blame }
vim.api.nvim_create_autocmd('User', au_opts)
<
# History navigation ~
Function |MiniGit.show_at_cursor()| is specifically exported to make Git
history navigation easier. Here are some different ways it can be used:
- Call inside buffer for already committed file to show the evolution of
the current line (or visually selected range) through history.
It is essentially a `:Git log HEAD` with proper `-L` flag.
This also works inside output of |MiniGit.show_diff_source()|.
- Call with cursor on commit hash to inspect that commit in full.
This is usually helpful in the output of `:Git log`.
- Call with cursor inside diff entry to inspect its file in the state how it
was at certain commit. By default it shows state after commit, unless cursor
is on the "deleted" line (i.e. line starting with "-") in which case
state before commit is shown.
This workflow can be made more interactive when used with mapping, like this: >lua
local rhs = '<Cmd>lua MiniGit.show_at_cursor()<CR>'
vim.keymap.set({ 'n', 'x' }, '<Leader>gs', rhs, { desc = 'Show at cursor' })
<
------------------------------------------------------------------------------
*MiniGit-command*
*:Git*
The `:Git` user command runs `git` CLI call with extra integration for currently
opened Neovim process:
- Command is executed inside repository root of the currently active file
(or |current-directory| if file is not tracked by this module).
- Command output is shown either in dedicated buffer in window split or as
notification via |vim.notify()|. Which method is used depends on whether
particular Git subcommand is supposed to show data for user to inspect
(like `log`, `status`, etc.) or not (like `commit`, `push`, etc.). This is
determined automatically based on the data Git itself provides.
Split window is made current after command execution.
Use split-related |command-modifiers| (|:vertical|, |:horizontal|, or |:tab|)
to force output in a particular type of split. Default split direction is
controlled by `command.split` in |MiniGit.config|.
Use |:silent| command modifier to not show any output.
Errors and warnings are always shown as notifications.
See |MiniGit-examples| for the example of tweaking command output.
- Editor for tasks that require interactive user input (like `:Git commit` or
`:Git rebase --interactive`) is opened inside current session in a separate
split. Make modifications as in regular buffer, |:write| changes followed by
|:close| / |:quit| for Git CLI command to resume.
Examples of usage:
- `:Git log --oneline` - show compact log of current repository.
- `:vert Git blame -- %` - show latest commits per line in vertical split.
- `:Git help rebase` - show help page for `rebase` subcommand.
- `:Git -C <cwd> status` - execute `git status` inside |current-directory|.
There is also a context aware completion which can be invoked with `<Tab>`:
- If completed word starts with "-", options for the current Git subcommand
are shown. Like completion at `:Git log -` will suggest `-L`, `--oneline`, etc.
- If there is an explicit " -- " to the cursor's left, incremental path
suggestions will be shown.
- If there is no recognized Git subcommand yet, show list of subcommands.
Otherwise for some common subcommands list of its targets will be suggested:
like for `:Git branch` it will be list of branches, etc.
Notes:
- Paths are always treated as relative to command's execution directory
(file's repository root or |current-directory| if absent).
- Don't use quotes for entries containing space, escape it with `\` directly.
Like `:Git commit -m Hello\ world` and not `:Git commit -m 'Hello world'`
(which treats `'Hello` and `world'` as separate arguments).
*MiniGit-command-events*
There are several `User` events triggered during command execution:
- `MiniGitCommandDone` - after command is done executing. For Lua callbacks it
provides a special `data` table with the following fields:
- <cmd_input> `(table)` - structured data about executed command.
Has same structure as Lua function input in |nvim_create_user_command()|.
- <cwd> `(string)` - directory path inside which Git command was executed.
- `<exit_code>` `(number)` - exit code of CLI process.
- `<git_command>` `(table)` - array with arguments of full executed command.
- `<git_subcommand>` `(string)` - detected Git subcommand (like "log", etc.).
- `<stderr>` `(string)` - `stderr` process output.
- `<stdout>` `(string)` - `stdout` process output.
- `MiniGitCommandSplit` - after command showed its output in a split. Triggered
after `MiniGitCommandDone` and provides similar `data` table with extra fields:
- `<win_source>` `(number)` - window identifier of "source" window (current at
the moment before command execution).
- `<win_stdout>` `(number)` - window identifier of command output.
------------------------------------------------------------------------------
*MiniGit.setup()*
`MiniGit.setup`({config})
Module setup
Besides general side effects (see |mini.nvim|), it also:
- Sets up auto enabling in every normal buffer for an actual file on disk.
- Creates |:Git| command.
Parameters ~
{config} `(table|nil)` Module config table. See |MiniGit.config|.
Usage ~
>lua
require('mini.git').setup() -- use default config
-- OR
require('mini.git').setup({}) -- replace {} with your config table
<
------------------------------------------------------------------------------
*MiniGit.config*
`MiniGit.config`
Module config
Default values:
>lua
MiniGit.config = {
-- General CLI execution
job = {
-- Path to Git executable
git_executable = 'git',
-- Timeout (in ms) for each job before force quit
timeout = 30000,
},
-- Options for `:Git` command
command = {
-- Default split direction
split = 'auto',
},
}
<
# Job ~
`config.job` contains options for customizing CLI executions.
`job.git_executable` defines a full path to Git executable. Default: "git".
`job.timeout` is a duration (in ms) from job start until it is forced to stop.
Default: 30000.
# Command ~
`config.command` contains options for customizing |:Git| command.
`command.split` defines default split direction for |:Git| command output. Can be
one of "horizontal", "vertical", "tab", or "auto". Value "auto" uses |:vertical|
if only 'mini.git' buffers are shown in the tabpage and |:tab| otherwise.
Default: "auto".
------------------------------------------------------------------------------
*MiniGit.show_at_cursor()*
`MiniGit.show_at_cursor`({opts})
Show Git related data at cursor
- If there is a commit-like |<cword>|, show it in split.
- If possible, show diff source via |MiniGit.show_diff_source()|.
- If possible, show range history via |MiniGit.show_range_history()|.
- Otherwise throw an error.
Parameters ~
{opts} `(table|nil)` Options. Possible values:
- <split> `(string)` - split direction. One of "horizontal", "vertical",
"tab", or "auto" (default). Value "auto" uses |:vertical| if only 'mini.git'
buffers are shown in the tabpage and |:tab| otherwise.
- Fields appropriate for forwarding to other functions.
------------------------------------------------------------------------------
*MiniGit.show_diff_source()*
`MiniGit.show_diff_source`({opts})
Show diff source
When buffer contains text formatted as unified patch (like after
`:Git log --patch`, `:Git diff`, or |MiniGit.show_range_history()|),
show state of the file at the particular state. Target commit/state, path,
and line number are deduced from cursor position.
Notes:
- Needs |current-directory| to be the Git root for relative paths to work.
- Needs cursor to be inside hunk lines or on "---" / "+++" lines with paths.
- Only basic forms of `:Git diff` output is supported: `:Git diff`,
`:Git diff --cached`, and `:Git diff <commit>`.
Parameters ~
{opts} `(table|nil)` Options. Possible values:
- <split> `(string)` - split direction. One of "horizontal", "vertical",
"tab", or "auto" (default). Value "auto" uses |:vertical| if only 'mini.git'
buffers are shown in the tabpage and |:tab| otherwise.
- <target> `(string)` - which file state to show. One of "before", "after",
"both" (both states in vertical split), "auto" (default). Value "auto"
shows "before" state if cursor line starts with "-", otherwise - "after".
------------------------------------------------------------------------------
*MiniGit.show_range_history()*
`MiniGit.show_range_history`({opts})
Show range history
Compute and show in split data about how particular line range in current
buffer evolved through Git history. Essentially a `git log` with `-L` flag.
Notes:
- Works well with |MiniGit.diff_foldexpr()|.
- Does not work if there are uncommited changes, as there is no easy way to
compute effective range line numbers.
Parameters ~
{opts} `(table|nil)` Options. Possible fields:
- <line_start> `(number)` - range start line.
- <line_end> `(number)` - range end line.
If both <line_start> and <line_end> are not supplied, they default to
current line in Normal mode and visual selection in Visual mode.
- <log_args> `(table)` - array of options to append to `git log` call.
- <split> `(string)` - split direction. One of "horizontal", "vertical",
"tab", or "auto" (default). Value "auto" uses |:vertical| if only 'mini.git'
buffers are shown in the tabpage and |:tab| otherwise.
------------------------------------------------------------------------------
*MiniGit.diff_foldexpr()*
`MiniGit.diff_foldexpr`({lnum})
Fold expression for Git logs
Folds contents of hunks, file patches, and log entries in unified diff.
Useful for filetypes "diff" (like after `:Git diff`) and "git" (like after
`:Git log --patch` or `:Git show` for commit).
Works well with |MiniGit.show_range_history()|.
General idea of folding levels (use |zr| and |zm| to adjust interactively):
- At level 0 there is one line per whole patch or log entry.
- At level 1 there is one line per patched file.
- At level 2 there is one line per hunk.
- At level 3 there is no folds.
For automated setup, set the following for "git" and "diff" filetypes (either
inside |FileType| autocommand or |ftplugin|): >vim
setlocal foldmethod=expr foldexpr=v:lua.MiniGit.diff_foldexpr()
<
Parameters ~
{lnum} `(number|nil)` Line number for which fold level is computed.
Default: |v:lnum|.
Return ~
`(number|string)` Line fold level. See |fold-expr|.
------------------------------------------------------------------------------
*MiniGit.enable()*
`MiniGit.enable`({buf_id})
Enable Git tracking in a file buffer
Tracking is done by reacting to changes in file content or file's repository
in the form of keeping buffer data up to date. The data can be used via:
- |MiniGit.get_buf_data()|. See its help for a list of actually tracked data.
- `vim.b.minigit_summary` (table) and `vim.b.minigit_summary_string` (string)
buffer-local variables which are more suitable for statusline.
`vim.b.minigit_summary_string` contains information about HEAD, file status,
and in progress action (see |MiniGit.get_buf_data()| for more details).
See |MiniGit-examples| for how it can be tweaked and used in statusline.
Note: this function is called automatically for all new normal buffers.
Use it explicitly if buffer was disabled.
`User` event `MiniGitUpdated` is triggered whenever tracking data is updated.
Note that not all data listed in |MiniGit.get_buf_data()| can be present (yet)
at the point of event being triggered.
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
------------------------------------------------------------------------------
*MiniGit.disable()*
`MiniGit.disable`({buf_id})
Disable Git tracking in buffer
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
------------------------------------------------------------------------------
*MiniGit.toggle()*
`MiniGit.toggle`({buf_id})
Toggle Git tracking in buffer
Enable if disabled, disable if enabled.
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
------------------------------------------------------------------------------
*MiniGit.get_buf_data()*
`MiniGit.get_buf_data`({buf_id})
Get buffer data
Parameters ~
{buf_id} `(number)` Target buffer identifier. Default: 0 for current buffer.
Return ~
`(table|nil)` Table with buffer Git data or `nil` if buffer is not enabled.
If the file is not part of Git repo, table will be empty.
Table has the following fields:
- <repo> `(string)` - full path to '.git' directory.
- <root> `(string)` - full path to worktree root.
- <head> `(string)` - full commit of current HEAD.
- <head_name> `(string)` - short name of current HEAD (like "master").
For detached HEAD it is "HEAD".
- <status> `(string)` - two character file status as returned by `git status`.
- <in_progress> `(string)` - name of action(s) currently in progress
(bisect, merge, etc.). Can be a combination of those separated by ",".
vim:tw=78:ts=8:noet:ft=help:norl: