mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-10-05 05:26:53 -04:00
Compare commits
88
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7efe433f1c | ||
|
|
6f535d71c9 | ||
|
|
ec3a28df43 | ||
|
|
5b933762c2 | ||
|
|
3eed997161 | ||
|
|
526d8a8a76 | ||
|
|
e1fc90615d | ||
|
|
460a166e16 | ||
|
|
2e66d87b94 | ||
|
|
820f7b9404 | ||
|
|
2e0d0a92ee | ||
|
|
ed857d1e07 | ||
|
|
b30ec538fb | ||
|
|
ee11046d35 | ||
|
|
25f8b0337e | ||
|
|
63ddc52a6b | ||
|
|
b07c4fc001 | ||
|
|
2304ffc804 | ||
|
|
401610c0ef | ||
|
|
64b2685c2d | ||
|
|
7d4bfb6621 | ||
|
|
a2778f01c6 | ||
|
|
d161afe37f | ||
|
|
3dd96a8010 | ||
|
|
681a9bf20d | ||
|
|
0606b7a43b | ||
|
|
33da56eeb4 | ||
|
|
46b93bba0e | ||
|
|
d2d50aedd0 | ||
|
|
ea5fb6a364 | ||
|
|
a82134f41c | ||
|
|
00b03079d8 | ||
|
|
9592686629 | ||
|
|
114ad52ff6 | ||
|
|
9514284f8e | ||
|
|
ece14844bb | ||
|
|
9910a7c308 | ||
|
|
910a61dc46 | ||
|
|
e5dd4d3110 | ||
|
|
595c7ee73e | ||
|
|
051af6a066 | ||
|
|
e2d7e461a8 | ||
|
|
7c66ca83c1 | ||
|
|
007b406b14 | ||
|
|
2b30085dba | ||
|
|
5c95d23169 | ||
|
|
e156e090cc | ||
|
|
0accb07dcc | ||
|
|
3cac14c76e | ||
|
|
1636931c2b | ||
|
|
6e027f42da | ||
|
|
5dacbb6293 | ||
|
|
8d68ab41b6 | ||
|
|
af97bf484c | ||
|
|
a57310df24 | ||
|
|
9d68b287db | ||
|
|
a5c72d056d | ||
|
|
8a86de85c8 | ||
|
|
dd31f8ecea | ||
|
|
5a7b2ab6d0 | ||
|
|
68a9d7fd77 | ||
|
|
19cbafcdfc | ||
|
|
f2c85c5b19 | ||
|
|
0faa41e6f8 | ||
|
|
2e32e55957 | ||
|
|
037cd99138 | ||
|
|
6388885699 | ||
|
|
d3e9bc2185 | ||
|
|
ea4587a3b8 | ||
|
|
2da300f2fb | ||
|
|
7848958326 | ||
|
|
2cba98e3fe | ||
|
|
f8c9ce33c2 | ||
|
|
71753770ad | ||
|
|
820b1e811d | ||
|
|
4a33fede7b | ||
|
|
db12853bbe | ||
|
|
711674f6cd | ||
|
|
fc91ef6a59 | ||
|
|
43251e7275 | ||
|
|
f081358943 | ||
|
|
0c6ab4b43e | ||
|
|
1b2fb34ffd | ||
|
|
0e5a4c7a36 | ||
|
|
47b91f1ef5 | ||
|
|
e2db6a1732 | ||
|
|
509e3efa70 | ||
|
|
8edad826ca |
Vendored
+2
-2
@@ -5,8 +5,8 @@
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Generate cheatsheet",
|
||||
"type": "process",
|
||||
"command": "go run scripts/cheatsheet/main.go ",
|
||||
"type": "shell",
|
||||
"command": "go run scripts/cheatsheet/main.go generate",
|
||||
"problemMatcher": [],
|
||||
},
|
||||
{
|
||||
|
||||
+9
-3
@@ -65,6 +65,8 @@ gui:
|
||||
splitDiff: 'auto' # one of 'auto' | 'always'
|
||||
skipRewordInEditorWarning: false # for skipping the confirmation before launching the reword editor
|
||||
border: 'single' # one of 'single' | 'double' | 'rounded' | 'hidden'
|
||||
# For marking recent commits which changed the selected file
|
||||
experimentalMarkCommitsWhichChangedFile: false
|
||||
git:
|
||||
paging:
|
||||
colorArg: always
|
||||
@@ -88,6 +90,9 @@ git:
|
||||
# displays the whole git graph by default in the commits panel (equivalent to passing the `--all` argument to `git log`)
|
||||
showWholeGraph: false
|
||||
skipHookPrefix: WIP
|
||||
# The main branches. We colour commits green if they belong to one of these branches,
|
||||
# so that you can easily see which commits are unique to your branch (coloured in yellow)
|
||||
mainBranches: [master, main]
|
||||
autoFetch: true
|
||||
autoRefresh: true
|
||||
branchLogCmd: 'git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --'
|
||||
@@ -285,9 +290,9 @@ os:
|
||||
editPreset: 'vscode'
|
||||
```
|
||||
|
||||
Supported presets are `vim`, `emacs`, `nano`, `vscode`, `sublime`, `bbedit`, and
|
||||
`xcode`. In many cases lazygit will be able to guess the right preset from your
|
||||
$(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
|
||||
Supported presets are `vim`, `nvim`, `emacs`, `nano`, `vscode`, `sublime`, `bbedit`,
|
||||
`kakoune` and `xcode`. In many cases lazygit will be able to guess the right preset
|
||||
from your $(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
|
||||
|
||||
If for some reason you are not happy with the default commands from a preset, or
|
||||
there simply is no preset for your editor, you can customize the commands by
|
||||
@@ -344,6 +349,7 @@ The available attributes are:
|
||||
- default
|
||||
- reverse # useful for high-contrast
|
||||
- underline
|
||||
- strikethrough
|
||||
|
||||
## Highlighting the selected line
|
||||
|
||||
|
||||
@@ -2,28 +2,30 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
|
||||
# Lazygit Keybindings
|
||||
|
||||
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
## Global Keybindings
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+r</kbd>: switch to a recent repo
|
||||
<kbd>pgup</kbd>: scroll up main panel (fn+up/shift+k)
|
||||
<kbd>pgdown</kbd>: scroll down main panel (fn+down/shift+j)
|
||||
<kbd>m</kbd>: view merge/rebase options
|
||||
<kbd>ctrl+p</kbd>: view custom patch options
|
||||
<kbd>R</kbd>: refresh
|
||||
<kbd>?</kbd>: open menu
|
||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: prev screen mode
|
||||
<kbd>ctrl+s</kbd>: view filter-by-path options
|
||||
<kbd>W</kbd>: open diff menu
|
||||
<kbd>ctrl+e</kbd>: open diff menu
|
||||
<kbd><c-r></kbd>: switch to a recent repo
|
||||
<kbd><pgup></kbd>: scroll up main panel (fn+up/shift+k)
|
||||
<kbd><pgdown></kbd>: scroll down main panel (fn+down/shift+j)
|
||||
<kbd>@</kbd>: open command log menu
|
||||
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
||||
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
||||
<kbd>:</kbd>: execute custom command
|
||||
<kbd><c-p></kbd>: view custom patch options
|
||||
<kbd>m</kbd>: view merge/rebase options
|
||||
<kbd>R</kbd>: refresh
|
||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: prev screen mode
|
||||
<kbd>?</kbd>: open menu
|
||||
<kbd><c-s></kbd>: view filter-by-path options
|
||||
<kbd>W</kbd>: open diff menu
|
||||
<kbd><c-e></kbd>: open diff menu
|
||||
<kbd><c-w></kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||
<kbd>z</kbd>: undo (via reflog) (experimental)
|
||||
<kbd>ctrl+z</kbd>: redo (via reflog) (experimental)
|
||||
<kbd><c-z></kbd>: redo (via reflog) (experimental)
|
||||
<kbd>P</kbd>: push
|
||||
<kbd>p</kbd>: pull
|
||||
</pre>
|
||||
@@ -33,9 +35,9 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<pre>
|
||||
<kbd>,</kbd>: previous page
|
||||
<kbd>.</kbd>: next page
|
||||
<kbd><</kbd>: scroll to top
|
||||
<kbd><</kbd>: scroll to top
|
||||
<kbd>/</kbd>: start search
|
||||
<kbd>></kbd>: scroll to bottom
|
||||
<kbd>></kbd>: scroll to bottom
|
||||
<kbd>H</kbd>: scroll left
|
||||
<kbd>L</kbd>: scroll right
|
||||
<kbd>]</kbd>: next tab
|
||||
@@ -45,22 +47,29 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Commit Files
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy the committed file name to the clipboard
|
||||
<kbd><c-o></kbd>: copy the committed file name to the clipboard
|
||||
<kbd>c</kbd>: checkout file
|
||||
<kbd>d</kbd>: discard this commit's changes to this file
|
||||
<kbd>o</kbd>: open file
|
||||
<kbd>e</kbd>: edit file
|
||||
<kbd>space</kbd>: toggle file included in patch
|
||||
<kbd><space></kbd>: toggle file included in patch
|
||||
<kbd>a</kbd>: toggle all files included in patch
|
||||
<kbd>enter</kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
<kbd><enter></kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
<kbd>`</kbd>: toggle file tree view
|
||||
</pre>
|
||||
|
||||
## Commit Summary
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: confirm
|
||||
<kbd><esc></kbd>: close
|
||||
</pre>
|
||||
|
||||
## Commits
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy commit SHA to clipboard
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd><c-o></kbd>: copy commit SHA to clipboard
|
||||
<kbd><c-r></kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd>b</kbd>: view bisect options
|
||||
<kbd>s</kbd>: squash down
|
||||
<kbd>f</kbd>: fixup commit
|
||||
@@ -71,31 +80,38 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>p</kbd>: pick commit (when mid-rebase)
|
||||
<kbd>F</kbd>: create fixup commit for this commit
|
||||
<kbd>S</kbd>: squash all 'fixup!' commits above selected commit (autosquash)
|
||||
<kbd>ctrl+j</kbd>: move commit down one
|
||||
<kbd>ctrl+k</kbd>: move commit up one
|
||||
<kbd><c-j></kbd>: move commit down one
|
||||
<kbd><c-k></kbd>: move commit up one
|
||||
<kbd>v</kbd>: paste commits (cherry-pick)
|
||||
<kbd>A</kbd>: amend commit with staged changes
|
||||
<kbd>a</kbd>: reset commit author
|
||||
<kbd>t</kbd>: revert commit
|
||||
<kbd>T</kbd>: tag commit
|
||||
<kbd>ctrl+l</kbd>: open log menu
|
||||
<kbd>space</kbd>: checkout commit
|
||||
<kbd><c-l></kbd>: open log menu
|
||||
<kbd><space></kbd>: checkout commit
|
||||
<kbd>y</kbd>: copy commit attribute
|
||||
<kbd>o</kbd>: open commit in browser
|
||||
<kbd>n</kbd>: create new branch off of commit
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>c</kbd>: copy commit (cherry-pick)
|
||||
<kbd>C</kbd>: copy commit range (cherry-pick)
|
||||
<kbd>enter</kbd>: view selected item's files
|
||||
<kbd><enter></kbd>: view selected item's files
|
||||
</pre>
|
||||
|
||||
## Confirmation Panel
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: confirm
|
||||
<kbd><esc></kbd>: close/cancel
|
||||
</pre>
|
||||
|
||||
## Files
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy the file name to the clipboard
|
||||
<kbd><c-o></kbd>: copy the file name to the clipboard
|
||||
<kbd>d</kbd>: view 'discard changes' options
|
||||
<kbd>space</kbd>: toggle staged
|
||||
<kbd>ctrl+b</kbd>: Filter files (staged/unstaged)
|
||||
<kbd><space></kbd>: toggle staged
|
||||
<kbd><c-b></kbd>: Filter files (staged/unstaged)
|
||||
<kbd>c</kbd>: commit changes
|
||||
<kbd>w</kbd>: commit changes without pre-commit hook
|
||||
<kbd>A</kbd>: amend last commit
|
||||
@@ -107,7 +123,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>s</kbd>: stash all changes
|
||||
<kbd>S</kbd>: view stash options
|
||||
<kbd>a</kbd>: stage/unstage all
|
||||
<kbd>enter</kbd>: stage individual hunks/lines for file, or collapse/expand for directory
|
||||
<kbd><enter></kbd>: stage individual hunks/lines for file, or collapse/expand for directory
|
||||
<kbd>g</kbd>: view upstream reset options
|
||||
<kbd>D</kbd>: view reset options
|
||||
<kbd>`</kbd>: toggle file tree view
|
||||
@@ -118,13 +134,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Local Branches
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy branch name to clipboard
|
||||
<kbd><c-o></kbd>: copy branch name to clipboard
|
||||
<kbd>i</kbd>: show git-flow options
|
||||
<kbd>space</kbd>: checkout
|
||||
<kbd><space></kbd>: checkout
|
||||
<kbd>n</kbd>: new branch
|
||||
<kbd>o</kbd>: create pull request
|
||||
<kbd>O</kbd>: create pull request options
|
||||
<kbd>ctrl+y</kbd>: copy pull request URL to clipboard
|
||||
<kbd><c-y></kbd>: copy pull request URL to clipboard
|
||||
<kbd>c</kbd>: checkout by name
|
||||
<kbd>F</kbd>: force checkout
|
||||
<kbd>d</kbd>: delete branch
|
||||
@@ -135,7 +151,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>R</kbd>: rename branch
|
||||
<kbd>u</kbd>: set/unset upstream
|
||||
<kbd>enter</kbd>: view commits
|
||||
<kbd><enter></kbd>: view commits
|
||||
</pre>
|
||||
|
||||
## Main Panel (Merging)
|
||||
@@ -143,53 +159,53 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<pre>
|
||||
<kbd>e</kbd>: edit file
|
||||
<kbd>o</kbd>: open file
|
||||
<kbd>◀</kbd>: select previous conflict
|
||||
<kbd>▶</kbd>: select next conflict
|
||||
<kbd>▲</kbd>: select previous hunk
|
||||
<kbd>▼</kbd>: select next hunk
|
||||
<kbd><left></kbd>: select previous conflict
|
||||
<kbd><right></kbd>: select next conflict
|
||||
<kbd><up></kbd>: select previous hunk
|
||||
<kbd><down></kbd>: select next hunk
|
||||
<kbd>z</kbd>: undo
|
||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
||||
<kbd>space</kbd>: pick hunk
|
||||
<kbd><space></kbd>: pick hunk
|
||||
<kbd>b</kbd>: pick all hunks
|
||||
<kbd>esc</kbd>: return to files panel
|
||||
<kbd><esc></kbd>: return to files panel
|
||||
</pre>
|
||||
|
||||
## Main Panel (Normal)
|
||||
|
||||
<pre>
|
||||
<kbd>mouse wheel ▼</kbd>: scroll down (fn+up)
|
||||
<kbd>mouse wheel ▲</kbd>: scroll up (fn+down)
|
||||
<kbd>mouse wheel down</kbd>: scroll down (fn+up)
|
||||
<kbd>mouse wheel up</kbd>: scroll up (fn+down)
|
||||
</pre>
|
||||
|
||||
## Main Panel (Patch Building)
|
||||
|
||||
<pre>
|
||||
<kbd>◀</kbd>: select previous hunk
|
||||
<kbd>▶</kbd>: select next hunk
|
||||
<kbd><left></kbd>: select previous hunk
|
||||
<kbd><right></kbd>: select next hunk
|
||||
<kbd>v</kbd>: toggle drag select
|
||||
<kbd>V</kbd>: toggle drag select
|
||||
<kbd>a</kbd>: toggle select hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd><c-o></kbd>: copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: open file
|
||||
<kbd>e</kbd>: edit file
|
||||
<kbd>space</kbd>: add/remove line(s) to patch
|
||||
<kbd>esc</kbd>: exit custom patch builder
|
||||
<kbd><space></kbd>: add/remove line(s) to patch
|
||||
<kbd><esc></kbd>: exit custom patch builder
|
||||
</pre>
|
||||
|
||||
## Main Panel (Staging)
|
||||
|
||||
<pre>
|
||||
<kbd>◀</kbd>: select previous hunk
|
||||
<kbd>▶</kbd>: select next hunk
|
||||
<kbd><left></kbd>: select previous hunk
|
||||
<kbd><right></kbd>: select next hunk
|
||||
<kbd>v</kbd>: toggle drag select
|
||||
<kbd>V</kbd>: toggle drag select
|
||||
<kbd>a</kbd>: toggle select hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd><c-o></kbd>: copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: open file
|
||||
<kbd>e</kbd>: edit file
|
||||
<kbd>esc</kbd>: return to files panel
|
||||
<kbd>tab</kbd>: switch to other panel (staged/unstaged changes)
|
||||
<kbd>space</kbd>: toggle line staged / unstaged
|
||||
<kbd><esc></kbd>: return to files panel
|
||||
<kbd><tab></kbd>: switch to other panel (staged/unstaged changes)
|
||||
<kbd><space></kbd>: toggle line staged / unstaged
|
||||
<kbd>d</kbd>: delete change (git reset)
|
||||
<kbd>E</kbd>: edit hunk
|
||||
<kbd>c</kbd>: commit changes
|
||||
@@ -197,34 +213,41 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>C</kbd>: commit changes using git editor
|
||||
</pre>
|
||||
|
||||
## Menu
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: execute
|
||||
<kbd><esc></kbd>: close
|
||||
</pre>
|
||||
|
||||
## Reflog
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy commit SHA to clipboard
|
||||
<kbd>space</kbd>: checkout commit
|
||||
<kbd><c-o></kbd>: copy commit SHA to clipboard
|
||||
<kbd><space></kbd>: checkout commit
|
||||
<kbd>y</kbd>: copy commit attribute
|
||||
<kbd>o</kbd>: open commit in browser
|
||||
<kbd>n</kbd>: create new branch off of commit
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>c</kbd>: copy commit (cherry-pick)
|
||||
<kbd>C</kbd>: copy commit range (cherry-pick)
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd>enter</kbd>: view commits
|
||||
<kbd><c-r></kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd><enter></kbd>: view commits
|
||||
</pre>
|
||||
|
||||
## Remote Branches
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy branch name to clipboard
|
||||
<kbd>space</kbd>: checkout
|
||||
<kbd><c-o></kbd>: copy branch name to clipboard
|
||||
<kbd><space></kbd>: checkout
|
||||
<kbd>n</kbd>: new branch
|
||||
<kbd>M</kbd>: merge into currently checked out branch
|
||||
<kbd>r</kbd>: rebase checked-out branch onto this branch
|
||||
<kbd>d</kbd>: delete branch
|
||||
<kbd>u</kbd>: set as upstream of checked-out branch
|
||||
<kbd>esc</kbd>: Return to remotes list
|
||||
<kbd><esc></kbd>: Return to remotes list
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>enter</kbd>: view commits
|
||||
<kbd><enter></kbd>: view commits
|
||||
</pre>
|
||||
|
||||
## Remotes
|
||||
@@ -239,44 +262,44 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Stash
|
||||
|
||||
<pre>
|
||||
<kbd>space</kbd>: apply
|
||||
<kbd><space></kbd>: apply
|
||||
<kbd>g</kbd>: pop
|
||||
<kbd>d</kbd>: drop
|
||||
<kbd>n</kbd>: new branch
|
||||
<kbd>r</kbd>: rename stash
|
||||
<kbd>enter</kbd>: view selected item's files
|
||||
<kbd><enter></kbd>: view selected item's files
|
||||
</pre>
|
||||
|
||||
## Status
|
||||
|
||||
<pre>
|
||||
<kbd>e</kbd>: edit config file
|
||||
<kbd>o</kbd>: open config file
|
||||
<kbd>e</kbd>: edit config file
|
||||
<kbd>u</kbd>: check for update
|
||||
<kbd>enter</kbd>: switch to a recent repo
|
||||
<kbd><enter></kbd>: switch to a recent repo
|
||||
<kbd>a</kbd>: show all branch logs
|
||||
</pre>
|
||||
|
||||
## Sub-commits
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy commit SHA to clipboard
|
||||
<kbd>space</kbd>: checkout commit
|
||||
<kbd><c-o></kbd>: copy commit SHA to clipboard
|
||||
<kbd><space></kbd>: checkout commit
|
||||
<kbd>y</kbd>: copy commit attribute
|
||||
<kbd>o</kbd>: open commit in browser
|
||||
<kbd>n</kbd>: create new branch off of commit
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>c</kbd>: copy commit (cherry-pick)
|
||||
<kbd>C</kbd>: copy commit range (cherry-pick)
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd>enter</kbd>: view selected item's files
|
||||
<kbd><c-r></kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd><enter></kbd>: view selected item's files
|
||||
</pre>
|
||||
|
||||
## Submodules
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy submodule name to clipboard
|
||||
<kbd>enter</kbd>: enter submodule
|
||||
<kbd><c-o></kbd>: copy submodule name to clipboard
|
||||
<kbd><enter></kbd>: enter submodule
|
||||
<kbd>d</kbd>: remove submodule
|
||||
<kbd>u</kbd>: update submodule
|
||||
<kbd>n</kbd>: add new submodule
|
||||
@@ -288,10 +311,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Tags
|
||||
|
||||
<pre>
|
||||
<kbd>space</kbd>: checkout
|
||||
<kbd><space></kbd>: checkout
|
||||
<kbd>d</kbd>: delete tag
|
||||
<kbd>P</kbd>: push tag
|
||||
<kbd>n</kbd>: create tag
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>enter</kbd>: view commits
|
||||
<kbd><enter></kbd>: view commits
|
||||
</pre>
|
||||
|
||||
@@ -2,28 +2,30 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
|
||||
# Lazygit キーバインド
|
||||
|
||||
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
## グローバルキーバインド
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+r</kbd>: 最近使用したリポジトリに切り替え
|
||||
<kbd>pgup</kbd>: メインパネルを上にスクロール (fn+up/shift+k)
|
||||
<kbd>pgdown</kbd>: メインパネルを下にスクロール (fn+down/shift+j)
|
||||
<kbd>m</kbd>: view merge/rebase options
|
||||
<kbd>ctrl+p</kbd>: view custom patch options
|
||||
<kbd>R</kbd>: リフレッシュ
|
||||
<kbd>?</kbd>: メニューを開く
|
||||
<kbd>+</kbd>: 次のスクリーンモード (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: 前のスクリーンモード
|
||||
<kbd>ctrl+s</kbd>: view filter-by-path options
|
||||
<kbd>W</kbd>: 差分メニューを開く
|
||||
<kbd>ctrl+e</kbd>: 差分メニューを開く
|
||||
<kbd><c-r></kbd>: 最近使用したリポジトリに切り替え
|
||||
<kbd><pgup></kbd>: メインパネルを上にスクロール (fn+up/shift+k)
|
||||
<kbd><pgdown></kbd>: メインパネルを下にスクロール (fn+down/shift+j)
|
||||
<kbd>@</kbd>: コマンドログメニューを開く
|
||||
<kbd>ctrl+w</kbd>: 空白文字の差分の表示有無を切り替え
|
||||
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
||||
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
||||
<kbd>:</kbd>: カスタムコマンドを実行
|
||||
<kbd><c-p></kbd>: view custom patch options
|
||||
<kbd>m</kbd>: view merge/rebase options
|
||||
<kbd>R</kbd>: リフレッシュ
|
||||
<kbd>+</kbd>: 次のスクリーンモード (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: 前のスクリーンモード
|
||||
<kbd>?</kbd>: メニューを開く
|
||||
<kbd><c-s></kbd>: view filter-by-path options
|
||||
<kbd>W</kbd>: 差分メニューを開く
|
||||
<kbd><c-e></kbd>: 差分メニューを開く
|
||||
<kbd><c-w></kbd>: 空白文字の差分の表示有無を切り替え
|
||||
<kbd>z</kbd>: アンドゥ (via reflog) (experimental)
|
||||
<kbd>ctrl+z</kbd>: リドゥ (via reflog) (experimental)
|
||||
<kbd><c-z></kbd>: リドゥ (via reflog) (experimental)
|
||||
<kbd>P</kbd>: push
|
||||
<kbd>p</kbd>: pull
|
||||
</pre>
|
||||
@@ -33,9 +35,9 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<pre>
|
||||
<kbd>,</kbd>: 前のページ
|
||||
<kbd>.</kbd>: 次のページ
|
||||
<kbd><</kbd>: 最上部までスクロール
|
||||
<kbd><</kbd>: 最上部までスクロール
|
||||
<kbd>/</kbd>: 検索を開始
|
||||
<kbd>></kbd>: 最下部までスクロール
|
||||
<kbd>></kbd>: 最下部までスクロール
|
||||
<kbd>H</kbd>: 左スクロール
|
||||
<kbd>L</kbd>: 右スクロール
|
||||
<kbd>]</kbd>: 次のタブ
|
||||
@@ -45,34 +47,34 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Stash
|
||||
|
||||
<pre>
|
||||
<kbd>space</kbd>: 適用
|
||||
<kbd><space></kbd>: 適用
|
||||
<kbd>g</kbd>: pop
|
||||
<kbd>d</kbd>: drop
|
||||
<kbd>n</kbd>: 新しいブランチを作成
|
||||
<kbd>r</kbd>: Stashを変更
|
||||
<kbd>enter</kbd>: view selected item's files
|
||||
<kbd><enter></kbd>: view selected item's files
|
||||
</pre>
|
||||
|
||||
## Sub-commits
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: コミットのSHAをクリップボードにコピー
|
||||
<kbd>space</kbd>: コミットをチェックアウト
|
||||
<kbd><c-o></kbd>: コミットのSHAをクリップボードにコピー
|
||||
<kbd><space></kbd>: コミットをチェックアウト
|
||||
<kbd>y</kbd>: コミットの情報をコピー
|
||||
<kbd>o</kbd>: ブラウザでコミットを開く
|
||||
<kbd>n</kbd>: コミットにブランチを作成
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
||||
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd>enter</kbd>: view selected item's files
|
||||
<kbd><c-r></kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd><enter></kbd>: view selected item's files
|
||||
</pre>
|
||||
|
||||
## コミット
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: コミットのSHAをクリップボードにコピー
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd><c-o></kbd>: コミットのSHAをクリップボードにコピー
|
||||
<kbd><c-r></kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd>b</kbd>: view bisect options
|
||||
<kbd>s</kbd>: squash down
|
||||
<kbd>f</kbd>: fixup commit
|
||||
@@ -83,43 +85,50 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>p</kbd>: pick commit (when mid-rebase)
|
||||
<kbd>F</kbd>: このコミットに対するfixupコミットを作成
|
||||
<kbd>S</kbd>: squash all 'fixup!' commits above selected commit (autosquash)
|
||||
<kbd>ctrl+j</kbd>: コミットを1つ下に移動
|
||||
<kbd>ctrl+k</kbd>: コミットを1つ上に移動
|
||||
<kbd><c-j></kbd>: コミットを1つ下に移動
|
||||
<kbd><c-k></kbd>: コミットを1つ上に移動
|
||||
<kbd>v</kbd>: コミットを貼り付け (cherry-pick)
|
||||
<kbd>A</kbd>: ステージされた変更でamendコミット
|
||||
<kbd>a</kbd>: reset commit author
|
||||
<kbd>t</kbd>: コミットをrevert
|
||||
<kbd>T</kbd>: タグを作成
|
||||
<kbd>ctrl+l</kbd>: ログメニューを開く
|
||||
<kbd>space</kbd>: コミットをチェックアウト
|
||||
<kbd><c-l></kbd>: ログメニューを開く
|
||||
<kbd><space></kbd>: コミットをチェックアウト
|
||||
<kbd>y</kbd>: コミットの情報をコピー
|
||||
<kbd>o</kbd>: ブラウザでコミットを開く
|
||||
<kbd>n</kbd>: コミットにブランチを作成
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
||||
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
||||
<kbd>enter</kbd>: view selected item's files
|
||||
<kbd><enter></kbd>: view selected item's files
|
||||
</pre>
|
||||
|
||||
## コミットファイル
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: コミットされたファイル名をクリップボードにコピー
|
||||
<kbd><c-o></kbd>: コミットされたファイル名をクリップボードにコピー
|
||||
<kbd>c</kbd>: checkout file
|
||||
<kbd>d</kbd>: discard this commit's changes to this file
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
<kbd>e</kbd>: ファイルを編集
|
||||
<kbd>space</kbd>: toggle file included in patch
|
||||
<kbd><space></kbd>: toggle file included in patch
|
||||
<kbd>a</kbd>: toggle all files included in patch
|
||||
<kbd>enter</kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
<kbd><enter></kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
<kbd>`</kbd>: ファイルツリーの表示を切り替え
|
||||
</pre>
|
||||
|
||||
## コミットメッセージ
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: 確認
|
||||
<kbd><esc></kbd>: 閉じる
|
||||
</pre>
|
||||
|
||||
## サブモジュール
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: サブモジュール名をクリップボードにコピー
|
||||
<kbd>enter</kbd>: サブモジュールを開く
|
||||
<kbd><c-o></kbd>: サブモジュール名をクリップボードにコピー
|
||||
<kbd><enter></kbd>: サブモジュールを開く
|
||||
<kbd>d</kbd>: サブモジュールを削除
|
||||
<kbd>u</kbd>: サブモジュールを更新
|
||||
<kbd>n</kbd>: サブモジュールを新規追加
|
||||
@@ -131,31 +140,31 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## ステータス
|
||||
|
||||
<pre>
|
||||
<kbd>e</kbd>: 設定ファイルを編集
|
||||
<kbd>o</kbd>: 設定ファイルを開く
|
||||
<kbd>e</kbd>: 設定ファイルを編集
|
||||
<kbd>u</kbd>: 更新を確認
|
||||
<kbd>enter</kbd>: 最近使用したリポジトリに切り替え
|
||||
<kbd><enter></kbd>: 最近使用したリポジトリに切り替え
|
||||
<kbd>a</kbd>: すべてのブランチログを表示
|
||||
</pre>
|
||||
|
||||
## タグ
|
||||
|
||||
<pre>
|
||||
<kbd>space</kbd>: チェックアウト
|
||||
<kbd><space></kbd>: チェックアウト
|
||||
<kbd>d</kbd>: タグを削除
|
||||
<kbd>P</kbd>: タグをpush
|
||||
<kbd>n</kbd>: タグを作成
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>enter</kbd>: コミットを閲覧
|
||||
<kbd><enter></kbd>: コミットを閲覧
|
||||
</pre>
|
||||
|
||||
## ファイル
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: ファイル名をクリップボードにコピー
|
||||
<kbd><c-o></kbd>: ファイル名をクリップボードにコピー
|
||||
<kbd>d</kbd>: view 'discard changes' options
|
||||
<kbd>space</kbd>: ステージ/アンステージ
|
||||
<kbd>ctrl+b</kbd>: ファイルをフィルタ (ステージ/アンステージ)
|
||||
<kbd><space></kbd>: ステージ/アンステージ
|
||||
<kbd><c-b></kbd>: ファイルをフィルタ (ステージ/アンステージ)
|
||||
<kbd>c</kbd>: 変更をコミット
|
||||
<kbd>w</kbd>: pre-commitフックを実行せずに変更をコミット
|
||||
<kbd>A</kbd>: 最新のコミットにamend
|
||||
@@ -167,7 +176,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>s</kbd>: 変更をstash
|
||||
<kbd>S</kbd>: view stash options
|
||||
<kbd>a</kbd>: すべての変更をステージ/アンステージ
|
||||
<kbd>enter</kbd>: stage individual hunks/lines for file, or collapse/expand for directory
|
||||
<kbd><enter></kbd>: stage individual hunks/lines for file, or collapse/expand for directory
|
||||
<kbd>g</kbd>: view upstream reset options
|
||||
<kbd>D</kbd>: view reset options
|
||||
<kbd>`</kbd>: ファイルツリーの表示を切り替え
|
||||
@@ -178,13 +187,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## ブランチ
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: ブランチ名をクリップボードにコピー
|
||||
<kbd><c-o></kbd>: ブランチ名をクリップボードにコピー
|
||||
<kbd>i</kbd>: show git-flow options
|
||||
<kbd>space</kbd>: チェックアウト
|
||||
<kbd><space></kbd>: チェックアウト
|
||||
<kbd>n</kbd>: 新しいブランチを作成
|
||||
<kbd>o</kbd>: Pull Requestを作成
|
||||
<kbd>O</kbd>: create pull request options
|
||||
<kbd>ctrl+y</kbd>: Pull RequestのURLをクリップボードにコピー
|
||||
<kbd><c-y></kbd>: Pull RequestのURLをクリップボードにコピー
|
||||
<kbd>c</kbd>: checkout by name
|
||||
<kbd>F</kbd>: force checkout
|
||||
<kbd>d</kbd>: ブランチを削除
|
||||
@@ -195,7 +204,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>R</kbd>: ブランチ名を変更
|
||||
<kbd>u</kbd>: set/unset upstream
|
||||
<kbd>enter</kbd>: コミットを閲覧
|
||||
<kbd><enter></kbd>: コミットを閲覧
|
||||
</pre>
|
||||
|
||||
## メインパネル (Merging)
|
||||
@@ -203,53 +212,53 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<pre>
|
||||
<kbd>e</kbd>: ファイルを編集
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
<kbd>◀</kbd>: 前のコンフリクトを選択
|
||||
<kbd>▶</kbd>: 次のコンフリクトを選択
|
||||
<kbd>▲</kbd>: 前のhunkを選択
|
||||
<kbd>▼</kbd>: 次のhunkを選択
|
||||
<kbd><left></kbd>: 前のコンフリクトを選択
|
||||
<kbd><right></kbd>: 次のコンフリクトを選択
|
||||
<kbd><up></kbd>: 前のhunkを選択
|
||||
<kbd><down></kbd>: 次のhunkを選択
|
||||
<kbd>z</kbd>: アンドゥ
|
||||
<kbd>M</kbd>: git mergetoolを開く
|
||||
<kbd>space</kbd>: pick hunk
|
||||
<kbd><space></kbd>: pick hunk
|
||||
<kbd>b</kbd>: pick all hunks
|
||||
<kbd>esc</kbd>: ファイル一覧に戻る
|
||||
<kbd><esc></kbd>: ファイル一覧に戻る
|
||||
</pre>
|
||||
|
||||
## メインパネル (Normal)
|
||||
|
||||
<pre>
|
||||
<kbd>mouse wheel ▼</kbd>: 下にスクロール (fn+up)
|
||||
<kbd>mouse wheel ▲</kbd>: 上にスクロール (fn+down)
|
||||
<kbd>mouse wheel down</kbd>: 下にスクロール (fn+up)
|
||||
<kbd>mouse wheel up</kbd>: 上にスクロール (fn+down)
|
||||
</pre>
|
||||
|
||||
## メインパネル (Patch Building)
|
||||
|
||||
<pre>
|
||||
<kbd>◀</kbd>: 前のhunkを選択
|
||||
<kbd>▶</kbd>: 次のhunkを選択
|
||||
<kbd><left></kbd>: 前のhunkを選択
|
||||
<kbd><right></kbd>: 次のhunkを選択
|
||||
<kbd>v</kbd>: 範囲選択を切り替え
|
||||
<kbd>V</kbd>: 範囲選択を切り替え
|
||||
<kbd>a</kbd>: hunk選択を切り替え
|
||||
<kbd>ctrl+o</kbd>: 選択されたテキストをクリップボードにコピー
|
||||
<kbd><c-o></kbd>: 選択されたテキストをクリップボードにコピー
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
<kbd>e</kbd>: ファイルを編集
|
||||
<kbd>space</kbd>: 行をパッチに追加/削除
|
||||
<kbd>esc</kbd>: exit custom patch builder
|
||||
<kbd><space></kbd>: 行をパッチに追加/削除
|
||||
<kbd><esc></kbd>: exit custom patch builder
|
||||
</pre>
|
||||
|
||||
## メインパネル (Staging)
|
||||
|
||||
<pre>
|
||||
<kbd>◀</kbd>: 前のhunkを選択
|
||||
<kbd>▶</kbd>: 次のhunkを選択
|
||||
<kbd><left></kbd>: 前のhunkを選択
|
||||
<kbd><right></kbd>: 次のhunkを選択
|
||||
<kbd>v</kbd>: 範囲選択を切り替え
|
||||
<kbd>V</kbd>: 範囲選択を切り替え
|
||||
<kbd>a</kbd>: hunk選択を切り替え
|
||||
<kbd>ctrl+o</kbd>: 選択されたテキストをクリップボードにコピー
|
||||
<kbd><c-o></kbd>: 選択されたテキストをクリップボードにコピー
|
||||
<kbd>o</kbd>: ファイルを開く
|
||||
<kbd>e</kbd>: ファイルを編集
|
||||
<kbd>esc</kbd>: ファイル一覧に戻る
|
||||
<kbd>tab</kbd>: パネルを切り替え
|
||||
<kbd>space</kbd>: 選択行をステージ/アンステージ
|
||||
<kbd><esc></kbd>: ファイル一覧に戻る
|
||||
<kbd><tab></kbd>: パネルを切り替え
|
||||
<kbd><space></kbd>: 選択行をステージ/アンステージ
|
||||
<kbd>d</kbd>: 変更を削除 (git reset)
|
||||
<kbd>E</kbd>: edit hunk
|
||||
<kbd>c</kbd>: 変更をコミット
|
||||
@@ -257,6 +266,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>C</kbd>: gitエディタを使用して変更をコミット
|
||||
</pre>
|
||||
|
||||
## メニュー
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: 実行
|
||||
<kbd><esc></kbd>: 閉じる
|
||||
</pre>
|
||||
|
||||
## リモート
|
||||
|
||||
<pre>
|
||||
@@ -269,29 +285,36 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## リモートブランチ
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: ブランチ名をクリップボードにコピー
|
||||
<kbd>space</kbd>: チェックアウト
|
||||
<kbd><c-o></kbd>: ブランチ名をクリップボードにコピー
|
||||
<kbd><space></kbd>: チェックアウト
|
||||
<kbd>n</kbd>: 新しいブランチを作成
|
||||
<kbd>M</kbd>: 現在のブランチにマージ
|
||||
<kbd>r</kbd>: rebase checked-out branch onto this branch
|
||||
<kbd>d</kbd>: ブランチを削除
|
||||
<kbd>u</kbd>: set as upstream of checked-out branch
|
||||
<kbd>esc</kbd>: リモート一覧に戻る
|
||||
<kbd><esc></kbd>: リモート一覧に戻る
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>enter</kbd>: コミットを閲覧
|
||||
<kbd><enter></kbd>: コミットを閲覧
|
||||
</pre>
|
||||
|
||||
## 参照ログ
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: コミットのSHAをクリップボードにコピー
|
||||
<kbd>space</kbd>: コミットをチェックアウト
|
||||
<kbd><c-o></kbd>: コミットのSHAをクリップボードにコピー
|
||||
<kbd><space></kbd>: コミットをチェックアウト
|
||||
<kbd>y</kbd>: コミットの情報をコピー
|
||||
<kbd>o</kbd>: ブラウザでコミットを開く
|
||||
<kbd>n</kbd>: コミットにブランチを作成
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>c</kbd>: コミットをコピー (cherry-pick)
|
||||
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd>enter</kbd>: コミットを閲覧
|
||||
<kbd><c-r></kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd><enter></kbd>: コミットを閲覧
|
||||
</pre>
|
||||
|
||||
## 確認パネル
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: 確認
|
||||
<kbd><esc></kbd>: 閉じる/キャンセル
|
||||
</pre>
|
||||
|
||||
@@ -2,28 +2,30 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
|
||||
# Lazygit 키 바인딩
|
||||
|
||||
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
## 글로벌 키 바인딩
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+r</kbd>: 최근에 사용한 저장소로 전환
|
||||
<kbd>pgup</kbd>: 메인 패널을 위로 스크롤 (fn+up/shift+k)
|
||||
<kbd>pgdown</kbd>: 메인 패널을 아래로로 스크롤 (fn+down/shift+j)
|
||||
<kbd>m</kbd>: view merge/rebase options
|
||||
<kbd>ctrl+p</kbd>: 커스텀 Patch 옵션 보기
|
||||
<kbd>R</kbd>: 새로고침
|
||||
<kbd>?</kbd>: 매뉴 열기
|
||||
<kbd>+</kbd>: 다음 스크린 모드 (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: 이전 스크린 모드
|
||||
<kbd>ctrl+s</kbd>: view filter-by-path options
|
||||
<kbd>W</kbd>: Diff 메뉴 열기
|
||||
<kbd>ctrl+e</kbd>: Diff 메뉴 열기
|
||||
<kbd><c-r></kbd>: 최근에 사용한 저장소로 전환
|
||||
<kbd><pgup></kbd>: 메인 패널을 위로 스크롤 (fn+up/shift+k)
|
||||
<kbd><pgdown></kbd>: 메인 패널을 아래로로 스크롤 (fn+down/shift+j)
|
||||
<kbd>@</kbd>: 명령어 로그 메뉴 열기
|
||||
<kbd>ctrl+w</kbd>: 공백문자를 Diff 뷰에서 표시 여부 전환
|
||||
<kbd>}</kbd>: diff 보기의 변경 사항 주위에 표시되는 컨텍스트의 크기를 늘리기
|
||||
<kbd>{</kbd>: diff 보기의 변경 사항 주위에 표시되는 컨텍스트 크기 줄이기
|
||||
<kbd>:</kbd>: execute custom command
|
||||
<kbd><c-p></kbd>: 커스텀 Patch 옵션 보기
|
||||
<kbd>m</kbd>: view merge/rebase options
|
||||
<kbd>R</kbd>: 새로고침
|
||||
<kbd>+</kbd>: 다음 스크린 모드 (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: 이전 스크린 모드
|
||||
<kbd>?</kbd>: 매뉴 열기
|
||||
<kbd><c-s></kbd>: view filter-by-path options
|
||||
<kbd>W</kbd>: Diff 메뉴 열기
|
||||
<kbd><c-e></kbd>: Diff 메뉴 열기
|
||||
<kbd><c-w></kbd>: 공백문자를 Diff 뷰에서 표시 여부 전환
|
||||
<kbd>z</kbd>: 되돌리기 (reflog) (실험적)
|
||||
<kbd>ctrl+z</kbd>: 다시 실행 (reflog) (실험적)
|
||||
<kbd><c-z></kbd>: 다시 실행 (reflog) (실험적)
|
||||
<kbd>P</kbd>: 푸시
|
||||
<kbd>p</kbd>: 업데이트
|
||||
</pre>
|
||||
@@ -33,9 +35,9 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<pre>
|
||||
<kbd>,</kbd>: 이전 페이지
|
||||
<kbd>.</kbd>: 다음 페이지
|
||||
<kbd><</kbd>: 맨 위로 스크롤
|
||||
<kbd><</kbd>: 맨 위로 스크롤
|
||||
<kbd>/</kbd>: 검색 시작
|
||||
<kbd>></kbd>: 맨 아래로 스크롤
|
||||
<kbd>></kbd>: 맨 아래로 스크롤
|
||||
<kbd>H</kbd>: 우 스크롤
|
||||
<kbd>L</kbd>: 좌 스크롤
|
||||
<kbd>]</kbd>: 이전 탭
|
||||
@@ -45,42 +47,49 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Reflog
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 커밋 SHA를 클립보드에 복사
|
||||
<kbd>space</kbd>: 커밋을 체크아웃
|
||||
<kbd><c-o></kbd>: 커밋 SHA를 클립보드에 복사
|
||||
<kbd><space></kbd>: 커밋을 체크아웃
|
||||
<kbd>y</kbd>: 커밋 attribute 복사
|
||||
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
||||
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
||||
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd>enter</kbd>: 커밋 보기
|
||||
<kbd><c-r></kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd><enter></kbd>: 커밋 보기
|
||||
</pre>
|
||||
|
||||
## Stash
|
||||
|
||||
<pre>
|
||||
<kbd>space</kbd>: 적용
|
||||
<kbd><space></kbd>: 적용
|
||||
<kbd>g</kbd>: pop
|
||||
<kbd>d</kbd>: drop
|
||||
<kbd>n</kbd>: 새 브랜치 생성
|
||||
<kbd>r</kbd>: rename stash
|
||||
<kbd>enter</kbd>: view selected item's files
|
||||
<kbd><enter></kbd>: view selected item's files
|
||||
</pre>
|
||||
|
||||
## Sub-commits
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 커밋 SHA를 클립보드에 복사
|
||||
<kbd>space</kbd>: 커밋을 체크아웃
|
||||
<kbd><c-o></kbd>: 커밋 SHA를 클립보드에 복사
|
||||
<kbd><space></kbd>: 커밋을 체크아웃
|
||||
<kbd>y</kbd>: 커밋 attribute 복사
|
||||
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
||||
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
||||
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd>enter</kbd>: view selected item's files
|
||||
<kbd><c-r></kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd><enter></kbd>: view selected item's files
|
||||
</pre>
|
||||
|
||||
## 메뉴
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: 실행
|
||||
<kbd><esc></kbd>: 닫기
|
||||
</pre>
|
||||
|
||||
## 메인 패널 (Merging)
|
||||
@@ -88,53 +97,53 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<pre>
|
||||
<kbd>e</kbd>: 파일 편집
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
<kbd>◀</kbd>: 이전 충돌을 선택
|
||||
<kbd>▶</kbd>: 다음 충돌을 선택
|
||||
<kbd>▲</kbd>: 이전 hunk를 선택
|
||||
<kbd>▼</kbd>: 다음 hunk를 선택
|
||||
<kbd><left></kbd>: 이전 충돌을 선택
|
||||
<kbd><right></kbd>: 다음 충돌을 선택
|
||||
<kbd><up></kbd>: 이전 hunk를 선택
|
||||
<kbd><down></kbd>: 다음 hunk를 선택
|
||||
<kbd>z</kbd>: 되돌리기
|
||||
<kbd>M</kbd>: git mergetool를 열기
|
||||
<kbd>space</kbd>: pick hunk
|
||||
<kbd><space></kbd>: pick hunk
|
||||
<kbd>b</kbd>: pick all hunks
|
||||
<kbd>esc</kbd>: 파일 목록으로 돌아가기
|
||||
<kbd><esc></kbd>: 파일 목록으로 돌아가기
|
||||
</pre>
|
||||
|
||||
## 메인 패널 (Normal)
|
||||
|
||||
<pre>
|
||||
<kbd>mouse wheel ▼</kbd>: 아래로 스크롤 (fn+up)
|
||||
<kbd>mouse wheel ▲</kbd>: 위로 스크롤 (fn+down)
|
||||
<kbd>mouse wheel down</kbd>: 아래로 스크롤 (fn+up)
|
||||
<kbd>mouse wheel up</kbd>: 위로 스크롤 (fn+down)
|
||||
</pre>
|
||||
|
||||
## 메인 패널 (Patch Building)
|
||||
|
||||
<pre>
|
||||
<kbd>◀</kbd>: 이전 hunk를 선택
|
||||
<kbd>▶</kbd>: 다음 hunk를 선택
|
||||
<kbd><left></kbd>: 이전 hunk를 선택
|
||||
<kbd><right></kbd>: 다음 hunk를 선택
|
||||
<kbd>v</kbd>: 드래그 선택 전환
|
||||
<kbd>V</kbd>: 드래그 선택 전환
|
||||
<kbd>a</kbd>: toggle select hunk
|
||||
<kbd>ctrl+o</kbd>: 선택한 텍스트를 클립보드에 복사
|
||||
<kbd><c-o></kbd>: 선택한 텍스트를 클립보드에 복사
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
<kbd>e</kbd>: 파일 편집
|
||||
<kbd>space</kbd>: line(s)을 패치에 추가/삭제
|
||||
<kbd>esc</kbd>: exit custom patch builder
|
||||
<kbd><space></kbd>: line(s)을 패치에 추가/삭제
|
||||
<kbd><esc></kbd>: exit custom patch builder
|
||||
</pre>
|
||||
|
||||
## 메인 패널 (Staging)
|
||||
|
||||
<pre>
|
||||
<kbd>◀</kbd>: 이전 hunk를 선택
|
||||
<kbd>▶</kbd>: 다음 hunk를 선택
|
||||
<kbd><left></kbd>: 이전 hunk를 선택
|
||||
<kbd><right></kbd>: 다음 hunk를 선택
|
||||
<kbd>v</kbd>: 드래그 선택 전환
|
||||
<kbd>V</kbd>: 드래그 선택 전환
|
||||
<kbd>a</kbd>: toggle select hunk
|
||||
<kbd>ctrl+o</kbd>: 선택한 텍스트를 클립보드에 복사
|
||||
<kbd><c-o></kbd>: 선택한 텍스트를 클립보드에 복사
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
<kbd>e</kbd>: 파일 편집
|
||||
<kbd>esc</kbd>: 파일 목록으로 돌아가기
|
||||
<kbd>tab</kbd>: 패널 전환
|
||||
<kbd>space</kbd>: 선택한 행을 staged / unstaged
|
||||
<kbd><esc></kbd>: 파일 목록으로 돌아가기
|
||||
<kbd><tab></kbd>: 패널 전환
|
||||
<kbd><space></kbd>: 선택한 행을 staged / unstaged
|
||||
<kbd>d</kbd>: 변경을 삭제 (git reset)
|
||||
<kbd>E</kbd>: edit hunk
|
||||
<kbd>c</kbd>: 커밋 변경내용
|
||||
@@ -145,13 +154,13 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## 브랜치
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 브랜치명을 클립보드에 복사
|
||||
<kbd><c-o></kbd>: 브랜치명을 클립보드에 복사
|
||||
<kbd>i</kbd>: git-flow 옵션 보기
|
||||
<kbd>space</kbd>: 체크아웃
|
||||
<kbd><space></kbd>: 체크아웃
|
||||
<kbd>n</kbd>: 새 브랜치 생성
|
||||
<kbd>o</kbd>: 풀 리퀘스트 생성
|
||||
<kbd>O</kbd>: 풀 리퀘스트 생성 옵션
|
||||
<kbd>ctrl+y</kbd>: 풀 리퀘스트 URL을 클립보드에 복사
|
||||
<kbd><c-y></kbd>: 풀 리퀘스트 URL을 클립보드에 복사
|
||||
<kbd>c</kbd>: 이름으로 체크아웃
|
||||
<kbd>F</kbd>: 강제 체크아웃
|
||||
<kbd>d</kbd>: 브랜치 삭제
|
||||
@@ -162,24 +171,24 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>R</kbd>: 브랜치 이름 변경
|
||||
<kbd>u</kbd>: set/unset upstream
|
||||
<kbd>enter</kbd>: 커밋 보기
|
||||
<kbd><enter></kbd>: 커밋 보기
|
||||
</pre>
|
||||
|
||||
## 상태
|
||||
|
||||
<pre>
|
||||
<kbd>e</kbd>: 설정 파일 수정
|
||||
<kbd>o</kbd>: 설정 파일 열기
|
||||
<kbd>e</kbd>: 설정 파일 수정
|
||||
<kbd>u</kbd>: 업데이트 확인
|
||||
<kbd>enter</kbd>: 최근에 사용한 저장소로 전환
|
||||
<kbd><enter></kbd>: 최근에 사용한 저장소로 전환
|
||||
<kbd>a</kbd>: 모든 브랜치 로그 표시
|
||||
</pre>
|
||||
|
||||
## 서브모듈
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 서브모듈 이름을 클립보드에 복사
|
||||
<kbd>enter</kbd>: 서브모듈 열기
|
||||
<kbd><c-o></kbd>: 서브모듈 이름을 클립보드에 복사
|
||||
<kbd><enter></kbd>: 서브모듈 열기
|
||||
<kbd>d</kbd>: 서브모듈 삭제
|
||||
<kbd>u</kbd>: 서브모듈 업데이트
|
||||
<kbd>n</kbd>: 새로운 서브모듈 추가
|
||||
@@ -200,23 +209,23 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## 원격 브랜치
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 브랜치명을 클립보드에 복사
|
||||
<kbd>space</kbd>: 체크아웃
|
||||
<kbd><c-o></kbd>: 브랜치명을 클립보드에 복사
|
||||
<kbd><space></kbd>: 체크아웃
|
||||
<kbd>n</kbd>: 새 브랜치 생성
|
||||
<kbd>M</kbd>: 현재 브랜치에 병합
|
||||
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
|
||||
<kbd>d</kbd>: 브랜치 삭제
|
||||
<kbd>u</kbd>: set as upstream of checked-out branch
|
||||
<kbd>esc</kbd>: 원격목록으로 돌아가기
|
||||
<kbd><esc></kbd>: 원격목록으로 돌아가기
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>enter</kbd>: 커밋 보기
|
||||
<kbd><enter></kbd>: 커밋 보기
|
||||
</pre>
|
||||
|
||||
## 커밋
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 커밋 SHA를 클립보드에 복사
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd><c-o></kbd>: 커밋 SHA를 클립보드에 복사
|
||||
<kbd><c-r></kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd>b</kbd>: bisect 옵션 보기
|
||||
<kbd>s</kbd>: squash down
|
||||
<kbd>f</kbd>: fixup commit
|
||||
@@ -227,56 +236,63 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>p</kbd>: pick commit (when mid-rebase)
|
||||
<kbd>F</kbd>: create fixup commit for this commit
|
||||
<kbd>S</kbd>: squash all 'fixup!' commits above selected commit (autosquash)
|
||||
<kbd>ctrl+j</kbd>: 커밋을 1개 아래로 이동
|
||||
<kbd>ctrl+k</kbd>: 커밋을 1개 위로 이동
|
||||
<kbd><c-j></kbd>: 커밋을 1개 아래로 이동
|
||||
<kbd><c-k></kbd>: 커밋을 1개 위로 이동
|
||||
<kbd>v</kbd>: 커밋을 붙여넣기 (cherry-pick)
|
||||
<kbd>A</kbd>: amend commit with staged changes
|
||||
<kbd>a</kbd>: reset commit author
|
||||
<kbd>t</kbd>: 커밋 되돌리기
|
||||
<kbd>T</kbd>: tag commit
|
||||
<kbd>ctrl+l</kbd>: 로그 메뉴 열기
|
||||
<kbd>space</kbd>: 커밋을 체크아웃
|
||||
<kbd><c-l></kbd>: 로그 메뉴 열기
|
||||
<kbd><space></kbd>: 커밋을 체크아웃
|
||||
<kbd>y</kbd>: 커밋 attribute 복사
|
||||
<kbd>o</kbd>: 브라우저에서 커밋 열기
|
||||
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
|
||||
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
|
||||
<kbd>enter</kbd>: view selected item's files
|
||||
<kbd><enter></kbd>: view selected item's files
|
||||
</pre>
|
||||
|
||||
## 커밋 파일
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 커밋한 파일명을 클립보드에 복사
|
||||
<kbd><c-o></kbd>: 커밋한 파일명을 클립보드에 복사
|
||||
<kbd>c</kbd>: checkout file
|
||||
<kbd>d</kbd>: discard this commit's changes to this file
|
||||
<kbd>o</kbd>: 파일 닫기
|
||||
<kbd>e</kbd>: 파일 편집
|
||||
<kbd>space</kbd>: toggle file included in patch
|
||||
<kbd><space></kbd>: toggle file included in patch
|
||||
<kbd>a</kbd>: toggle all files included in patch
|
||||
<kbd>enter</kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
<kbd><enter></kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
<kbd>`</kbd>: 파일 트리뷰로 전환
|
||||
</pre>
|
||||
|
||||
## 커밋메시지
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: 확인
|
||||
<kbd><esc></kbd>: 닫기
|
||||
</pre>
|
||||
|
||||
## 태그
|
||||
|
||||
<pre>
|
||||
<kbd>space</kbd>: 체크아웃
|
||||
<kbd><space></kbd>: 체크아웃
|
||||
<kbd>d</kbd>: 태그 삭제
|
||||
<kbd>P</kbd>: 태그를 push
|
||||
<kbd>n</kbd>: 태그를 생성
|
||||
<kbd>g</kbd>: view reset options
|
||||
<kbd>enter</kbd>: 커밋 보기
|
||||
<kbd><enter></kbd>: 커밋 보기
|
||||
</pre>
|
||||
|
||||
## 파일
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 파일명을 클립보드에 복사
|
||||
<kbd><c-o></kbd>: 파일명을 클립보드에 복사
|
||||
<kbd>d</kbd>: view 'discard changes' options
|
||||
<kbd>space</kbd>: Staged 전환
|
||||
<kbd>ctrl+b</kbd>: 파일을 필터하기 (Staged/unstaged)
|
||||
<kbd><space></kbd>: Staged 전환
|
||||
<kbd><c-b></kbd>: 파일을 필터하기 (Staged/unstaged)
|
||||
<kbd>c</kbd>: 커밋 변경내용
|
||||
<kbd>w</kbd>: commit changes without pre-commit hook
|
||||
<kbd>A</kbd>: 마지맛 커밋 수정
|
||||
@@ -288,10 +304,17 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>s</kbd>: 변경사항을 Stash
|
||||
<kbd>S</kbd>: Stash 옵션 보기
|
||||
<kbd>a</kbd>: 모든 변경을 Staged/unstaged으로 전환
|
||||
<kbd>enter</kbd>: stage individual hunks/lines for file, or collapse/expand for directory
|
||||
<kbd><enter></kbd>: stage individual hunks/lines for file, or collapse/expand for directory
|
||||
<kbd>g</kbd>: view upstream reset options
|
||||
<kbd>D</kbd>: view reset options
|
||||
<kbd>`</kbd>: 파일 트리뷰로 전환
|
||||
<kbd>M</kbd>: git mergetool를 열기
|
||||
<kbd>f</kbd>: fetch
|
||||
</pre>
|
||||
|
||||
## 확인 패널
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: 확인
|
||||
<kbd><esc></kbd>: 닫기/취소
|
||||
</pre>
|
||||
|
||||
@@ -2,28 +2,30 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
|
||||
# Lazygit Sneltoetsen
|
||||
|
||||
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
## Globale Sneltoetsen
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+r</kbd>: wissel naar een recente repo
|
||||
<kbd>pgup</kbd>: scroll naar beneden vanaf hoofdpaneel (fn+up/shift+k)
|
||||
<kbd>pgdown</kbd>: scroll naar beneden vanaf hoofdpaneel (fn+down/shift+j)
|
||||
<kbd>m</kbd>: bekijk merge/rebase opties
|
||||
<kbd>ctrl+p</kbd>: bekijk aangepaste patch opties
|
||||
<kbd>R</kbd>: verversen
|
||||
<kbd>?</kbd>: open menu
|
||||
<kbd>+</kbd>: volgende scherm modus (normaal/half/groot)
|
||||
<kbd>_</kbd>: vorige scherm modus
|
||||
<kbd>ctrl+s</kbd>: bekijk scoping opties
|
||||
<kbd>W</kbd>: open diff menu
|
||||
<kbd>ctrl+e</kbd>: open diff menu
|
||||
<kbd><c-r></kbd>: wissel naar een recente repo
|
||||
<kbd><pgup></kbd>: scroll naar beneden vanaf hoofdpaneel (fn+up/shift+k)
|
||||
<kbd><pgdown></kbd>: scroll naar beneden vanaf hoofdpaneel (fn+down/shift+j)
|
||||
<kbd>@</kbd>: open command log menu
|
||||
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
||||
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
||||
<kbd>:</kbd>: voer aangepaste commando uit
|
||||
<kbd><c-p></kbd>: bekijk aangepaste patch opties
|
||||
<kbd>m</kbd>: bekijk merge/rebase opties
|
||||
<kbd>R</kbd>: verversen
|
||||
<kbd>+</kbd>: volgende scherm modus (normaal/half/groot)
|
||||
<kbd>_</kbd>: vorige scherm modus
|
||||
<kbd>?</kbd>: open menu
|
||||
<kbd><c-s></kbd>: bekijk scoping opties
|
||||
<kbd>W</kbd>: open diff menu
|
||||
<kbd><c-e></kbd>: open diff menu
|
||||
<kbd><c-w></kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||
<kbd>z</kbd>: ongedaan maken (via reflog) (experimenteel)
|
||||
<kbd>ctrl+z</kbd>: redo (via reflog) (experimenteel)
|
||||
<kbd><c-z></kbd>: redo (via reflog) (experimenteel)
|
||||
<kbd>P</kbd>: push
|
||||
<kbd>p</kbd>: pull
|
||||
</pre>
|
||||
@@ -33,9 +35,9 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<pre>
|
||||
<kbd>,</kbd>: vorige pagina
|
||||
<kbd>.</kbd>: volgende pagina
|
||||
<kbd><</kbd>: scroll naar boven
|
||||
<kbd><</kbd>: scroll naar boven
|
||||
<kbd>/</kbd>: start met zoeken
|
||||
<kbd>></kbd>: scroll naar beneden
|
||||
<kbd>></kbd>: scroll naar beneden
|
||||
<kbd>H</kbd>: scroll left
|
||||
<kbd>L</kbd>: scroll right
|
||||
<kbd>]</kbd>: volgende tabblad
|
||||
@@ -45,10 +47,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Bestanden
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: kopieer de bestandsnaam naar het klembord
|
||||
<kbd><c-o></kbd>: kopieer de bestandsnaam naar het klembord
|
||||
<kbd>d</kbd>: bekijk 'veranderingen ongedaan maken' opties
|
||||
<kbd>space</kbd>: toggle staged
|
||||
<kbd>ctrl+b</kbd>: Filter files (staged/unstaged)
|
||||
<kbd><space></kbd>: toggle staged
|
||||
<kbd><c-b></kbd>: Filter files (staged/unstaged)
|
||||
<kbd>c</kbd>: commit veranderingen
|
||||
<kbd>w</kbd>: commit veranderingen zonder pre-commit hook
|
||||
<kbd>A</kbd>: wijzig laatste commit
|
||||
@@ -60,7 +62,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>s</kbd>: stash-bestanden
|
||||
<kbd>S</kbd>: bekijk stash opties
|
||||
<kbd>a</kbd>: toggle staged alle
|
||||
<kbd>enter</kbd>: stage individuele hunks/lijnen
|
||||
<kbd><enter></kbd>: stage individuele hunks/lijnen
|
||||
<kbd>g</kbd>: bekijk upstream reset opties
|
||||
<kbd>D</kbd>: bekijk reset opties
|
||||
<kbd>`</kbd>: toggle bestandsboom weergave
|
||||
@@ -68,16 +70,23 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>f</kbd>: fetch
|
||||
</pre>
|
||||
|
||||
## Bevestigingspaneel
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: bevestig
|
||||
<kbd><esc></kbd>: sluiten
|
||||
</pre>
|
||||
|
||||
## Branches
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: kopieer branch name naar klembord
|
||||
<kbd><c-o></kbd>: kopieer branch name naar klembord
|
||||
<kbd>i</kbd>: laat git-flow opties zien
|
||||
<kbd>space</kbd>: uitchecken
|
||||
<kbd><space></kbd>: uitchecken
|
||||
<kbd>n</kbd>: nieuwe branch
|
||||
<kbd>o</kbd>: maak een pull-request
|
||||
<kbd>O</kbd>: bekijk opties voor pull-aanvraag
|
||||
<kbd>ctrl+y</kbd>: kopieer de URL van het pull-verzoek naar het klembord
|
||||
<kbd><c-y></kbd>: kopieer de URL van het pull-verzoek naar het klembord
|
||||
<kbd>c</kbd>: uitchecken bij naam
|
||||
<kbd>F</kbd>: forceer checkout
|
||||
<kbd>d</kbd>: verwijder branch
|
||||
@@ -88,28 +97,35 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>g</kbd>: bekijk reset opties
|
||||
<kbd>R</kbd>: hernoem branch
|
||||
<kbd>u</kbd>: set/unset upstream
|
||||
<kbd>enter</kbd>: bekijk commits
|
||||
<kbd><enter></kbd>: bekijk commits
|
||||
</pre>
|
||||
|
||||
## Commit Bericht
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: bevestig
|
||||
<kbd><esc></kbd>: sluiten
|
||||
</pre>
|
||||
|
||||
## Commit bestanden
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: kopieer de vastgelegde bestandsnaam naar het klembord
|
||||
<kbd><c-o></kbd>: kopieer de vastgelegde bestandsnaam naar het klembord
|
||||
<kbd>c</kbd>: bestand uitchecken
|
||||
<kbd>d</kbd>: uitsluit deze commit zijn veranderingen aan dit bestand
|
||||
<kbd>o</kbd>: open bestand
|
||||
<kbd>e</kbd>: verander bestand
|
||||
<kbd>space</kbd>: toggle bestand inbegrepen in patch
|
||||
<kbd><space></kbd>: toggle bestand inbegrepen in patch
|
||||
<kbd>a</kbd>: toggle all files included in patch
|
||||
<kbd>enter</kbd>: enter bestand om geselecteerde regels toe te voegen aan de patch
|
||||
<kbd><enter></kbd>: enter bestand om geselecteerde regels toe te voegen aan de patch
|
||||
<kbd>`</kbd>: toggle bestandsboom weergave
|
||||
</pre>
|
||||
|
||||
## Commits
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: kopieer commit SHA naar klembord
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (gekopieerde) commits selectie
|
||||
<kbd><c-o></kbd>: kopieer commit SHA naar klembord
|
||||
<kbd><c-r></kbd>: reset cherry-picked (gekopieerde) commits selectie
|
||||
<kbd>b</kbd>: view bisect options
|
||||
<kbd>s</kbd>: squash beneden
|
||||
<kbd>f</kbd>: Fixup commit
|
||||
@@ -120,22 +136,29 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>p</kbd>: kies commit (wanneer midden in rebase)
|
||||
<kbd>F</kbd>: creëer fixup commit voor deze commit
|
||||
<kbd>S</kbd>: squash bovenstaande commits
|
||||
<kbd>ctrl+j</kbd>: verplaats commit 1 naar beneden
|
||||
<kbd>ctrl+k</kbd>: verplaats commit 1 naar boven
|
||||
<kbd><c-j></kbd>: verplaats commit 1 naar beneden
|
||||
<kbd><c-k></kbd>: verplaats commit 1 naar boven
|
||||
<kbd>v</kbd>: plak commits (cherry-pick)
|
||||
<kbd>A</kbd>: wijzig commit met staged veranderingen
|
||||
<kbd>a</kbd>: reset commit author
|
||||
<kbd>t</kbd>: commit ongedaan maken
|
||||
<kbd>T</kbd>: tag commit
|
||||
<kbd>ctrl+l</kbd>: open log menu
|
||||
<kbd>space</kbd>: checkout commit
|
||||
<kbd><c-l></kbd>: open log menu
|
||||
<kbd><space></kbd>: checkout commit
|
||||
<kbd>y</kbd>: copy commit attribute
|
||||
<kbd>o</kbd>: open commit in browser
|
||||
<kbd>n</kbd>: creëer nieuwe branch van commit
|
||||
<kbd>g</kbd>: bekijk reset opties
|
||||
<kbd>c</kbd>: kopieer commit (cherry-pick)
|
||||
<kbd>C</kbd>: kopieer commit reeks (cherry-pick)
|
||||
<kbd>enter</kbd>: bekijk gecommite bestanden
|
||||
<kbd><enter></kbd>: bekijk gecommite bestanden
|
||||
</pre>
|
||||
|
||||
## Menu
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: uitvoeren
|
||||
<kbd><esc></kbd>: sluiten
|
||||
</pre>
|
||||
|
||||
## Mergen
|
||||
@@ -143,67 +166,67 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<pre>
|
||||
<kbd>e</kbd>: verander bestand
|
||||
<kbd>o</kbd>: open bestand
|
||||
<kbd>◀</kbd>: selecteer voorgaand conflict
|
||||
<kbd>▶</kbd>: selecteer volgende conflict
|
||||
<kbd>▲</kbd>: selecteer bovenste hunk
|
||||
<kbd>▼</kbd>: selecteer onderste hunk
|
||||
<kbd><left></kbd>: selecteer voorgaand conflict
|
||||
<kbd><right></kbd>: selecteer volgende conflict
|
||||
<kbd><up></kbd>: selecteer bovenste hunk
|
||||
<kbd><down></kbd>: selecteer onderste hunk
|
||||
<kbd>z</kbd>: ongedaan maken
|
||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
||||
<kbd>space</kbd>: kies hunk
|
||||
<kbd><space></kbd>: kies hunk
|
||||
<kbd>b</kbd>: kies bijde hunks
|
||||
<kbd>esc</kbd>: ga terug naar het bestanden paneel
|
||||
<kbd><esc></kbd>: ga terug naar het bestanden paneel
|
||||
</pre>
|
||||
|
||||
## Normaal
|
||||
|
||||
<pre>
|
||||
<kbd>mouse wheel ▼</kbd>: scroll omlaag (fn+up)
|
||||
<kbd>mouse wheel ▲</kbd>: scroll omhoog (fn+down)
|
||||
<kbd>mouse wheel down</kbd>: scroll omlaag (fn+up)
|
||||
<kbd>mouse wheel up</kbd>: scroll omhoog (fn+down)
|
||||
</pre>
|
||||
|
||||
## Patch Bouwen
|
||||
|
||||
<pre>
|
||||
<kbd>◀</kbd>: selecteer de vorige hunk
|
||||
<kbd>▶</kbd>: selecteer de volgende hunk
|
||||
<kbd><left></kbd>: selecteer de vorige hunk
|
||||
<kbd><right></kbd>: selecteer de volgende hunk
|
||||
<kbd>v</kbd>: toggle drag selecteer
|
||||
<kbd>V</kbd>: toggle drag selecteer
|
||||
<kbd>a</kbd>: toggle selecteer hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd><c-o></kbd>: copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: open bestand
|
||||
<kbd>e</kbd>: verander bestand
|
||||
<kbd>space</kbd>: voeg toe/verwijder lijn(en) in patch
|
||||
<kbd>esc</kbd>: sluit lijn-bij-lijn modus
|
||||
<kbd><space></kbd>: voeg toe/verwijder lijn(en) in patch
|
||||
<kbd><esc></kbd>: sluit lijn-bij-lijn modus
|
||||
</pre>
|
||||
|
||||
## Reflog
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: kopieer commit SHA naar klembord
|
||||
<kbd>space</kbd>: checkout commit
|
||||
<kbd><c-o></kbd>: kopieer commit SHA naar klembord
|
||||
<kbd><space></kbd>: checkout commit
|
||||
<kbd>y</kbd>: copy commit attribute
|
||||
<kbd>o</kbd>: open commit in browser
|
||||
<kbd>n</kbd>: creëer nieuwe branch van commit
|
||||
<kbd>g</kbd>: bekijk reset opties
|
||||
<kbd>c</kbd>: kopieer commit (cherry-pick)
|
||||
<kbd>C</kbd>: kopieer commit reeks (cherry-pick)
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (gekopieerde) commits selectie
|
||||
<kbd>enter</kbd>: bekijk commits
|
||||
<kbd><c-r></kbd>: reset cherry-picked (gekopieerde) commits selectie
|
||||
<kbd><enter></kbd>: bekijk commits
|
||||
</pre>
|
||||
|
||||
## Remote Branches
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: kopieer branch name naar klembord
|
||||
<kbd>space</kbd>: uitchecken
|
||||
<kbd><c-o></kbd>: kopieer branch name naar klembord
|
||||
<kbd><space></kbd>: uitchecken
|
||||
<kbd>n</kbd>: nieuwe branch
|
||||
<kbd>M</kbd>: merge in met huidige checked out branch
|
||||
<kbd>r</kbd>: rebase branch
|
||||
<kbd>d</kbd>: verwijder branch
|
||||
<kbd>u</kbd>: stel in als upstream van uitgecheckte branch
|
||||
<kbd>esc</kbd>: ga terug naar remotes lijst
|
||||
<kbd><esc></kbd>: ga terug naar remotes lijst
|
||||
<kbd>g</kbd>: bekijk reset opties
|
||||
<kbd>enter</kbd>: bekijk commits
|
||||
<kbd><enter></kbd>: bekijk commits
|
||||
</pre>
|
||||
|
||||
## Remotes
|
||||
@@ -218,17 +241,17 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Staging
|
||||
|
||||
<pre>
|
||||
<kbd>◀</kbd>: selecteer de vorige hunk
|
||||
<kbd>▶</kbd>: selecteer de volgende hunk
|
||||
<kbd><left></kbd>: selecteer de vorige hunk
|
||||
<kbd><right></kbd>: selecteer de volgende hunk
|
||||
<kbd>v</kbd>: toggle drag selecteer
|
||||
<kbd>V</kbd>: toggle drag selecteer
|
||||
<kbd>a</kbd>: toggle selecteer hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd><c-o></kbd>: copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: open bestand
|
||||
<kbd>e</kbd>: verander bestand
|
||||
<kbd>esc</kbd>: ga terug naar het bestanden paneel
|
||||
<kbd>tab</kbd>: ga naar een ander paneel
|
||||
<kbd>space</kbd>: toggle lijnen staged / unstaged
|
||||
<kbd><esc></kbd>: ga terug naar het bestanden paneel
|
||||
<kbd><tab></kbd>: ga naar een ander paneel
|
||||
<kbd><space></kbd>: toggle lijnen staged / unstaged
|
||||
<kbd>d</kbd>: verwijdert change (git reset)
|
||||
<kbd>E</kbd>: edit hunk
|
||||
<kbd>c</kbd>: commit veranderingen
|
||||
@@ -239,44 +262,44 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Stash
|
||||
|
||||
<pre>
|
||||
<kbd>space</kbd>: toepassen
|
||||
<kbd><space></kbd>: toepassen
|
||||
<kbd>g</kbd>: pop
|
||||
<kbd>d</kbd>: laten vallen
|
||||
<kbd>n</kbd>: nieuwe branch
|
||||
<kbd>r</kbd>: rename stash
|
||||
<kbd>enter</kbd>: bekijk gecommite bestanden
|
||||
<kbd><enter></kbd>: bekijk gecommite bestanden
|
||||
</pre>
|
||||
|
||||
## Status
|
||||
|
||||
<pre>
|
||||
<kbd>e</kbd>: verander config bestand
|
||||
<kbd>o</kbd>: open config bestand
|
||||
<kbd>e</kbd>: verander config bestand
|
||||
<kbd>u</kbd>: check voor updates
|
||||
<kbd>enter</kbd>: wissel naar een recente repo
|
||||
<kbd><enter></kbd>: wissel naar een recente repo
|
||||
<kbd>a</kbd>: alle logs van de branch laten zien
|
||||
</pre>
|
||||
|
||||
## Sub-commits
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: kopieer commit SHA naar klembord
|
||||
<kbd>space</kbd>: checkout commit
|
||||
<kbd><c-o></kbd>: kopieer commit SHA naar klembord
|
||||
<kbd><space></kbd>: checkout commit
|
||||
<kbd>y</kbd>: copy commit attribute
|
||||
<kbd>o</kbd>: open commit in browser
|
||||
<kbd>n</kbd>: creëer nieuwe branch van commit
|
||||
<kbd>g</kbd>: bekijk reset opties
|
||||
<kbd>c</kbd>: kopieer commit (cherry-pick)
|
||||
<kbd>C</kbd>: kopieer commit reeks (cherry-pick)
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (gekopieerde) commits selectie
|
||||
<kbd>enter</kbd>: bekijk gecommite bestanden
|
||||
<kbd><c-r></kbd>: reset cherry-picked (gekopieerde) commits selectie
|
||||
<kbd><enter></kbd>: bekijk gecommite bestanden
|
||||
</pre>
|
||||
|
||||
## Submodules
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: kopieer submodule naam naar klembord
|
||||
<kbd>enter</kbd>: enter submodule
|
||||
<kbd><c-o></kbd>: kopieer submodule naam naar klembord
|
||||
<kbd><enter></kbd>: enter submodule
|
||||
<kbd>d</kbd>: remove submodule
|
||||
<kbd>u</kbd>: update submodule
|
||||
<kbd>n</kbd>: voeg nieuwe submodule toe
|
||||
@@ -288,10 +311,10 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Tags
|
||||
|
||||
<pre>
|
||||
<kbd>space</kbd>: uitchecken
|
||||
<kbd><space></kbd>: uitchecken
|
||||
<kbd>d</kbd>: verwijder tag
|
||||
<kbd>P</kbd>: push tag
|
||||
<kbd>n</kbd>: creëer tag
|
||||
<kbd>g</kbd>: bekijk reset opties
|
||||
<kbd>enter</kbd>: bekijk commits
|
||||
<kbd><enter></kbd>: bekijk commits
|
||||
</pre>
|
||||
|
||||
@@ -2,28 +2,30 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
|
||||
# Lazygit Keybindings
|
||||
|
||||
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
## Globalne
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+r</kbd>: switch to a recent repo
|
||||
<kbd>pgup</kbd>: scroll up main panel (fn+up/shift+k)
|
||||
<kbd>pgdown</kbd>: scroll down main panel (fn+down/shift+j)
|
||||
<kbd>m</kbd>: widok scalenia/opcje zmiany bazy
|
||||
<kbd>ctrl+p</kbd>: view custom patch options
|
||||
<kbd>R</kbd>: odśwież
|
||||
<kbd>?</kbd>: open menu
|
||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: prev screen mode
|
||||
<kbd>ctrl+s</kbd>: view filter-by-path options
|
||||
<kbd>W</kbd>: open diff menu
|
||||
<kbd>ctrl+e</kbd>: open diff menu
|
||||
<kbd><c-r></kbd>: switch to a recent repo
|
||||
<kbd><pgup></kbd>: scroll up main panel (fn+up/shift+k)
|
||||
<kbd><pgdown></kbd>: scroll down main panel (fn+down/shift+j)
|
||||
<kbd>@</kbd>: open command log menu
|
||||
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||
<kbd>}</kbd>: Increase the size of the context shown around changes in the diff view
|
||||
<kbd>{</kbd>: Decrease the size of the context shown around changes in the diff view
|
||||
<kbd>:</kbd>: wykonaj własną komendę
|
||||
<kbd><c-p></kbd>: view custom patch options
|
||||
<kbd>m</kbd>: widok scalenia/opcje zmiany bazy
|
||||
<kbd>R</kbd>: odśwież
|
||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: prev screen mode
|
||||
<kbd>?</kbd>: open menu
|
||||
<kbd><c-s></kbd>: view filter-by-path options
|
||||
<kbd>W</kbd>: open diff menu
|
||||
<kbd><c-e></kbd>: open diff menu
|
||||
<kbd><c-w></kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||
<kbd>z</kbd>: undo (via reflog) (experimental)
|
||||
<kbd>ctrl+z</kbd>: redo (via reflog) (experimental)
|
||||
<kbd><c-z></kbd>: redo (via reflog) (experimental)
|
||||
<kbd>P</kbd>: push
|
||||
<kbd>p</kbd>: pull
|
||||
</pre>
|
||||
@@ -33,20 +35,27 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<pre>
|
||||
<kbd>,</kbd>: previous page
|
||||
<kbd>.</kbd>: next page
|
||||
<kbd><</kbd>: scroll to top
|
||||
<kbd><</kbd>: scroll to top
|
||||
<kbd>/</kbd>: start search
|
||||
<kbd>></kbd>: scroll to bottom
|
||||
<kbd>></kbd>: scroll to bottom
|
||||
<kbd>H</kbd>: scroll left
|
||||
<kbd>L</kbd>: scroll right
|
||||
<kbd>]</kbd>: next tab
|
||||
<kbd>[</kbd>: previous tab
|
||||
</pre>
|
||||
|
||||
## Commit Summary
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: potwierdź
|
||||
<kbd><esc></kbd>: zamknij
|
||||
</pre>
|
||||
|
||||
## Commity
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy commit SHA to clipboard
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd><c-o></kbd>: copy commit SHA to clipboard
|
||||
<kbd><c-r></kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd>b</kbd>: view bisect options
|
||||
<kbd>s</kbd>: ściśnij
|
||||
<kbd>f</kbd>: napraw commit
|
||||
@@ -57,34 +66,41 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>p</kbd>: wybierz commit (podczas zmiany bazy)
|
||||
<kbd>F</kbd>: utwórz commit naprawczy dla tego commita
|
||||
<kbd>S</kbd>: spłaszcz wszystkie commity naprawcze powyżej zaznaczonych commitów (autosquash)
|
||||
<kbd>ctrl+j</kbd>: przenieś commit 1 w dół
|
||||
<kbd>ctrl+k</kbd>: przenieś commit 1 w górę
|
||||
<kbd><c-j></kbd>: przenieś commit 1 w dół
|
||||
<kbd><c-k></kbd>: przenieś commit 1 w górę
|
||||
<kbd>v</kbd>: wklej commity (przebieranie)
|
||||
<kbd>A</kbd>: popraw commit zmianami z poczekalni
|
||||
<kbd>a</kbd>: reset commit author
|
||||
<kbd>t</kbd>: odwróć commit
|
||||
<kbd>T</kbd>: tag commit
|
||||
<kbd>ctrl+l</kbd>: open log menu
|
||||
<kbd>space</kbd>: checkout commit
|
||||
<kbd><c-l></kbd>: open log menu
|
||||
<kbd><space></kbd>: checkout commit
|
||||
<kbd>y</kbd>: copy commit attribute
|
||||
<kbd>o</kbd>: open commit in browser
|
||||
<kbd>n</kbd>: create new branch off of commit
|
||||
<kbd>g</kbd>: wyświetl opcje resetu
|
||||
<kbd>c</kbd>: kopiuj commit (przebieranie)
|
||||
<kbd>C</kbd>: kopiuj zakres commitów (przebieranie)
|
||||
<kbd>enter</kbd>: przeglądaj pliki commita
|
||||
<kbd><enter></kbd>: przeglądaj pliki commita
|
||||
</pre>
|
||||
|
||||
## Confirmation Panel
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: potwierdź
|
||||
<kbd><esc></kbd>: zamknij
|
||||
</pre>
|
||||
|
||||
## Local Branches
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy branch name to clipboard
|
||||
<kbd><c-o></kbd>: copy branch name to clipboard
|
||||
<kbd>i</kbd>: show git-flow options
|
||||
<kbd>space</kbd>: przełącz
|
||||
<kbd><space></kbd>: przełącz
|
||||
<kbd>n</kbd>: nowa gałąź
|
||||
<kbd>o</kbd>: utwórz żądanie pobrania
|
||||
<kbd>O</kbd>: utwórz opcje żądania ściągnięcia
|
||||
<kbd>ctrl+y</kbd>: skopiuj adres URL żądania pobrania do schowka
|
||||
<kbd><c-y></kbd>: skopiuj adres URL żądania pobrania do schowka
|
||||
<kbd>c</kbd>: przełącz używając nazwy
|
||||
<kbd>F</kbd>: wymuś przełączenie
|
||||
<kbd>d</kbd>: usuń gałąź
|
||||
@@ -95,31 +111,38 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>g</kbd>: wyświetl opcje resetu
|
||||
<kbd>R</kbd>: rename branch
|
||||
<kbd>u</kbd>: set/unset upstream
|
||||
<kbd>enter</kbd>: view commits
|
||||
<kbd><enter></kbd>: view commits
|
||||
</pre>
|
||||
|
||||
## Main Panel (Patch Building)
|
||||
|
||||
<pre>
|
||||
<kbd>◀</kbd>: poprzedni kawałek
|
||||
<kbd>▶</kbd>: następny kawałek
|
||||
<kbd><left></kbd>: poprzedni kawałek
|
||||
<kbd><right></kbd>: następny kawałek
|
||||
<kbd>v</kbd>: toggle drag select
|
||||
<kbd>V</kbd>: toggle drag select
|
||||
<kbd>a</kbd>: toggle select hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd><c-o></kbd>: copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: otwórz plik
|
||||
<kbd>e</kbd>: edytuj plik
|
||||
<kbd>space</kbd>: add/remove line(s) to patch
|
||||
<kbd>esc</kbd>: wyście z trybu "linia po linii"
|
||||
<kbd><space></kbd>: add/remove line(s) to patch
|
||||
<kbd><esc></kbd>: wyście z trybu "linia po linii"
|
||||
</pre>
|
||||
|
||||
## Menu
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: wykonaj
|
||||
<kbd><esc></kbd>: zamknij
|
||||
</pre>
|
||||
|
||||
## Pliki
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy the file name to the clipboard
|
||||
<kbd><c-o></kbd>: copy the file name to the clipboard
|
||||
<kbd>d</kbd>: pokaż opcje porzucania zmian
|
||||
<kbd>space</kbd>: przełącz stan poczekalni
|
||||
<kbd>ctrl+b</kbd>: Filter files (staged/unstaged)
|
||||
<kbd><space></kbd>: przełącz stan poczekalni
|
||||
<kbd><c-b></kbd>: Filter files (staged/unstaged)
|
||||
<kbd>c</kbd>: Zatwierdź zmiany
|
||||
<kbd>w</kbd>: zatwierdź zmiany bez skryptu pre-commit
|
||||
<kbd>A</kbd>: Zmień ostatni commit
|
||||
@@ -131,7 +154,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>s</kbd>: przechowaj zmiany
|
||||
<kbd>S</kbd>: wyświetl opcje schowka
|
||||
<kbd>a</kbd>: przełącz stan poczekalni wszystkich
|
||||
<kbd>enter</kbd>: zatwierdź pojedyncze linie
|
||||
<kbd><enter></kbd>: zatwierdź pojedyncze linie
|
||||
<kbd>g</kbd>: view upstream reset options
|
||||
<kbd>D</kbd>: wyświetl opcje resetu
|
||||
<kbd>`</kbd>: toggle file tree view
|
||||
@@ -142,31 +165,31 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Pliki commita
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy the committed file name to the clipboard
|
||||
<kbd><c-o></kbd>: copy the committed file name to the clipboard
|
||||
<kbd>c</kbd>: plik wybierania
|
||||
<kbd>d</kbd>: porzuć zmiany commita dla tego pliku
|
||||
<kbd>o</kbd>: otwórz plik
|
||||
<kbd>e</kbd>: edytuj plik
|
||||
<kbd>space</kbd>: toggle file included in patch
|
||||
<kbd><space></kbd>: toggle file included in patch
|
||||
<kbd>a</kbd>: toggle all files included in patch
|
||||
<kbd>enter</kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
<kbd><enter></kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
|
||||
<kbd>`</kbd>: toggle file tree view
|
||||
</pre>
|
||||
|
||||
## Poczekalnia
|
||||
|
||||
<pre>
|
||||
<kbd>◀</kbd>: poprzedni kawałek
|
||||
<kbd>▶</kbd>: następny kawałek
|
||||
<kbd><left></kbd>: poprzedni kawałek
|
||||
<kbd><right></kbd>: następny kawałek
|
||||
<kbd>v</kbd>: toggle drag select
|
||||
<kbd>V</kbd>: toggle drag select
|
||||
<kbd>a</kbd>: toggle select hunk
|
||||
<kbd>ctrl+o</kbd>: copy the selected text to the clipboard
|
||||
<kbd><c-o></kbd>: copy the selected text to the clipboard
|
||||
<kbd>o</kbd>: otwórz plik
|
||||
<kbd>e</kbd>: edytuj plik
|
||||
<kbd>esc</kbd>: wróć do panelu plików
|
||||
<kbd>tab</kbd>: switch to other panel (staged/unstaged changes)
|
||||
<kbd>space</kbd>: toggle line staged / unstaged
|
||||
<kbd><esc></kbd>: wróć do panelu plików
|
||||
<kbd><tab></kbd>: switch to other panel (staged/unstaged changes)
|
||||
<kbd><space></kbd>: toggle line staged / unstaged
|
||||
<kbd>d</kbd>: delete change (git reset)
|
||||
<kbd>E</kbd>: edit hunk
|
||||
<kbd>c</kbd>: Zatwierdź zmiany
|
||||
@@ -177,31 +200,31 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Reflog
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy commit SHA to clipboard
|
||||
<kbd>space</kbd>: checkout commit
|
||||
<kbd><c-o></kbd>: copy commit SHA to clipboard
|
||||
<kbd><space></kbd>: checkout commit
|
||||
<kbd>y</kbd>: copy commit attribute
|
||||
<kbd>o</kbd>: open commit in browser
|
||||
<kbd>n</kbd>: create new branch off of commit
|
||||
<kbd>g</kbd>: wyświetl opcje resetu
|
||||
<kbd>c</kbd>: kopiuj commit (przebieranie)
|
||||
<kbd>C</kbd>: kopiuj zakres commitów (przebieranie)
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd>enter</kbd>: view commits
|
||||
<kbd><c-r></kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd><enter></kbd>: view commits
|
||||
</pre>
|
||||
|
||||
## Remote Branches
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy branch name to clipboard
|
||||
<kbd>space</kbd>: przełącz
|
||||
<kbd><c-o></kbd>: copy branch name to clipboard
|
||||
<kbd><space></kbd>: przełącz
|
||||
<kbd>n</kbd>: nowa gałąź
|
||||
<kbd>M</kbd>: scal do obecnej gałęzi
|
||||
<kbd>r</kbd>: zmiana bazy gałęzi
|
||||
<kbd>d</kbd>: usuń gałąź
|
||||
<kbd>u</kbd>: set as upstream of checked-out branch
|
||||
<kbd>esc</kbd>: wróć do listy repozytoriów zdalnych
|
||||
<kbd><esc></kbd>: wróć do listy repozytoriów zdalnych
|
||||
<kbd>g</kbd>: wyświetl opcje resetu
|
||||
<kbd>enter</kbd>: view commits
|
||||
<kbd><enter></kbd>: view commits
|
||||
</pre>
|
||||
|
||||
## Remotes
|
||||
@@ -218,58 +241,58 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<pre>
|
||||
<kbd>e</kbd>: edytuj plik
|
||||
<kbd>o</kbd>: otwórz plik
|
||||
<kbd>◀</kbd>: poprzedni konflikt
|
||||
<kbd>▶</kbd>: następny konflikt
|
||||
<kbd>▲</kbd>: wybierz poprzedni kawałek
|
||||
<kbd>▼</kbd>: wybierz następny kawałek
|
||||
<kbd><left></kbd>: poprzedni konflikt
|
||||
<kbd><right></kbd>: następny konflikt
|
||||
<kbd><up></kbd>: wybierz poprzedni kawałek
|
||||
<kbd><down></kbd>: wybierz następny kawałek
|
||||
<kbd>z</kbd>: cofnij
|
||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
||||
<kbd>space</kbd>: wybierz kawałek
|
||||
<kbd><space></kbd>: wybierz kawałek
|
||||
<kbd>b</kbd>: wybierz wszystkie kawałki
|
||||
<kbd>esc</kbd>: wróć do panelu plików
|
||||
<kbd><esc></kbd>: wróć do panelu plików
|
||||
</pre>
|
||||
|
||||
## Schowek
|
||||
|
||||
<pre>
|
||||
<kbd>space</kbd>: zastosuj
|
||||
<kbd><space></kbd>: zastosuj
|
||||
<kbd>g</kbd>: wyciągnij
|
||||
<kbd>d</kbd>: porzuć
|
||||
<kbd>n</kbd>: nowa gałąź
|
||||
<kbd>r</kbd>: rename stash
|
||||
<kbd>enter</kbd>: przeglądaj pliki commita
|
||||
<kbd><enter></kbd>: przeglądaj pliki commita
|
||||
</pre>
|
||||
|
||||
## Status
|
||||
|
||||
<pre>
|
||||
<kbd>e</kbd>: edytuj konfigurację
|
||||
<kbd>o</kbd>: otwórz konfigurację
|
||||
<kbd>e</kbd>: edytuj konfigurację
|
||||
<kbd>u</kbd>: sprawdź aktualizacje
|
||||
<kbd>enter</kbd>: switch to a recent repo
|
||||
<kbd><enter></kbd>: switch to a recent repo
|
||||
<kbd>a</kbd>: pokaż wszystkie logi gałęzi
|
||||
</pre>
|
||||
|
||||
## Sub-commits
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy commit SHA to clipboard
|
||||
<kbd>space</kbd>: checkout commit
|
||||
<kbd><c-o></kbd>: copy commit SHA to clipboard
|
||||
<kbd><space></kbd>: checkout commit
|
||||
<kbd>y</kbd>: copy commit attribute
|
||||
<kbd>o</kbd>: open commit in browser
|
||||
<kbd>n</kbd>: create new branch off of commit
|
||||
<kbd>g</kbd>: wyświetl opcje resetu
|
||||
<kbd>c</kbd>: kopiuj commit (przebieranie)
|
||||
<kbd>C</kbd>: kopiuj zakres commitów (przebieranie)
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd>enter</kbd>: przeglądaj pliki commita
|
||||
<kbd><c-r></kbd>: reset cherry-picked (copied) commits selection
|
||||
<kbd><enter></kbd>: przeglądaj pliki commita
|
||||
</pre>
|
||||
|
||||
## Submodules
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy submodule name to clipboard
|
||||
<kbd>enter</kbd>: enter submodule
|
||||
<kbd><c-o></kbd>: copy submodule name to clipboard
|
||||
<kbd><enter></kbd>: enter submodule
|
||||
<kbd>d</kbd>: remove submodule
|
||||
<kbd>u</kbd>: update submodule
|
||||
<kbd>n</kbd>: add new submodule
|
||||
@@ -281,17 +304,17 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Tags
|
||||
|
||||
<pre>
|
||||
<kbd>space</kbd>: przełącz
|
||||
<kbd><space></kbd>: przełącz
|
||||
<kbd>d</kbd>: delete tag
|
||||
<kbd>P</kbd>: push tag
|
||||
<kbd>n</kbd>: create tag
|
||||
<kbd>g</kbd>: wyświetl opcje resetu
|
||||
<kbd>enter</kbd>: view commits
|
||||
<kbd><enter></kbd>: view commits
|
||||
</pre>
|
||||
|
||||
## Zwykłe
|
||||
|
||||
<pre>
|
||||
<kbd>mouse wheel ▼</kbd>: przewiń w dół (fn+up)
|
||||
<kbd>mouse wheel ▲</kbd>: przewiń w górę (fn+down)
|
||||
<kbd>mouse wheel down</kbd>: przewiń w dół (fn+up)
|
||||
<kbd>mouse wheel up</kbd>: przewiń w górę (fn+down)
|
||||
</pre>
|
||||
|
||||
@@ -2,28 +2,30 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
|
||||
# Lazygit 按键绑定
|
||||
|
||||
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
|
||||
|
||||
## 全局键绑定
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+r</kbd>: 切换到最近的仓库
|
||||
<kbd>pgup</kbd>: 向上滚动主面板 (fn+up/shift+k)
|
||||
<kbd>pgdown</kbd>: 向下滚动主面板 (fn+down/shift+j)
|
||||
<kbd>m</kbd>: 查看 合并/变基 选项
|
||||
<kbd>ctrl+p</kbd>: 查看自定义补丁选项
|
||||
<kbd>R</kbd>: 刷新
|
||||
<kbd>?</kbd>: 打开菜单
|
||||
<kbd>+</kbd>: 下一屏模式(正常/半屏/全屏)
|
||||
<kbd>_</kbd>: 上一屏模式
|
||||
<kbd>ctrl+s</kbd>: 查看按路径过滤选项
|
||||
<kbd>W</kbd>: 打开 diff 菜单
|
||||
<kbd>ctrl+e</kbd>: 打开 diff 菜单
|
||||
<kbd><c-r></kbd>: 切换到最近的仓库
|
||||
<kbd><pgup></kbd>: 向上滚动主面板 (fn+up/shift+k)
|
||||
<kbd><pgdown></kbd>: 向下滚动主面板 (fn+down/shift+j)
|
||||
<kbd>@</kbd>: 打开命令日志菜单
|
||||
<kbd>ctrl+w</kbd>: 切换是否在差异视图中显示空白字符差异
|
||||
<kbd>}</kbd>: 扩大差异视图中显示的上下文范围
|
||||
<kbd>{</kbd>: 缩小差异视图中显示的上下文范围
|
||||
<kbd>:</kbd>: 执行自定义命令
|
||||
<kbd><c-p></kbd>: 查看自定义补丁选项
|
||||
<kbd>m</kbd>: 查看 合并/变基 选项
|
||||
<kbd>R</kbd>: 刷新
|
||||
<kbd>+</kbd>: 下一屏模式(正常/半屏/全屏)
|
||||
<kbd>_</kbd>: 上一屏模式
|
||||
<kbd>?</kbd>: 打开菜单
|
||||
<kbd><c-s></kbd>: 查看按路径过滤选项
|
||||
<kbd>W</kbd>: 打开 diff 菜单
|
||||
<kbd><c-e></kbd>: 打开 diff 菜单
|
||||
<kbd><c-w></kbd>: 切换是否在差异视图中显示空白字符差异
|
||||
<kbd>z</kbd>: (通过 reflog)撤销「实验功能」
|
||||
<kbd>ctrl+z</kbd>: (通过 reflog)重做「实验功能」
|
||||
<kbd><c-z></kbd>: (通过 reflog)重做「实验功能」
|
||||
<kbd>P</kbd>: 推送
|
||||
<kbd>p</kbd>: 拉取
|
||||
</pre>
|
||||
@@ -33,9 +35,9 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<pre>
|
||||
<kbd>,</kbd>: 上一页
|
||||
<kbd>.</kbd>: 下一页
|
||||
<kbd><</kbd>: 滚动到顶部
|
||||
<kbd><</kbd>: 滚动到顶部
|
||||
<kbd>/</kbd>: 开始搜索
|
||||
<kbd>></kbd>: 滚动到底部
|
||||
<kbd>></kbd>: 滚动到底部
|
||||
<kbd>H</kbd>: 向左滚动
|
||||
<kbd>L</kbd>: 向右滚动
|
||||
<kbd>]</kbd>: 下一个标签
|
||||
@@ -45,28 +47,28 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## Reflog 页面
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 将提交的 SHA 复制到剪贴板
|
||||
<kbd>space</kbd>: 检出提交
|
||||
<kbd><c-o></kbd>: 将提交的 SHA 复制到剪贴板
|
||||
<kbd><space></kbd>: 检出提交
|
||||
<kbd>y</kbd>: copy commit attribute
|
||||
<kbd>o</kbd>: 在浏览器中打开提交
|
||||
<kbd>n</kbd>: 从提交创建新分支
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>c</kbd>: 复制提交(拣选)
|
||||
<kbd>C</kbd>: 复制提交范围(拣选)
|
||||
<kbd>ctrl+r</kbd>: 重置已拣选(复制)的提交
|
||||
<kbd>enter</kbd>: 查看提交
|
||||
<kbd><c-r></kbd>: 重置已拣选(复制)的提交
|
||||
<kbd><enter></kbd>: 查看提交
|
||||
</pre>
|
||||
|
||||
## 分支页面
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 将分支名称复制到剪贴板
|
||||
<kbd><c-o></kbd>: 将分支名称复制到剪贴板
|
||||
<kbd>i</kbd>: 显示 git-flow 选项
|
||||
<kbd>space</kbd>: 检出
|
||||
<kbd><space></kbd>: 检出
|
||||
<kbd>n</kbd>: 新分支
|
||||
<kbd>o</kbd>: 创建抓取请求
|
||||
<kbd>O</kbd>: 创建抓取请求选项
|
||||
<kbd>ctrl+y</kbd>: 将抓取请求 URL 复制到剪贴板
|
||||
<kbd><c-y></kbd>: 将抓取请求 URL 复制到剪贴板
|
||||
<kbd>c</kbd>: 按名称检出
|
||||
<kbd>F</kbd>: 强制检出
|
||||
<kbd>d</kbd>: 删除分支
|
||||
@@ -77,29 +79,29 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>R</kbd>: 重命名分支
|
||||
<kbd>u</kbd>: set/unset upstream
|
||||
<kbd>enter</kbd>: 查看提交
|
||||
<kbd><enter></kbd>: 查看提交
|
||||
</pre>
|
||||
|
||||
## 子提交
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 将提交的 SHA 复制到剪贴板
|
||||
<kbd>space</kbd>: 检出提交
|
||||
<kbd><c-o></kbd>: 将提交的 SHA 复制到剪贴板
|
||||
<kbd><space></kbd>: 检出提交
|
||||
<kbd>y</kbd>: copy commit attribute
|
||||
<kbd>o</kbd>: 在浏览器中打开提交
|
||||
<kbd>n</kbd>: 从提交创建新分支
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>c</kbd>: 复制提交(拣选)
|
||||
<kbd>C</kbd>: 复制提交范围(拣选)
|
||||
<kbd>ctrl+r</kbd>: 重置已拣选(复制)的提交
|
||||
<kbd>enter</kbd>: 查看提交的文件
|
||||
<kbd><c-r></kbd>: 重置已拣选(复制)的提交
|
||||
<kbd><enter></kbd>: 查看提交的文件
|
||||
</pre>
|
||||
|
||||
## 子模块
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 将子模块名称复制到剪贴板
|
||||
<kbd>enter</kbd>: 输入子模块
|
||||
<kbd><c-o></kbd>: 将子模块名称复制到剪贴板
|
||||
<kbd><enter></kbd>: 输入子模块
|
||||
<kbd>d</kbd>: 删除子模块
|
||||
<kbd>u</kbd>: 更新子模块
|
||||
<kbd>n</kbd>: 添加新的子模块
|
||||
@@ -111,8 +113,8 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## 提交
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 将提交的 SHA 复制到剪贴板
|
||||
<kbd>ctrl+r</kbd>: 重置已拣选(复制)的提交
|
||||
<kbd><c-o></kbd>: 将提交的 SHA 复制到剪贴板
|
||||
<kbd><c-r></kbd>: 重置已拣选(复制)的提交
|
||||
<kbd>b</kbd>: 查看二分查找选项
|
||||
<kbd>s</kbd>: 向下压缩
|
||||
<kbd>f</kbd>: 修正提交(fixup)
|
||||
@@ -123,45 +125,52 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>p</kbd>: 选择提交(变基过程中)
|
||||
<kbd>F</kbd>: 为此提交创建修正
|
||||
<kbd>S</kbd>: 压缩在所选提交之上的所有“fixup!”提交(自动压缩)
|
||||
<kbd>ctrl+j</kbd>: 下移提交
|
||||
<kbd>ctrl+k</kbd>: 上移提交
|
||||
<kbd><c-j></kbd>: 下移提交
|
||||
<kbd><c-k></kbd>: 上移提交
|
||||
<kbd>v</kbd>: 粘贴提交(拣选)
|
||||
<kbd>A</kbd>: 用已暂存的更改来修补提交
|
||||
<kbd>a</kbd>: reset commit author
|
||||
<kbd>t</kbd>: 还原提交
|
||||
<kbd>T</kbd>: 标签提交
|
||||
<kbd>ctrl+l</kbd>: 打开日志菜单
|
||||
<kbd>space</kbd>: 检出提交
|
||||
<kbd><c-l></kbd>: 打开日志菜单
|
||||
<kbd><space></kbd>: 检出提交
|
||||
<kbd>y</kbd>: copy commit attribute
|
||||
<kbd>o</kbd>: 在浏览器中打开提交
|
||||
<kbd>n</kbd>: 从提交创建新分支
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>c</kbd>: 复制提交(拣选)
|
||||
<kbd>C</kbd>: 复制提交范围(拣选)
|
||||
<kbd>enter</kbd>: 查看提交的文件
|
||||
<kbd><enter></kbd>: 查看提交的文件
|
||||
</pre>
|
||||
|
||||
## 提交文件
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 将提交的文件名复制到剪贴板
|
||||
<kbd><c-o></kbd>: 将提交的文件名复制到剪贴板
|
||||
<kbd>c</kbd>: 检出文件
|
||||
<kbd>d</kbd>: 放弃对此文件的提交更改
|
||||
<kbd>o</kbd>: 打开文件
|
||||
<kbd>e</kbd>: 编辑文件
|
||||
<kbd>space</kbd>: 补丁中包含的切换文件
|
||||
<kbd><space></kbd>: 补丁中包含的切换文件
|
||||
<kbd>a</kbd>: toggle all files included in patch
|
||||
<kbd>enter</kbd>: 输入文件以将所选行添加到补丁中(或切换目录折叠)
|
||||
<kbd><enter></kbd>: 输入文件以将所选行添加到补丁中(或切换目录折叠)
|
||||
<kbd>`</kbd>: 切换文件树视图
|
||||
</pre>
|
||||
|
||||
## 提交讯息
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: 确认
|
||||
<kbd><esc></kbd>: 关闭
|
||||
</pre>
|
||||
|
||||
## 文件
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 将文件名复制到剪贴板
|
||||
<kbd><c-o></kbd>: 将文件名复制到剪贴板
|
||||
<kbd>d</kbd>: 查看'放弃更改'选项
|
||||
<kbd>space</kbd>: 切换暂存状态
|
||||
<kbd>ctrl+b</kbd>: Filter files (staged/unstaged)
|
||||
<kbd><space></kbd>: 切换暂存状态
|
||||
<kbd><c-b></kbd>: Filter files (staged/unstaged)
|
||||
<kbd>c</kbd>: 提交更改
|
||||
<kbd>w</kbd>: 提交更改而无需预先提交钩子
|
||||
<kbd>A</kbd>: 修补最后一次提交
|
||||
@@ -173,7 +182,7 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<kbd>s</kbd>: 将所有更改加入贮藏
|
||||
<kbd>S</kbd>: 查看贮藏选项
|
||||
<kbd>a</kbd>: 切换所有文件的暂存状态
|
||||
<kbd>enter</kbd>: 暂存单个 块/行 用于文件, 或 折叠/展开 目录
|
||||
<kbd><enter></kbd>: 暂存单个 块/行 用于文件, 或 折叠/展开 目录
|
||||
<kbd>g</kbd>: 查看上游重置选项
|
||||
<kbd>D</kbd>: 查看重置选项
|
||||
<kbd>`</kbd>: 切换文件树视图
|
||||
@@ -184,27 +193,27 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## 构建补丁中
|
||||
|
||||
<pre>
|
||||
<kbd>◀</kbd>: 选择上一个区块
|
||||
<kbd>▶</kbd>: 选择下一个区块
|
||||
<kbd><left></kbd>: 选择上一个区块
|
||||
<kbd><right></kbd>: 选择下一个区块
|
||||
<kbd>v</kbd>: 切换拖动选择
|
||||
<kbd>V</kbd>: 切换拖动选择
|
||||
<kbd>a</kbd>: 切换选择区块
|
||||
<kbd>ctrl+o</kbd>: 将选中文本复制到剪贴板
|
||||
<kbd><c-o></kbd>: 将选中文本复制到剪贴板
|
||||
<kbd>o</kbd>: 打开文件
|
||||
<kbd>e</kbd>: 编辑文件
|
||||
<kbd>space</kbd>: 添加/移除 行到补丁
|
||||
<kbd>esc</kbd>: 退出逐行模式
|
||||
<kbd><space></kbd>: 添加/移除 行到补丁
|
||||
<kbd><esc></kbd>: 退出逐行模式
|
||||
</pre>
|
||||
|
||||
## 标签页面
|
||||
|
||||
<pre>
|
||||
<kbd>space</kbd>: 检出
|
||||
<kbd><space></kbd>: 检出
|
||||
<kbd>d</kbd>: 删除标签
|
||||
<kbd>P</kbd>: 推送标签
|
||||
<kbd>n</kbd>: 创建标签
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>enter</kbd>: 查看提交
|
||||
<kbd><enter></kbd>: 查看提交
|
||||
</pre>
|
||||
|
||||
## 正在合并
|
||||
@@ -212,31 +221,31 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
<pre>
|
||||
<kbd>e</kbd>: 编辑文件
|
||||
<kbd>o</kbd>: 打开文件
|
||||
<kbd>◀</kbd>: 选择上一个冲突
|
||||
<kbd>▶</kbd>: 选择下一个冲突
|
||||
<kbd>▲</kbd>: 选择顶部块
|
||||
<kbd>▼</kbd>: 选择底部块
|
||||
<kbd><left></kbd>: 选择上一个冲突
|
||||
<kbd><right></kbd>: 选择下一个冲突
|
||||
<kbd><up></kbd>: 选择顶部块
|
||||
<kbd><down></kbd>: 选择底部块
|
||||
<kbd>z</kbd>: 撤销
|
||||
<kbd>M</kbd>: 打开外部合并工具 (git mergetool)
|
||||
<kbd>space</kbd>: 选中区块
|
||||
<kbd><space></kbd>: 选中区块
|
||||
<kbd>b</kbd>: 选中所有区块
|
||||
<kbd>esc</kbd>: 返回文件面板
|
||||
<kbd><esc></kbd>: 返回文件面板
|
||||
</pre>
|
||||
|
||||
## 正在暂存
|
||||
|
||||
<pre>
|
||||
<kbd>◀</kbd>: 选择上一个区块
|
||||
<kbd>▶</kbd>: 选择下一个区块
|
||||
<kbd><left></kbd>: 选择上一个区块
|
||||
<kbd><right></kbd>: 选择下一个区块
|
||||
<kbd>v</kbd>: 切换拖动选择
|
||||
<kbd>V</kbd>: 切换拖动选择
|
||||
<kbd>a</kbd>: 切换选择区块
|
||||
<kbd>ctrl+o</kbd>: 将选中文本复制到剪贴板
|
||||
<kbd><c-o></kbd>: 将选中文本复制到剪贴板
|
||||
<kbd>o</kbd>: 打开文件
|
||||
<kbd>e</kbd>: 编辑文件
|
||||
<kbd>esc</kbd>: 返回文件面板
|
||||
<kbd>tab</kbd>: 切换到其他面板
|
||||
<kbd>space</kbd>: 切换行暂存状态
|
||||
<kbd><esc></kbd>: 返回文件面板
|
||||
<kbd><tab></kbd>: 切换到其他面板
|
||||
<kbd><space></kbd>: 切换行暂存状态
|
||||
<kbd>d</kbd>: 取消变更 (git reset)
|
||||
<kbd>E</kbd>: edit hunk
|
||||
<kbd>c</kbd>: 提交更改
|
||||
@@ -247,44 +256,58 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
|
||||
## 正常
|
||||
|
||||
<pre>
|
||||
<kbd>mouse wheel ▼</kbd>: 向下滚动 (fn+up)
|
||||
<kbd>mouse wheel ▲</kbd>: 向上滚动 (fn+down)
|
||||
<kbd>mouse wheel down</kbd>: 向下滚动 (fn+up)
|
||||
<kbd>mouse wheel up</kbd>: 向上滚动 (fn+down)
|
||||
</pre>
|
||||
|
||||
## 状态
|
||||
|
||||
<pre>
|
||||
<kbd>e</kbd>: 编辑配置文件
|
||||
<kbd>o</kbd>: 打开配置文件
|
||||
<kbd>e</kbd>: 编辑配置文件
|
||||
<kbd>u</kbd>: 检查更新
|
||||
<kbd>enter</kbd>: 切换到最近的仓库
|
||||
<kbd><enter></kbd>: 切换到最近的仓库
|
||||
<kbd>a</kbd>: 显示所有分支的日志
|
||||
</pre>
|
||||
|
||||
## 确认面板
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: 确认
|
||||
<kbd><esc></kbd>: 关闭
|
||||
</pre>
|
||||
|
||||
## 菜单
|
||||
|
||||
<pre>
|
||||
<kbd><enter></kbd>: 执行
|
||||
<kbd><esc></kbd>: 关闭
|
||||
</pre>
|
||||
|
||||
## 贮藏
|
||||
|
||||
<pre>
|
||||
<kbd>space</kbd>: 应用
|
||||
<kbd><space></kbd>: 应用
|
||||
<kbd>g</kbd>: 应用并删除
|
||||
<kbd>d</kbd>: 删除
|
||||
<kbd>n</kbd>: 新分支
|
||||
<kbd>r</kbd>: rename stash
|
||||
<kbd>enter</kbd>: 查看提交的文件
|
||||
<kbd><enter></kbd>: 查看提交的文件
|
||||
</pre>
|
||||
|
||||
## 远程分支
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: 将分支名称复制到剪贴板
|
||||
<kbd>space</kbd>: 检出
|
||||
<kbd><c-o></kbd>: 将分支名称复制到剪贴板
|
||||
<kbd><space></kbd>: 检出
|
||||
<kbd>n</kbd>: 新分支
|
||||
<kbd>M</kbd>: 合并到当前检出的分支
|
||||
<kbd>r</kbd>: 将已检出的分支变基到该分支
|
||||
<kbd>d</kbd>: 删除分支
|
||||
<kbd>u</kbd>: 设置为检出分支的上游
|
||||
<kbd>esc</kbd>: 返回远程仓库列表
|
||||
<kbd><esc></kbd>: 返回远程仓库列表
|
||||
<kbd>g</kbd>: 查看重置选项
|
||||
<kbd>enter</kbd>: 查看提交
|
||||
<kbd><enter></kbd>: 查看提交
|
||||
</pre>
|
||||
|
||||
## 远程页面
|
||||
|
||||
+2
-4
@@ -33,7 +33,6 @@ type App struct {
|
||||
Config config.AppConfigurer
|
||||
OSCommand *oscommands.OSCommand
|
||||
Gui *gui.Gui
|
||||
Updater *updates.Updater // may only need this on the Gui
|
||||
}
|
||||
|
||||
func Run(
|
||||
@@ -87,8 +86,7 @@ func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
|
||||
|
||||
app.OSCommand = oscommands.NewOSCommand(common, config, oscommands.GetPlatform(), oscommands.NewNullGuiIO(app.Log))
|
||||
|
||||
var err error
|
||||
app.Updater, err = updates.NewUpdater(common, config, app.OSCommand)
|
||||
updater, err := updates.NewUpdater(common, config, app.OSCommand)
|
||||
if err != nil {
|
||||
return app, err
|
||||
}
|
||||
@@ -108,7 +106,7 @@ func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
|
||||
return app, err
|
||||
}
|
||||
|
||||
app.Gui, err = gui.NewGui(common, config, gitVersion, app.Updater, showRecentRepos, dirName)
|
||||
app.Gui, err = gui.NewGui(common, config, gitVersion, updater, showRecentRepos, dirName)
|
||||
if err != nil {
|
||||
return app, err
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/maps"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
@@ -183,6 +184,8 @@ func getHeader(binding *types.Binding, tr *i18n.TranslationSet) header {
|
||||
func formatSections(tr *i18n.TranslationSet, bindingSections []*bindingSection) string {
|
||||
content := fmt.Sprintf("# Lazygit %s\n", tr.Keybindings)
|
||||
|
||||
content += fmt.Sprintf("\n%s\n", italicize(tr.KeybindingsLegend))
|
||||
|
||||
for _, section := range bindingSections {
|
||||
content += formatTitle(section.title)
|
||||
content += "<pre>\n"
|
||||
@@ -200,13 +203,21 @@ func formatTitle(title string) string {
|
||||
}
|
||||
|
||||
func formatBinding(binding *types.Binding) string {
|
||||
result := fmt.Sprintf(" <kbd>%s</kbd>: %s", escapeAngleBrackets(keybindings.LabelFromKey(binding.Key)), binding.Description)
|
||||
if binding.Alternative != "" {
|
||||
return fmt.Sprintf(
|
||||
" <kbd>%s</kbd>: %s (%s)\n",
|
||||
keybindings.LabelFromKey(binding.Key),
|
||||
binding.Description,
|
||||
binding.Alternative,
|
||||
)
|
||||
result += fmt.Sprintf(" (%s)", binding.Alternative)
|
||||
}
|
||||
return fmt.Sprintf(" <kbd>%s</kbd>: %s\n", keybindings.LabelFromKey(binding.Key), binding.Description)
|
||||
result += "\n"
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func escapeAngleBrackets(str string) string {
|
||||
result := strings.ReplaceAll(str, ">", ">")
|
||||
result = strings.ReplaceAll(result, "<", "<")
|
||||
return result
|
||||
}
|
||||
|
||||
func italicize(str string) string {
|
||||
return fmt.Sprintf("_%s_", str)
|
||||
}
|
||||
|
||||
+2
-3
@@ -117,8 +117,7 @@ func NewGitCommandAux(
|
||||
workingTreeCommands := git_commands.NewWorkingTreeCommands(gitCommon, submoduleCommands, fileLoader)
|
||||
rebaseCommands := git_commands.NewRebaseCommands(gitCommon, commitCommands, workingTreeCommands)
|
||||
stashCommands := git_commands.NewStashCommands(gitCommon, fileLoader, workingTreeCommands)
|
||||
// TODO: have patch builder take workingTreeCommands in its entirety
|
||||
patchBuilder := patch.NewPatchBuilder(cmn.Log, workingTreeCommands.ApplyPatch,
|
||||
patchBuilder := patch.NewPatchBuilder(cmn.Log,
|
||||
func(from string, to string, reverse bool, filename string, plain bool) (string, error) {
|
||||
// TODO: make patch builder take Gui.IgnoreWhitespaceInDiffView into
|
||||
// account. For now we just pass false.
|
||||
@@ -129,7 +128,7 @@ func NewGitCommandAux(
|
||||
|
||||
branchLoader := git_commands.NewBranchLoader(cmn, branchCommands.GetRawBranches, branchCommands.CurrentBranchInfo, configCommands)
|
||||
commitFileLoader := git_commands.NewCommitFileLoader(cmn, cmd)
|
||||
commitLoader := git_commands.NewCommitLoader(cmn, cmd, dotGitDir, branchCommands.CurrentBranchInfo, statusCommands.RebaseMode)
|
||||
commitLoader := git_commands.NewCommitLoader(cmn, cmd, dotGitDir, statusCommands.RebaseMode)
|
||||
reflogCommitLoader := git_commands.NewReflogCommitLoader(cmn, cmd)
|
||||
remoteLoader := git_commands.NewRemoteLoader(cmn, cmd, repo.Remotes)
|
||||
stashLoader := git_commands.NewStashLoader(cmn, cmd)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -98,13 +97,15 @@ func (self *BisectCommands) GetInfo() *BisectInfo {
|
||||
}
|
||||
|
||||
func (self *BisectCommands) Reset() error {
|
||||
return self.cmd.New("git bisect reset").StreamOutput().Run()
|
||||
cmdStr := NewGitCmd("bisect").Arg("reset").ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).StreamOutput().Run()
|
||||
}
|
||||
|
||||
func (self *BisectCommands) Mark(ref string, term string) error {
|
||||
return self.cmd.New(
|
||||
fmt.Sprintf("git bisect %s %s", term, ref),
|
||||
).
|
||||
cmdStr := NewGitCmd("bisect").Arg(term, ref).ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).
|
||||
IgnoreEmptyError().
|
||||
StreamOutput().
|
||||
Run()
|
||||
@@ -115,7 +116,9 @@ func (self *BisectCommands) Skip(ref string) error {
|
||||
}
|
||||
|
||||
func (self *BisectCommands) Start() error {
|
||||
return self.cmd.New("git bisect start").StreamOutput().Run()
|
||||
cmdStr := NewGitCmd("bisect").Arg("start").ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).StreamOutput().Run()
|
||||
}
|
||||
|
||||
// tells us whether we've found our problem commit(s). We return a string slice of
|
||||
@@ -137,7 +140,8 @@ func (self *BisectCommands) IsDone() (bool, []string, error) {
|
||||
done := false
|
||||
candidates := []string{}
|
||||
|
||||
err := self.cmd.New(fmt.Sprintf("git rev-list %s", newSha)).RunAndProcessLines(func(line string) (bool, error) {
|
||||
cmdStr := NewGitCmd("rev-list").Arg(newSha).ToString()
|
||||
err := self.cmd.New(cmdStr).RunAndProcessLines(func(line string) (bool, error) {
|
||||
sha := strings.TrimSpace(line)
|
||||
|
||||
if status, ok := info.statusMap[sha]; ok {
|
||||
@@ -167,9 +171,11 @@ func (self *BisectCommands) IsDone() (bool, []string, error) {
|
||||
// bisecting is actually a descendant of our current bisect commit. If it's not, we need to
|
||||
// render the commits from the bad commit.
|
||||
func (self *BisectCommands) ReachableFromStart(bisectInfo *BisectInfo) bool {
|
||||
err := self.cmd.New(
|
||||
fmt.Sprintf("git merge-base --is-ancestor %s %s", bisectInfo.GetNewSha(), bisectInfo.GetStartSha()),
|
||||
).DontLog().Run()
|
||||
cmdStr := NewGitCmd("merge-base").
|
||||
Arg("--is-ancestor", bisectInfo.GetNewSha(), bisectInfo.GetStartSha()).
|
||||
ToString()
|
||||
|
||||
err := self.cmd.New(cmdStr).DontLog().Run()
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
@@ -20,12 +20,20 @@ func NewBranchCommands(gitCommon *GitCommon) *BranchCommands {
|
||||
|
||||
// New creates a new branch
|
||||
func (self *BranchCommands) New(name string, base string) error {
|
||||
return self.cmd.New(fmt.Sprintf("git checkout -b %s %s", self.cmd.Quote(name), self.cmd.Quote(base))).Run()
|
||||
cmdStr := NewGitCmd("checkout").
|
||||
Arg("-b", self.cmd.Quote(name), self.cmd.Quote(base)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// CurrentBranchInfo get the current branch information.
|
||||
func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
|
||||
branchName, err := self.cmd.New("git symbolic-ref --short HEAD").DontLog().RunWithOutput()
|
||||
branchName, err := self.cmd.New(
|
||||
NewGitCmd("symbolic-ref").
|
||||
Arg("--short", "HEAD").
|
||||
ToString(),
|
||||
).DontLog().RunWithOutput()
|
||||
if err == nil && branchName != "HEAD\n" {
|
||||
trimmedBranchName := strings.TrimSpace(branchName)
|
||||
return BranchInfo{
|
||||
@@ -34,7 +42,11 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
|
||||
DetachedHead: false,
|
||||
}, nil
|
||||
}
|
||||
output, err := self.cmd.New(`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`).DontLog().RunWithOutput()
|
||||
output, err := self.cmd.New(
|
||||
NewGitCmd("branch").
|
||||
Arg("--points-at=HEAD", "--format=\"%(HEAD)%00%(objectname)%00%(refname)\"").
|
||||
ToString(),
|
||||
).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return BranchInfo{}, err
|
||||
}
|
||||
@@ -59,13 +71,12 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
|
||||
|
||||
// Delete delete branch
|
||||
func (self *BranchCommands) Delete(branch string, force bool) error {
|
||||
command := "git branch -d"
|
||||
cmdStr := NewGitCmd("branch").
|
||||
ArgIfElse(force, "-D", "-d").
|
||||
Arg(self.cmd.Quote(branch)).
|
||||
ToString()
|
||||
|
||||
if force {
|
||||
command = "git branch -D"
|
||||
}
|
||||
|
||||
return self.cmd.New(fmt.Sprintf("%s %s", command, self.cmd.Quote(branch))).Run()
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// Checkout checks out a branch (or commit), with --force if you set the force arg to true
|
||||
@@ -75,12 +86,12 @@ type CheckoutOptions struct {
|
||||
}
|
||||
|
||||
func (self *BranchCommands) Checkout(branch string, options CheckoutOptions) error {
|
||||
forceArg := ""
|
||||
if options.Force {
|
||||
forceArg = " --force"
|
||||
}
|
||||
cmdStr := NewGitCmd("checkout").
|
||||
ArgIf(options.Force, "--force").
|
||||
Arg(self.cmd.Quote(branch)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(fmt.Sprintf("git checkout%s %s", forceArg, self.cmd.Quote(branch))).
|
||||
return self.cmd.New(cmdStr).
|
||||
// prevents git from prompting us for input which would freeze the program
|
||||
// TODO: see if this is actually needed here
|
||||
AddEnvVars("GIT_TERMINAL_PROMPT=0").
|
||||
@@ -104,15 +115,27 @@ func (self *BranchCommands) GetGraphCmdObj(branchName string) oscommands.ICmdObj
|
||||
}
|
||||
|
||||
func (self *BranchCommands) SetCurrentBranchUpstream(remoteName string, remoteBranchName string) error {
|
||||
return self.cmd.New(fmt.Sprintf("git branch --set-upstream-to=%s/%s", self.cmd.Quote(remoteName), self.cmd.Quote(remoteBranchName))).Run()
|
||||
cmdStr := NewGitCmd("branch").
|
||||
Arg(fmt.Sprintf("--set-upstream-to=%s/%s", self.cmd.Quote(remoteName), self.cmd.Quote(remoteBranchName))).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *BranchCommands) SetUpstream(remoteName string, remoteBranchName string, branchName string) error {
|
||||
return self.cmd.New(fmt.Sprintf("git branch --set-upstream-to=%s/%s %s", self.cmd.Quote(remoteName), self.cmd.Quote(remoteBranchName), self.cmd.Quote(branchName))).Run()
|
||||
cmdStr := NewGitCmd("branch").
|
||||
Arg(fmt.Sprintf("--set-upstream-to=%s/%s", self.cmd.Quote(remoteName), self.cmd.Quote(remoteBranchName))).
|
||||
Arg(self.cmd.Quote(branchName)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *BranchCommands) UnsetUpstream(branchName string) error {
|
||||
return self.cmd.New(fmt.Sprintf("git branch --unset-upstream %s", self.cmd.Quote(branchName))).Run()
|
||||
cmdStr := NewGitCmd("branch").Arg("--unset-upstream", self.cmd.Quote(branchName)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *BranchCommands) GetCurrentBranchUpstreamDifferenceCount() (string, string) {
|
||||
@@ -126,29 +149,49 @@ func (self *BranchCommands) GetUpstreamDifferenceCount(branchName string) (strin
|
||||
// GetCommitDifferences checks how many pushables/pullables there are for the
|
||||
// current branch
|
||||
func (self *BranchCommands) GetCommitDifferences(from, to string) (string, string) {
|
||||
command := "git rev-list %s..%s --count"
|
||||
pushableCount, err := self.cmd.New(fmt.Sprintf(command, to, from)).DontLog().RunWithOutput()
|
||||
pushableCount, err := self.countDifferences(to, from)
|
||||
if err != nil {
|
||||
return "?", "?"
|
||||
}
|
||||
pullableCount, err := self.cmd.New(fmt.Sprintf(command, from, to)).DontLog().RunWithOutput()
|
||||
pullableCount, err := self.countDifferences(from, to)
|
||||
if err != nil {
|
||||
return "?", "?"
|
||||
}
|
||||
return strings.TrimSpace(pushableCount), strings.TrimSpace(pullableCount)
|
||||
}
|
||||
|
||||
func (self *BranchCommands) countDifferences(from, to string) (string, error) {
|
||||
cmdStr := NewGitCmd("rev-list").
|
||||
Arg(fmt.Sprintf("%s..%s", from, to)).
|
||||
Arg("--count").
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
}
|
||||
|
||||
func (self *BranchCommands) IsHeadDetached() bool {
|
||||
err := self.cmd.New("git symbolic-ref -q HEAD").DontLog().Run()
|
||||
cmdStr := NewGitCmd("symbolic-ref").Arg("-q", "HEAD").ToString()
|
||||
|
||||
err := self.cmd.New(cmdStr).DontLog().Run()
|
||||
return err != nil
|
||||
}
|
||||
|
||||
func (self *BranchCommands) Rename(oldName string, newName string) error {
|
||||
return self.cmd.New(fmt.Sprintf("git branch --move %s %s", self.cmd.Quote(oldName), self.cmd.Quote(newName))).Run()
|
||||
cmdStr := NewGitCmd("branch").
|
||||
Arg("--move", self.cmd.Quote(oldName), self.cmd.Quote(newName)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *BranchCommands) GetRawBranches() (string, error) {
|
||||
return self.cmd.New(`git for-each-ref --sort=-committerdate --format="%(HEAD)%00%(refname:short)%00%(upstream:short)%00%(upstream:track)" refs/heads`).DontLog().RunWithOutput()
|
||||
cmdStr := NewGitCmd("for-each-ref").
|
||||
Arg("--sort=-committerdate").
|
||||
Arg(`--format="%(HEAD)%00%(refname:short)%00%(upstream:short)%00%(upstream:track)"`).
|
||||
Arg("refs/heads").
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
}
|
||||
|
||||
type MergeOpts struct {
|
||||
@@ -156,15 +199,12 @@ type MergeOpts struct {
|
||||
}
|
||||
|
||||
func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
|
||||
mergeArg := ""
|
||||
if self.UserConfig.Git.Merging.Args != "" {
|
||||
mergeArg = " " + self.UserConfig.Git.Merging.Args
|
||||
}
|
||||
|
||||
command := fmt.Sprintf("git merge --no-edit%s %s", mergeArg, self.cmd.Quote(branchName))
|
||||
if opts.FastForwardOnly {
|
||||
command = fmt.Sprintf("%s --ff-only", command)
|
||||
}
|
||||
command := NewGitCmd("merge").
|
||||
Arg("--no-edit").
|
||||
ArgIf(self.UserConfig.Git.Merging.Args != "", self.UserConfig.Git.Merging.Args).
|
||||
ArgIf(opts.FastForwardOnly, "--ff-only").
|
||||
Arg(self.cmd.Quote(branchName)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(command).Run()
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -99,12 +100,53 @@ func TestBranchDeleteBranch(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestBranchMerge(t *testing.T) {
|
||||
runner := oscommands.NewFakeRunner(t).
|
||||
Expect(`git merge --no-edit "test"`, "", nil)
|
||||
instance := buildBranchCommands(commonDeps{runner: runner})
|
||||
scenarios := []struct {
|
||||
testName string
|
||||
userConfig *config.UserConfig
|
||||
opts MergeOpts
|
||||
branchName string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
testName: "basic",
|
||||
userConfig: &config.UserConfig{},
|
||||
opts: MergeOpts{},
|
||||
branchName: "mybranch",
|
||||
expected: `git merge --no-edit "mybranch"`,
|
||||
},
|
||||
{
|
||||
testName: "merging args",
|
||||
userConfig: &config.UserConfig{
|
||||
Git: config.GitConfig{
|
||||
Merging: config.MergingConfig{
|
||||
Args: "--merging-args", // it's up to the user what they put here
|
||||
},
|
||||
},
|
||||
},
|
||||
opts: MergeOpts{},
|
||||
branchName: "mybranch",
|
||||
expected: `git merge --no-edit --merging-args "mybranch"`,
|
||||
},
|
||||
{
|
||||
testName: "fast forward only",
|
||||
userConfig: &config.UserConfig{},
|
||||
opts: MergeOpts{FastForwardOnly: true},
|
||||
branchName: "mybranch",
|
||||
expected: `git merge --no-edit --ff-only "mybranch"`,
|
||||
},
|
||||
}
|
||||
|
||||
assert.NoError(t, instance.Merge("test", MergeOpts{}))
|
||||
runner.CheckForMissingCalls()
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
runner := oscommands.NewFakeRunner(t).
|
||||
Expect(s.expected, "", nil)
|
||||
instance := buildBranchCommands(commonDeps{runner: runner, userConfig: s.userConfig})
|
||||
|
||||
assert.NoError(t, instance.Merge(s.branchName, s.opts))
|
||||
runner.CheckForMissingCalls()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBranchCheckout(t *testing.T) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package git_commands
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
@@ -22,18 +23,27 @@ func NewCommitCommands(gitCommon *GitCommon) *CommitCommands {
|
||||
|
||||
// ResetAuthor resets the author of the topmost commit
|
||||
func (self *CommitCommands) ResetAuthor() error {
|
||||
return self.cmd.New("git commit --allow-empty --only --no-edit --amend --reset-author").Run()
|
||||
cmdStr := NewGitCmd("commit").
|
||||
Arg("--allow-empty", "--only", "--no-edit", "--amend", "--reset-author").
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// Sets the commit's author to the supplied value. Value is expected to be of the form 'Name <Email>'
|
||||
func (self *CommitCommands) SetAuthor(value string) error {
|
||||
commandStr := fmt.Sprintf("git commit --allow-empty --only --no-edit --amend --author=%s", self.cmd.Quote(value))
|
||||
return self.cmd.New(commandStr).Run()
|
||||
cmdStr := NewGitCmd("commit").
|
||||
Arg("--allow-empty", "--only", "--no-edit", "--amend", "--author="+self.cmd.Quote(value)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// ResetToCommit reset to commit
|
||||
func (self *CommitCommands) ResetToCommit(sha string, strength string, envVars []string) error {
|
||||
return self.cmd.New(fmt.Sprintf("git reset --%s %s", strength, sha)).
|
||||
cmdStr := NewGitCmd("reset").Arg("--"+strength, sha).ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).
|
||||
// prevents git from prompting us for input which would freeze the program
|
||||
// TODO: see if this is actually needed here
|
||||
AddEnvVars("GIT_TERMINAL_PROMPT=0").
|
||||
@@ -45,38 +55,52 @@ func (self *CommitCommands) CommitCmdObj(message string) oscommands.ICmdObj {
|
||||
messageArgs := self.commitMessageArgs(message)
|
||||
|
||||
skipHookPrefix := self.UserConfig.Git.SkipHookPrefix
|
||||
noVerifyFlag := ""
|
||||
if skipHookPrefix != "" && strings.HasPrefix(message, skipHookPrefix) {
|
||||
noVerifyFlag = " --no-verify"
|
||||
}
|
||||
|
||||
return self.cmd.New(fmt.Sprintf("git commit%s%s%s", noVerifyFlag, self.signoffFlag(), messageArgs))
|
||||
cmdStr := NewGitCmd("commit").
|
||||
ArgIf(skipHookPrefix != "" && strings.HasPrefix(message, skipHookPrefix), "--no-verify").
|
||||
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
|
||||
Arg(messageArgs...).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr)
|
||||
}
|
||||
|
||||
// RewordLastCommit rewords the topmost commit with the given message
|
||||
func (self *CommitCommands) RewordLastCommit(message string) error {
|
||||
messageArgs := self.commitMessageArgs(message)
|
||||
return self.cmd.New(fmt.Sprintf("git commit --allow-empty --amend --only%s", messageArgs)).Run()
|
||||
|
||||
cmdStr := NewGitCmd("commit").
|
||||
Arg("--allow-empty", "--amend", "--only").
|
||||
Arg(messageArgs...).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *CommitCommands) commitMessageArgs(message string) string {
|
||||
func (self *CommitCommands) commitMessageArgs(message string) []string {
|
||||
msg, description, _ := strings.Cut(message, "\n")
|
||||
descriptionArgs := ""
|
||||
args := []string{"-m", self.cmd.Quote(msg)}
|
||||
|
||||
if description != "" {
|
||||
descriptionArgs = fmt.Sprintf(" -m %s", self.cmd.Quote(description))
|
||||
args = append(args, "-m", self.cmd.Quote(description))
|
||||
}
|
||||
|
||||
return fmt.Sprintf(" -m %s%s", self.cmd.Quote(msg), descriptionArgs)
|
||||
return args
|
||||
}
|
||||
|
||||
// runs git commit without the -m argument meaning it will invoke the user's editor
|
||||
func (self *CommitCommands) CommitEditorCmdObj() oscommands.ICmdObj {
|
||||
return self.cmd.New(fmt.Sprintf("git commit%s%s", self.signoffFlag(), self.verboseFlag()))
|
||||
cmdStr := NewGitCmd("commit").
|
||||
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
|
||||
ArgIf(self.verboseFlag() != "", self.verboseFlag()).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr)
|
||||
}
|
||||
|
||||
func (self *CommitCommands) signoffFlag() string {
|
||||
if self.UserConfig.Git.Commit.SignOff {
|
||||
return " --signoff"
|
||||
return "--signoff"
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
@@ -85,9 +109,9 @@ func (self *CommitCommands) signoffFlag() string {
|
||||
func (self *CommitCommands) verboseFlag() string {
|
||||
switch self.config.UserConfig.Git.Commit.Verbose {
|
||||
case "always":
|
||||
return " --verbose"
|
||||
return "--verbose"
|
||||
case "never":
|
||||
return " --no-verbose"
|
||||
return "--no-verbose"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
@@ -95,19 +119,25 @@ func (self *CommitCommands) verboseFlag() string {
|
||||
|
||||
// Get the subject of the HEAD commit
|
||||
func (self *CommitCommands) GetHeadCommitMessage() (string, error) {
|
||||
message, err := self.cmd.New("git log -1 --pretty=%s").DontLog().RunWithOutput()
|
||||
cmdStr := NewGitCmd("log").Arg("-1", "--pretty=%s").ToString()
|
||||
|
||||
message, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
return strings.TrimSpace(message), err
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitMessage(commitSha string) (string, error) {
|
||||
cmdStr := "git rev-list --format=%B --max-count=1 " + commitSha
|
||||
cmdStr := NewGitCmd("rev-list").
|
||||
Arg("--format=%B", "--max-count=1", commitSha).
|
||||
ToString()
|
||||
|
||||
messageWithHeader, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
message := strings.Join(strings.SplitAfter(messageWithHeader, "\n")[1:], "")
|
||||
return strings.TrimSpace(message), err
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitDiff(commitSha string) (string, error) {
|
||||
cmdStr := "git show --no-color " + commitSha
|
||||
cmdStr := NewGitCmd("show").Arg("--no-color", commitSha).ToString()
|
||||
|
||||
diff, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
return diff, err
|
||||
}
|
||||
@@ -118,7 +148,10 @@ type Author struct {
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitAuthor(commitSha string) (Author, error) {
|
||||
cmdStr := "git show --no-patch --pretty=format:'%an%x00%ae' " + commitSha
|
||||
cmdStr := NewGitCmd("show").
|
||||
Arg("--no-patch", "--pretty=format:'%an%x00%ae'", commitSha).
|
||||
ToString()
|
||||
|
||||
output, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return Author{}, err
|
||||
@@ -138,15 +171,21 @@ func (self *CommitCommands) GetCommitMessageFirstLine(sha string) (string, error
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitMessagesFirstLine(shas []string) (string, error) {
|
||||
return self.cmd.New(
|
||||
fmt.Sprintf("git show --no-patch --pretty=format:%%s %s", strings.Join(shas, " ")),
|
||||
).DontLog().RunWithOutput()
|
||||
cmdStr := NewGitCmd("show").
|
||||
Arg("--no-patch", "--pretty=format:%s").
|
||||
Arg(shas...).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
}
|
||||
|
||||
func (self *CommitCommands) GetCommitsOneline(shas []string) (string, error) {
|
||||
return self.cmd.New(
|
||||
fmt.Sprintf("git show --no-patch --oneline %s", strings.Join(shas, " ")),
|
||||
).DontLog().RunWithOutput()
|
||||
cmdStr := NewGitCmd("show").
|
||||
Arg("--no-patch", "--oneline").
|
||||
Arg(shas...).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
}
|
||||
|
||||
// AmendHead amends HEAD with whatever is staged in your working tree
|
||||
@@ -155,45 +194,76 @@ func (self *CommitCommands) AmendHead() error {
|
||||
}
|
||||
|
||||
func (self *CommitCommands) AmendHeadCmdObj() oscommands.ICmdObj {
|
||||
return self.cmd.New("git commit --amend --no-edit --allow-empty")
|
||||
cmdStr := NewGitCmd("commit").
|
||||
Arg("--amend", "--no-edit", "--allow-empty").
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr)
|
||||
}
|
||||
|
||||
func (self *CommitCommands) ShowCmdObj(sha string, filterPath string, ignoreWhitespace bool) oscommands.ICmdObj {
|
||||
contextSize := self.UserConfig.Git.DiffContextSize
|
||||
filterPathArg := ""
|
||||
if filterPath != "" {
|
||||
filterPathArg = fmt.Sprintf(" -- %s", self.cmd.Quote(filterPath))
|
||||
}
|
||||
ignoreWhitespaceArg := ""
|
||||
if ignoreWhitespace {
|
||||
ignoreWhitespaceArg = " --ignore-all-space"
|
||||
}
|
||||
|
||||
cmdStr := fmt.Sprintf("git show --submodule --color=%s --unified=%d --stat -p %s%s%s",
|
||||
self.UserConfig.Git.Paging.ColorArg, contextSize, sha, ignoreWhitespaceArg, filterPathArg)
|
||||
cmdStr := NewGitCmd("show").
|
||||
Arg("--submodule").
|
||||
Arg("--color="+self.UserConfig.Git.Paging.ColorArg).
|
||||
Arg(fmt.Sprintf("--unified=%d", contextSize)).
|
||||
Arg("--stat").
|
||||
Arg("-p").
|
||||
Arg(sha).
|
||||
ArgIf(ignoreWhitespace, "--ignore-all-space").
|
||||
ArgIf(filterPath != "", "--", self.cmd.Quote(filterPath)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).DontLog()
|
||||
}
|
||||
|
||||
// Revert reverts the selected commit by sha
|
||||
func (self *CommitCommands) Revert(sha string) error {
|
||||
return self.cmd.New(fmt.Sprintf("git revert %s", sha)).Run()
|
||||
cmdStr := NewGitCmd("revert").Arg(sha).ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *CommitCommands) RevertMerge(sha string, parentNumber int) error {
|
||||
return self.cmd.New(fmt.Sprintf("git revert %s -m %d", sha, parentNumber)).Run()
|
||||
cmdStr := NewGitCmd("revert").Arg(sha, "-m", fmt.Sprintf("%d", parentNumber)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// CreateFixupCommit creates a commit that fixes up a previous commit
|
||||
func (self *CommitCommands) CreateFixupCommit(sha string) error {
|
||||
return self.cmd.New(fmt.Sprintf("git commit --fixup=%s", sha)).Run()
|
||||
cmdStr := NewGitCmd("commit").Arg("--fixup=" + sha).ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// a value of 0 means the head commit, 1 is the parent commit, etc
|
||||
func (self *CommitCommands) GetCommitMessageFromHistory(value int) (string, error) {
|
||||
hash, _ := self.cmd.New(fmt.Sprintf("git log -1 --skip=%d --pretty=%%H", value)).DontLog().RunWithOutput()
|
||||
cmdStr := NewGitCmd("log").Arg("-1", fmt.Sprintf("--skip=%d", value), "--pretty=%H").
|
||||
ToString()
|
||||
|
||||
hash, _ := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
formattedHash := strings.TrimSpace(hash)
|
||||
if len(formattedHash) == 0 {
|
||||
return "", ErrInvalidCommitIndex
|
||||
}
|
||||
return self.GetCommitMessage(formattedHash)
|
||||
}
|
||||
|
||||
// Returns hashes of recent commits which changed the given file
|
||||
// Note: This does not look for the last X commits to change a file, instead
|
||||
// it looks among the last X commits and see which of them happened to have changed the file.
|
||||
// This is more efficient.
|
||||
func (self *CommitCommands) GetRecentCommitsWhichChangedFile(path string) []string {
|
||||
t := time.Now()
|
||||
// Checking last X commits. Funnily this seems to actually consider more than the last
|
||||
// X, perhaps because of topological sorting.
|
||||
cmdStr := NewGitCmd("log").Arg("HEAD~50..HEAD", "--pretty=%H", "--", self.cmd.Quote(path)).
|
||||
ToString()
|
||||
|
||||
hashes, _ := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
self.Log.Warn(fmt.Sprintf("GetRecentCommitsWhichChangedFile took %s", time.Since(t)))
|
||||
return strings.Split(strings.TrimSpace(hashes), "\n")
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
@@ -25,12 +24,18 @@ func NewCommitFileLoader(common *common.Common, cmd oscommands.ICmdObjBuilder) *
|
||||
|
||||
// GetFilesInDiff get the specified commit files
|
||||
func (self *CommitFileLoader) GetFilesInDiff(from string, to string, reverse bool) ([]*models.CommitFile, error) {
|
||||
reverseFlag := ""
|
||||
if reverse {
|
||||
reverseFlag = " -R "
|
||||
}
|
||||
cmdStr := NewGitCmd("diff").
|
||||
Arg("--submodule").
|
||||
Arg("--no-ext-diff").
|
||||
Arg("--name-status").
|
||||
Arg("-z").
|
||||
Arg("--no-renames").
|
||||
ArgIf(reverse, "-R").
|
||||
Arg(from).
|
||||
Arg(to).
|
||||
ToString()
|
||||
|
||||
filenames, err := self.cmd.New(fmt.Sprintf("git diff --submodule --no-ext-diff --name-status -z --no-renames %s %s %s", reverseFlag, from, to)).DontLog().RunWithOutput()
|
||||
filenames, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
// context:
|
||||
@@ -28,11 +29,14 @@ type CommitLoader struct {
|
||||
*common.Common
|
||||
cmd oscommands.ICmdObjBuilder
|
||||
|
||||
getCurrentBranchInfo func() (BranchInfo, error)
|
||||
getRebaseMode func() (enums.RebaseMode, error)
|
||||
readFile func(filename string) ([]byte, error)
|
||||
walkFiles func(root string, fn filepath.WalkFunc) error
|
||||
dotGitDir string
|
||||
getRebaseMode func() (enums.RebaseMode, error)
|
||||
readFile func(filename string) ([]byte, error)
|
||||
walkFiles func(root string, fn filepath.WalkFunc) error
|
||||
dotGitDir string
|
||||
// List of main branches that exist in the repo, quoted for direct use in a git command.
|
||||
// We use these to obtain the merge base of the branch.
|
||||
// When nil, we're yet to obtain the list of main branches.
|
||||
quotedMainBranches *string
|
||||
}
|
||||
|
||||
// making our dependencies explicit for the sake of easier testing
|
||||
@@ -40,17 +44,16 @@ func NewCommitLoader(
|
||||
cmn *common.Common,
|
||||
cmd oscommands.ICmdObjBuilder,
|
||||
dotGitDir string,
|
||||
getCurrentBranchInfo func() (BranchInfo, error),
|
||||
getRebaseMode func() (enums.RebaseMode, error),
|
||||
) *CommitLoader {
|
||||
return &CommitLoader{
|
||||
Common: cmn,
|
||||
cmd: cmd,
|
||||
getCurrentBranchInfo: getCurrentBranchInfo,
|
||||
getRebaseMode: getRebaseMode,
|
||||
readFile: os.ReadFile,
|
||||
walkFiles: filepath.Walk,
|
||||
dotGitDir: dotGitDir,
|
||||
Common: cmn,
|
||||
cmd: cmd,
|
||||
getRebaseMode: getRebaseMode,
|
||||
readFile: os.ReadFile,
|
||||
walkFiles: filepath.Walk,
|
||||
dotGitDir: dotGitDir,
|
||||
quotedMainBranches: nil,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,10 +104,7 @@ func (self *CommitLoader) GetCommits(opts GetCommitsOptions) ([]*models.Commit,
|
||||
return commits, nil
|
||||
}
|
||||
|
||||
commits, err = self.setCommitMergedStatuses(opts.RefName, commits)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commits = self.setCommitMergedStatuses(opts.RefName, commits)
|
||||
|
||||
return commits, nil
|
||||
}
|
||||
@@ -201,12 +201,11 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
|
||||
// note that we're not filtering these as we do non-rebasing commits just because
|
||||
// I suspect that will cause some damage
|
||||
cmdObj := self.cmd.New(
|
||||
fmt.Sprintf(
|
||||
"git -c log.showSignature=false show %s --no-patch --oneline %s --abbrev=%d",
|
||||
strings.Join(commitShas, " "),
|
||||
prettyFormat,
|
||||
20,
|
||||
),
|
||||
NewGitCmd("show").
|
||||
Config("log.showSignature=false").
|
||||
Arg("--no-patch", "--oneline", "--abbrev=20", prettyFormat).
|
||||
Arg(commitShas...).
|
||||
ToString(),
|
||||
).DontLog()
|
||||
|
||||
fullCommits := map[string]*models.Commit{}
|
||||
@@ -344,43 +343,63 @@ func (self *CommitLoader) commitFromPatch(content string) *models.Commit {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CommitLoader) setCommitMergedStatuses(refName string, commits []*models.Commit) ([]*models.Commit, error) {
|
||||
ancestor, err := self.getMergeBase(refName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (self *CommitLoader) setCommitMergedStatuses(refName string, commits []*models.Commit) []*models.Commit {
|
||||
ancestor := self.getMergeBase(refName)
|
||||
if ancestor == "" {
|
||||
return commits, nil
|
||||
return commits
|
||||
}
|
||||
passedAncestor := false
|
||||
for i, commit := range commits {
|
||||
if strings.HasPrefix(ancestor, commit.Sha) {
|
||||
passedAncestor = true
|
||||
}
|
||||
if commit.Status != models.StatusPushed {
|
||||
if commit.Status != models.StatusPushed && commit.Status != models.StatusUnpushed {
|
||||
continue
|
||||
}
|
||||
if passedAncestor {
|
||||
commits[i].Status = models.StatusMerged
|
||||
}
|
||||
}
|
||||
return commits, nil
|
||||
return commits
|
||||
}
|
||||
|
||||
func (self *CommitLoader) getMergeBase(refName string) (string, error) {
|
||||
info, err := self.getCurrentBranchInfo()
|
||||
func (self *CommitLoader) getMergeBase(refName string) string {
|
||||
if self.quotedMainBranches == nil {
|
||||
self.quotedMainBranches = lo.ToPtr(self.getExistingMainBranches())
|
||||
}
|
||||
|
||||
if *self.quotedMainBranches == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
// We pass all configured main branches to the merge-base call; git will
|
||||
// return the base commit for the closest one.
|
||||
|
||||
output, err := self.cmd.New(
|
||||
NewGitCmd("merge-base").Arg(self.cmd.Quote(refName), *self.quotedMainBranches).
|
||||
ToString(),
|
||||
).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return "", err
|
||||
// If there's an error, it must be because one of the main branches that
|
||||
// used to exist when we called getExistingMainBranches() was deleted
|
||||
// meanwhile. To fix this for next time, throw away our cache.
|
||||
self.quotedMainBranches = nil
|
||||
}
|
||||
return ignoringWarnings(output)
|
||||
}
|
||||
|
||||
baseBranch := "master"
|
||||
if strings.HasPrefix(info.RefName, "feature/") {
|
||||
baseBranch = "develop"
|
||||
}
|
||||
|
||||
// swallowing error because it's not a big deal; probably because there are no commits yet
|
||||
output, _ := self.cmd.New(fmt.Sprintf("git merge-base %s %s", self.cmd.Quote(refName), self.cmd.Quote(baseBranch))).DontLog().RunWithOutput()
|
||||
return ignoringWarnings(output), nil
|
||||
func (self *CommitLoader) getExistingMainBranches() string {
|
||||
return strings.Join(
|
||||
lo.FilterMap(self.UserConfig.Git.MainBranches,
|
||||
func(branchName string, _ int) (string, bool) {
|
||||
quotedRef := self.cmd.Quote("refs/heads/" + branchName)
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--verify", "--quiet", quotedRef).ToString(),
|
||||
).DontLog().Run(); err != nil {
|
||||
return "", false
|
||||
}
|
||||
return quotedRef, true
|
||||
}), " ")
|
||||
}
|
||||
|
||||
func ignoringWarnings(commandOutput string) string {
|
||||
@@ -398,9 +417,10 @@ func ignoringWarnings(commandOutput string) string {
|
||||
func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
|
||||
output, err := self.cmd.
|
||||
New(
|
||||
fmt.Sprintf("git merge-base %s %s@{u}",
|
||||
self.cmd.Quote(refName),
|
||||
self.cmd.Quote(strings.TrimPrefix(refName, "refs/heads/"))),
|
||||
NewGitCmd("merge-base").
|
||||
Arg(self.cmd.Quote(refName)).
|
||||
Arg(self.cmd.Quote(strings.TrimPrefix(refName, "refs/heads/")) + "@{u}").
|
||||
ToString(),
|
||||
).
|
||||
DontLog().
|
||||
RunWithOutput()
|
||||
@@ -413,42 +433,23 @@ func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
|
||||
|
||||
// getLog gets the git log.
|
||||
func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
||||
limitFlag := ""
|
||||
if opts.Limit {
|
||||
limitFlag = " -300"
|
||||
}
|
||||
|
||||
followFlag := ""
|
||||
filterFlag := ""
|
||||
if opts.FilterPath != "" {
|
||||
followFlag = " --follow"
|
||||
filterFlag = fmt.Sprintf(" %s", self.cmd.Quote(opts.FilterPath))
|
||||
}
|
||||
|
||||
config := self.UserConfig.Git.Log
|
||||
|
||||
orderFlag := ""
|
||||
if config.Order != "default" {
|
||||
orderFlag = " --" + config.Order
|
||||
}
|
||||
allFlag := ""
|
||||
if opts.All {
|
||||
allFlag = " --all"
|
||||
}
|
||||
cmdStr := NewGitCmd("log").
|
||||
Arg(self.cmd.Quote(opts.RefName)).
|
||||
ArgIf(config.Order != "default", "--"+config.Order).
|
||||
ArgIf(opts.All, "--all").
|
||||
Arg("--oneline").
|
||||
Arg(prettyFormat).
|
||||
Arg("--abbrev=40").
|
||||
ArgIf(opts.Limit, "-300").
|
||||
ArgIf(opts.FilterPath != "", "--follow").
|
||||
Arg("--no-show-signature").
|
||||
Arg("--").
|
||||
ArgIf(opts.FilterPath != "", self.cmd.Quote(opts.FilterPath)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(
|
||||
fmt.Sprintf(
|
||||
"git log %s%s%s --oneline %s%s --abbrev=%d%s --no-show-signature --%s",
|
||||
self.cmd.Quote(opts.RefName),
|
||||
orderFlag,
|
||||
allFlag,
|
||||
prettyFormat,
|
||||
limitFlag,
|
||||
40,
|
||||
followFlag,
|
||||
filterFlag,
|
||||
),
|
||||
).DontLog()
|
||||
return self.cmd.New(cmdStr).DontLog()
|
||||
}
|
||||
|
||||
const prettyFormat = `--pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s"`
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -21,25 +22,26 @@ d8084cd558925eb7c9c38afeed5725c21653ab90|1640821426|Jesse Duffield|jessedduffiel
|
||||
3d4470a6c072208722e5ae9a54bcb9634959a1c5|1640748818|Jesse Duffield|jessedduffield@gmail.com||053a66a7be3da43aacdc|WIP
|
||||
053a66a7be3da43aacdc7aa78e1fe757b82c4dd2|1640739815|Jesse Duffield|jessedduffield@gmail.com||985fe482e806b172aea4|refactoring the config struct`, "|", "\x00", -1)
|
||||
|
||||
var singleCommitOutput = strings.Replace(`0eea75e8c631fba6b58135697835d58ba4c18dbc|1640826609|Jesse Duffield|jessedduffield@gmail.com| (HEAD -> better-tests)|b21997d6b4cbdf84b149|better typing for rebase mode`, "|", "\x00", -1)
|
||||
|
||||
func TestGetCommits(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
runner *oscommands.FakeCmdObjRunner
|
||||
expectedCommits []*models.Commit
|
||||
expectedError error
|
||||
logOrder string
|
||||
rebaseMode enums.RebaseMode
|
||||
currentBranchName string
|
||||
opts GetCommitsOptions
|
||||
testName string
|
||||
runner *oscommands.FakeCmdObjRunner
|
||||
expectedCommits []*models.Commit
|
||||
expectedError error
|
||||
logOrder string
|
||||
rebaseMode enums.RebaseMode
|
||||
opts GetCommitsOptions
|
||||
mainBranches []string
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "should return no commits if there are none",
|
||||
logOrder: "topo-order",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
currentBranchName: "master",
|
||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||
testName: "should return no commits if there are none",
|
||||
logOrder: "topo-order",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
Expect(`git log "HEAD" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, "", nil),
|
||||
@@ -48,11 +50,10 @@ func TestGetCommits(t *testing.T) {
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
testName: "should use proper upstream name for branch",
|
||||
logOrder: "topo-order",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
currentBranchName: "mybranch",
|
||||
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", IncludeRebaseCommits: false},
|
||||
testName: "should use proper upstream name for branch",
|
||||
logOrder: "topo-order",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
opts: GetCommitsOptions{RefName: "refs/heads/mybranch", IncludeRebaseCommits: false},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
Expect(`git merge-base "refs/heads/mybranch" "mybranch"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
Expect(`git log "refs/heads/mybranch" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, "", nil),
|
||||
@@ -61,18 +62,21 @@ func TestGetCommits(t *testing.T) {
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
testName: "should return commits if they are present",
|
||||
logOrder: "topo-order",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
currentBranchName: "master",
|
||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||
testName: "should return commits if they are present",
|
||||
logOrder: "topo-order",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||
mainBranches: []string{"master", "main"},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
// here it's seeing which commits are yet to be pushed
|
||||
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
// here it's actually getting all the commits in a formatted form, one per line
|
||||
Expect(`git log "HEAD" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, commitsOutput, nil).
|
||||
// here it's testing which of the configured main branches exist
|
||||
Expect(`git rev-parse --verify --quiet "refs/heads/master"`, "", nil). // this one does
|
||||
Expect(`git rev-parse --verify --quiet "refs/heads/main"`, "", errors.New("error")). // this one doesn't
|
||||
// here it's seeing where our branch diverged from the master branch so that we can mark that commit and parent commits as 'merged'
|
||||
Expect(`git merge-base "HEAD" "master"`, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
|
||||
Expect(`git merge-base "HEAD" "refs/heads/master"`, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
|
||||
|
||||
expectedCommits: []*models.Commit{
|
||||
{
|
||||
@@ -191,11 +195,80 @@ func TestGetCommits(t *testing.T) {
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
testName: "should not specify order if `log.order` is `default`",
|
||||
logOrder: "default",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
currentBranchName: "master",
|
||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||
testName: "should not call merge-base for mainBranches if none exist",
|
||||
logOrder: "topo-order",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||
mainBranches: []string{"master", "main"},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
// here it's seeing which commits are yet to be pushed
|
||||
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
// here it's actually getting all the commits in a formatted form, one per line
|
||||
Expect(`git log "HEAD" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, singleCommitOutput, nil).
|
||||
// here it's testing which of the configured main branches exist; neither does
|
||||
Expect(`git rev-parse --verify --quiet "refs/heads/master"`, "", errors.New("error")).
|
||||
Expect(`git rev-parse --verify --quiet "refs/heads/main"`, "", errors.New("error")),
|
||||
|
||||
expectedCommits: []*models.Commit{
|
||||
{
|
||||
Sha: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
|
||||
Name: "better typing for rebase mode",
|
||||
Status: models.StatusUnpushed,
|
||||
Action: models.ActionNone,
|
||||
Tags: []string{},
|
||||
ExtraInfo: "(HEAD -> better-tests)",
|
||||
AuthorName: "Jesse Duffield",
|
||||
AuthorEmail: "jessedduffield@gmail.com",
|
||||
UnixTimestamp: 1640826609,
|
||||
Parents: []string{
|
||||
"b21997d6b4cbdf84b149",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
testName: "should call merge-base for all main branches that exist",
|
||||
logOrder: "topo-order",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||
mainBranches: []string{"master", "main", "develop", "1.0-hotfixes"},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
// here it's seeing which commits are yet to be pushed
|
||||
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
// here it's actually getting all the commits in a formatted form, one per line
|
||||
Expect(`git log "HEAD" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, singleCommitOutput, nil).
|
||||
// here it's testing which of the configured main branches exist
|
||||
Expect(`git rev-parse --verify --quiet "refs/heads/master"`, "", nil).
|
||||
Expect(`git rev-parse --verify --quiet "refs/heads/main"`, "", errors.New("error")).
|
||||
Expect(`git rev-parse --verify --quiet "refs/heads/develop"`, "", nil).
|
||||
Expect(`git rev-parse --verify --quiet "refs/heads/1.0-hotfixes"`, "", nil).
|
||||
// here it's seeing where our branch diverged from the master branch so that we can mark that commit and parent commits as 'merged'
|
||||
Expect(`git merge-base "HEAD" "refs/heads/master" "refs/heads/develop" "refs/heads/1.0-hotfixes"`, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
|
||||
|
||||
expectedCommits: []*models.Commit{
|
||||
{
|
||||
Sha: "0eea75e8c631fba6b58135697835d58ba4c18dbc",
|
||||
Name: "better typing for rebase mode",
|
||||
Status: models.StatusUnpushed,
|
||||
Action: models.ActionNone,
|
||||
Tags: []string{},
|
||||
ExtraInfo: "(HEAD -> better-tests)",
|
||||
AuthorName: "Jesse Duffield",
|
||||
AuthorEmail: "jessedduffield@gmail.com",
|
||||
UnixTimestamp: 1640826609,
|
||||
Parents: []string{
|
||||
"b21997d6b4cbdf84b149",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
testName: "should not specify order if `log.order` is `default`",
|
||||
logOrder: "default",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
Expect(`git log "HEAD" --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --no-show-signature --`, "", nil),
|
||||
@@ -204,11 +277,10 @@ func TestGetCommits(t *testing.T) {
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
testName: "should set filter path",
|
||||
logOrder: "default",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
currentBranchName: "master",
|
||||
opts: GetCommitsOptions{RefName: "HEAD", FilterPath: "src"},
|
||||
testName: "should set filter path",
|
||||
logOrder: "default",
|
||||
rebaseMode: enums.REBASE_MODE_NONE,
|
||||
opts: GetCommitsOptions{RefName: "HEAD", FilterPath: "src"},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||
Expect(`git log "HEAD" --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40 --follow --no-show-signature -- "src"`, "", nil),
|
||||
@@ -225,11 +297,8 @@ func TestGetCommits(t *testing.T) {
|
||||
common.UserConfig.Git.Log.Order = scenario.logOrder
|
||||
|
||||
builder := &CommitLoader{
|
||||
Common: common,
|
||||
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
|
||||
getCurrentBranchInfo: func() (BranchInfo, error) {
|
||||
return BranchInfo{RefName: scenario.currentBranchName, DisplayName: scenario.currentBranchName, DetachedHead: false}, nil
|
||||
},
|
||||
Common: common,
|
||||
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
|
||||
getRebaseMode: func() (enums.RebaseMode, error) { return scenario.rebaseMode, nil },
|
||||
dotGitDir: ".git",
|
||||
readFile: func(filename string) ([]byte, error) {
|
||||
@@ -240,6 +309,7 @@ func TestGetCommits(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
common.UserConfig.Git.MainBranches = scenario.mainBranches
|
||||
commits, err := builder.GetCommits(scenario.opts)
|
||||
|
||||
assert.Equal(t, scenario.expectedCommits, commits)
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
gogit "github.com/jesseduffield/go-git/v5"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
@@ -117,6 +118,26 @@ func buildWorkingTreeCommands(deps commonDeps) *WorkingTreeCommands {
|
||||
return NewWorkingTreeCommands(gitCommon, submoduleCommands, fileLoader)
|
||||
}
|
||||
|
||||
func buildPatchCommands(deps commonDeps) *PatchCommands {
|
||||
gitCommon := buildGitCommon(deps)
|
||||
rebaseCommands := buildRebaseCommands(deps)
|
||||
commitCommands := buildCommitCommands(deps)
|
||||
statusCommands := buildStatusCommands(deps)
|
||||
stashCommands := buildStashCommands(deps)
|
||||
loadFileFn := func(from string, to string, reverse bool, filename string, plain bool) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
patchBuilder := patch.NewPatchBuilder(gitCommon.Log, loadFileFn)
|
||||
|
||||
return NewPatchCommands(gitCommon, rebaseCommands, commitCommands, statusCommands, stashCommands, patchBuilder)
|
||||
}
|
||||
|
||||
func buildStatusCommands(deps commonDeps) *StatusCommands {
|
||||
gitCommon := buildGitCommon(deps)
|
||||
|
||||
return NewStatusCommands(gitCommon)
|
||||
}
|
||||
|
||||
func buildStashCommands(deps commonDeps) *StashCommands {
|
||||
gitCommon := buildGitCommon(deps)
|
||||
fileLoader := buildFileLoader(gitCommon)
|
||||
@@ -150,3 +171,9 @@ func buildBranchCommands(deps commonDeps) *BranchCommands {
|
||||
|
||||
return NewBranchCommands(gitCommon)
|
||||
}
|
||||
|
||||
func buildFlowCommands(deps commonDeps) *FlowCommands {
|
||||
gitCommon := buildGitCommon(deps)
|
||||
|
||||
return NewFlowCommands(gitCommon)
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func (self *FileLoader) GetStatusFiles(opts GetStatusFileOptions) []*models.File
|
||||
}
|
||||
untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting)
|
||||
|
||||
statuses, err := self.GitStatus(GitStatusOptions{NoRenames: opts.NoRenames, UntrackedFilesArg: untrackedFilesArg})
|
||||
statuses, err := self.gitStatus(GitStatusOptions{NoRenames: opts.NoRenames, UntrackedFilesArg: untrackedFilesArg})
|
||||
if err != nil {
|
||||
self.Log.Error(err)
|
||||
}
|
||||
@@ -81,13 +81,15 @@ type FileStatus struct {
|
||||
PreviousName string
|
||||
}
|
||||
|
||||
func (c *FileLoader) GitStatus(opts GitStatusOptions) ([]FileStatus, error) {
|
||||
noRenamesFlag := ""
|
||||
if opts.NoRenames {
|
||||
noRenamesFlag = " --no-renames"
|
||||
}
|
||||
func (c *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
|
||||
cmdStr := NewGitCmd("status").
|
||||
Arg(opts.UntrackedFilesArg).
|
||||
Arg("--porcelain").
|
||||
Arg("-z").
|
||||
ArgIf(opts.NoRenames, "--no-renames").
|
||||
ToString()
|
||||
|
||||
statusLines, _, err := c.cmd.New(fmt.Sprintf("git status %s --porcelain -z%s", opts.UntrackedFilesArg, noRenamesFlag)).DontLog().RunWithOutputs()
|
||||
statusLines, _, err := c.cmd.New(cmdStr).DontLog().RunWithOutputs()
|
||||
if err != nil {
|
||||
return []FileStatus{}, err
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ func (self *FlowCommands) FinishCmdObj(branchName string) (oscommands.ICmdObj, e
|
||||
branchType := ""
|
||||
for _, line := range strings.Split(strings.TrimSpace(prefixes), "\n") {
|
||||
if strings.HasPrefix(line, "gitflow.prefix.") && strings.HasSuffix(line, prefix) {
|
||||
|
||||
regex := regexp.MustCompile("gitflow.prefix.([^ ]*) .*")
|
||||
matches := regex.FindAllStringSubmatch(line, 1)
|
||||
|
||||
@@ -48,9 +49,13 @@ func (self *FlowCommands) FinishCmdObj(branchName string) (oscommands.ICmdObj, e
|
||||
return nil, errors.New(self.Tr.NotAGitFlowBranch)
|
||||
}
|
||||
|
||||
return self.cmd.New("git flow " + branchType + " finish " + suffix), nil
|
||||
cmdStr := NewGitCmd("flow").Arg(branchType, "finish", suffix).ToString()
|
||||
|
||||
return self.cmd.New(cmdStr), nil
|
||||
}
|
||||
|
||||
func (self *FlowCommands) StartCmdObj(branchType string, name string) oscommands.ICmdObj {
|
||||
return self.cmd.New("git flow " + branchType + " start " + name)
|
||||
cmdStr := NewGitCmd("flow").Arg(branchType, "start", name).ToString()
|
||||
|
||||
return self.cmd.New(cmdStr)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStartCmdObj(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
testName string
|
||||
branchType string
|
||||
name string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
testName: "basic",
|
||||
branchType: "feature",
|
||||
name: "test",
|
||||
expected: "git flow feature start test",
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
instance := buildFlowCommands(commonDeps{})
|
||||
|
||||
assert.Equal(t,
|
||||
instance.StartCmdObj(s.branchType, s.name).ToString(),
|
||||
s.expected,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFinishCmdObj(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
testName string
|
||||
branchName string
|
||||
expected string
|
||||
expectedError string
|
||||
gitConfigMockResponses map[string]string
|
||||
}{
|
||||
{
|
||||
testName: "not a git flow branch",
|
||||
branchName: "mybranch",
|
||||
expected: "",
|
||||
expectedError: "This does not seem to be a git flow branch",
|
||||
gitConfigMockResponses: nil,
|
||||
},
|
||||
{
|
||||
testName: "feature branch without config",
|
||||
branchName: "feature/mybranch",
|
||||
expected: "",
|
||||
expectedError: "This does not seem to be a git flow branch",
|
||||
gitConfigMockResponses: nil,
|
||||
},
|
||||
{
|
||||
testName: "feature branch with config",
|
||||
branchName: "feature/mybranch",
|
||||
expected: "git flow feature finish mybranch",
|
||||
expectedError: "",
|
||||
gitConfigMockResponses: map[string]string{
|
||||
"--local --get-regexp gitflow.prefix": "gitflow.prefix.feature feature/",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
instance := buildFlowCommands(commonDeps{
|
||||
gitConfig: git_config.NewFakeGitConfig(s.gitConfigMockResponses),
|
||||
})
|
||||
|
||||
cmd, err := instance.FinishCmdObj(s.branchName)
|
||||
|
||||
if s.expectedError != "" {
|
||||
if err == nil {
|
||||
t.Errorf("Expected error, got nil")
|
||||
} else {
|
||||
assert.Equal(t, err.Error(), s.expectedError)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, cmd.ToString(), s.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package git_commands
|
||||
|
||||
import "strings"
|
||||
|
||||
// convenience struct for building git commands. Especially useful when
|
||||
// including conditional args
|
||||
type GitCommandBuilder struct {
|
||||
// command string
|
||||
args []string
|
||||
}
|
||||
|
||||
func NewGitCmd(command string) *GitCommandBuilder {
|
||||
return &GitCommandBuilder{args: []string{command}}
|
||||
}
|
||||
|
||||
func (self *GitCommandBuilder) Arg(args ...string) *GitCommandBuilder {
|
||||
self.args = append(self.args, args...)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *GitCommandBuilder) ArgIf(condition bool, ifTrue ...string) *GitCommandBuilder {
|
||||
if condition {
|
||||
self.Arg(ifTrue...)
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *GitCommandBuilder) ArgIfElse(condition bool, ifTrue string, ifFalse string) *GitCommandBuilder {
|
||||
if condition {
|
||||
return self.Arg(ifTrue)
|
||||
} else {
|
||||
return self.Arg(ifFalse)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *GitCommandBuilder) Config(value string) *GitCommandBuilder {
|
||||
// config settings come before the command
|
||||
self.args = append([]string{"-c", value}, self.args...)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *GitCommandBuilder) RepoPath(value string) *GitCommandBuilder {
|
||||
// repo path comes before the command
|
||||
self.args = append([]string{"-C", value}, self.args...)
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *GitCommandBuilder) ToString() string {
|
||||
return "git " + strings.Join(self.args, " ")
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGitCommandBuilder(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
input: NewGitCmd("push").
|
||||
Arg("--force-with-lease").
|
||||
Arg("--set-upstream").
|
||||
Arg("origin").
|
||||
Arg("master").
|
||||
ToString(),
|
||||
expected: "git push --force-with-lease --set-upstream origin master",
|
||||
},
|
||||
{
|
||||
input: NewGitCmd("push").ArgIf(true, "--test").ToString(),
|
||||
expected: "git push --test",
|
||||
},
|
||||
{
|
||||
input: NewGitCmd("push").ArgIf(false, "--test").ToString(),
|
||||
expected: "git push",
|
||||
},
|
||||
{
|
||||
input: NewGitCmd("push").ArgIfElse(true, "-b", "-a").ToString(),
|
||||
expected: "git push -b",
|
||||
},
|
||||
{
|
||||
input: NewGitCmd("push").ArgIfElse(false, "-a", "-b").ToString(),
|
||||
expected: "git push -b",
|
||||
},
|
||||
{
|
||||
input: NewGitCmd("push").Arg("-a", "-b").ToString(),
|
||||
expected: "git push -a -b",
|
||||
},
|
||||
{
|
||||
input: NewGitCmd("push").Config("user.name=foo").Config("user.email=bar").ToString(),
|
||||
expected: "git -c user.email=bar -c user.name=foo push",
|
||||
},
|
||||
{
|
||||
input: NewGitCmd("push").RepoPath("a/b/c").ToString(),
|
||||
expected: "git -C a/b/c push",
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
assert.Equal(t, s.input, s.expected)
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/fsmiamoto/git-todo-parser/todo"
|
||||
"github.com/go-errors/errors"
|
||||
@@ -9,6 +11,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type PatchCommands struct {
|
||||
@@ -39,6 +42,53 @@ func NewPatchCommands(
|
||||
}
|
||||
}
|
||||
|
||||
type ApplyPatchOpts struct {
|
||||
ThreeWay bool
|
||||
Cached bool
|
||||
Index bool
|
||||
Reverse bool
|
||||
}
|
||||
|
||||
func (self *PatchCommands) ApplyCustomPatch(reverse bool) error {
|
||||
patch := self.PatchBuilder.PatchToApply(reverse)
|
||||
|
||||
return self.ApplyPatch(patch, ApplyPatchOpts{
|
||||
Index: true,
|
||||
ThreeWay: true,
|
||||
Reverse: reverse,
|
||||
})
|
||||
}
|
||||
|
||||
func (self *PatchCommands) ApplyPatch(patch string, opts ApplyPatchOpts) error {
|
||||
filepath, err := self.SaveTemporaryPatch(patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.applyPatchFile(filepath, opts)
|
||||
}
|
||||
|
||||
func (self *PatchCommands) applyPatchFile(filepath string, opts ApplyPatchOpts) error {
|
||||
cmdStr := NewGitCmd("apply").
|
||||
ArgIf(opts.ThreeWay, "--3way").
|
||||
ArgIf(opts.Cached, "--cached").
|
||||
ArgIf(opts.Index, "--index").
|
||||
ArgIf(opts.Reverse, "--reverse").
|
||||
Arg(self.cmd.Quote(filepath)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *PatchCommands) SaveTemporaryPatch(patch string) (string, error) {
|
||||
filepath := filepath.Join(self.os.GetTempDir(), utils.GetCurrentRepoName(), time.Now().Format("Jan _2 15.04.05.000000000")+".patch")
|
||||
self.Log.Infof("saving temporary patch to %s", filepath)
|
||||
if err := self.os.CreateFileWithContent(filepath, patch); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath, nil
|
||||
}
|
||||
|
||||
// DeletePatchesFromCommit applies a patch in reverse for a commit
|
||||
func (self *PatchCommands) DeletePatchesFromCommit(commits []*models.Commit, commitIndex int) error {
|
||||
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, commitIndex); err != nil {
|
||||
@@ -46,7 +96,7 @@ func (self *PatchCommands) DeletePatchesFromCommit(commits []*models.Commit, com
|
||||
}
|
||||
|
||||
// apply each patch in reverse
|
||||
if err := self.PatchBuilder.ApplyPatches(true); err != nil {
|
||||
if err := self.ApplyCustomPatch(true); err != nil {
|
||||
_ = self.rebase.AbortRebase()
|
||||
return err
|
||||
}
|
||||
@@ -72,7 +122,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
|
||||
}
|
||||
|
||||
// apply each patch forward
|
||||
if err := self.PatchBuilder.ApplyPatches(false); err != nil {
|
||||
if err := self.ApplyCustomPatch(false); err != nil {
|
||||
// Don't abort the rebase here; this might cause conflicts, so give
|
||||
// the user a chance to resolve them
|
||||
return err
|
||||
@@ -121,7 +171,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
|
||||
}
|
||||
|
||||
// apply each patch in reverse
|
||||
if err := self.PatchBuilder.ApplyPatches(true); err != nil {
|
||||
if err := self.ApplyCustomPatch(true); err != nil {
|
||||
_ = self.rebase.AbortRebase()
|
||||
return err
|
||||
}
|
||||
@@ -144,7 +194,7 @@ func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, s
|
||||
self.rebase.onSuccessfulContinue = func() error {
|
||||
// now we should be up to the destination, so let's apply forward these patches to that.
|
||||
// ideally we would ensure we're on the right commit but I'm not sure if that check is necessary
|
||||
if err := self.rebase.workingTree.ApplyPatch(patch, "index", "3way"); err != nil {
|
||||
if err := self.ApplyPatch(patch, ApplyPatchOpts{Index: true, ThreeWay: true}); err != nil {
|
||||
// Don't abort the rebase here; this might cause conflicts, so give
|
||||
// the user a chance to resolve them
|
||||
return err
|
||||
@@ -177,7 +227,7 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.PatchBuilder.ApplyPatches(true); err != nil {
|
||||
if err := self.ApplyCustomPatch(true); err != nil {
|
||||
if self.status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
|
||||
_ = self.rebase.AbortRebase()
|
||||
}
|
||||
@@ -201,7 +251,7 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId
|
||||
|
||||
self.rebase.onSuccessfulContinue = func() error {
|
||||
// add patches to index
|
||||
if err := self.rebase.workingTree.ApplyPatch(patch, "index", "3way"); err != nil {
|
||||
if err := self.ApplyPatch(patch, ApplyPatchOpts{Index: true, ThreeWay: true}); err != nil {
|
||||
if self.status.WorkingTreeState() == enums.REBASE_MODE_REBASING {
|
||||
_ = self.rebase.AbortRebase()
|
||||
}
|
||||
@@ -226,7 +276,7 @@ func (self *PatchCommands) PullPatchIntoNewCommit(commits []*models.Commit, comm
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.PatchBuilder.ApplyPatches(true); err != nil {
|
||||
if err := self.ApplyCustomPatch(true); err != nil {
|
||||
_ = self.rebase.AbortRebase()
|
||||
return err
|
||||
}
|
||||
@@ -242,7 +292,7 @@ func (self *PatchCommands) PullPatchIntoNewCommit(commits []*models.Commit, comm
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.rebase.workingTree.ApplyPatch(patch, "index", "3way"); err != nil {
|
||||
if err := self.ApplyPatch(patch, ApplyPatchOpts{Index: true, ThreeWay: true}); err != nil {
|
||||
_ = self.rebase.AbortRebase()
|
||||
return err
|
||||
}
|
||||
@@ -267,5 +317,7 @@ func (self *PatchCommands) PullPatchIntoNewCommit(commits []*models.Commit, comm
|
||||
// only some lines of a range of adjacent added lines. To solve this, we
|
||||
// get the diff of HEAD and the original commit and then apply that.
|
||||
func (self *PatchCommands) diffHeadAgainstCommit(commit *models.Commit) (string, error) {
|
||||
return self.cmd.New(fmt.Sprintf("git diff HEAD..%s", commit.Sha)).RunWithOutput()
|
||||
cmdStr := NewGitCmd("diff").Arg("HEAD.." + commit.Sha).ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).RunWithOutput()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestWorkingTreeApplyPatch(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
runner *oscommands.FakeCmdObjRunner
|
||||
test func(error)
|
||||
}
|
||||
|
||||
expectFn := func(regexStr string, errToReturn error) func(cmdObj oscommands.ICmdObj) (string, error) {
|
||||
return func(cmdObj oscommands.ICmdObj) (string, error) {
|
||||
re := regexp.MustCompile(regexStr)
|
||||
cmdStr := cmdObj.ToString()
|
||||
matches := re.FindStringSubmatch(cmdStr)
|
||||
assert.Equal(t, 2, len(matches), fmt.Sprintf("unexpected command: %s", cmdStr))
|
||||
|
||||
filename := matches[1]
|
||||
|
||||
content, err := os.ReadFile(filename)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "test", string(content))
|
||||
|
||||
return "", errToReturn
|
||||
}
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "valid case",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectFunc(expectFn(`git apply --cached "(.*)"`, nil)),
|
||||
test: func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "command returns error",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectFunc(expectFn(`git apply --cached "(.*)"`, errors.New("error"))),
|
||||
test: func(err error) {
|
||||
assert.Error(t, err)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
instance := buildPatchCommands(commonDeps{runner: s.runner})
|
||||
s.test(instance.ApplyPatch("test", ApplyPatchOpts{Cached: true}))
|
||||
s.runner.CheckForMissingCalls()
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -176,23 +176,21 @@ type PrepareInteractiveRebaseCommandOpts struct {
|
||||
func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteractiveRebaseCommandOpts) oscommands.ICmdObj {
|
||||
ex := oscommands.GetLazygitPath()
|
||||
|
||||
cmdStr := NewGitCmd("rebase").
|
||||
Arg("--interactive").
|
||||
Arg("--autostash").
|
||||
Arg("--keep-empty").
|
||||
ArgIf(!self.version.IsOlderThan(2, 26, 0), "--empty=keep").
|
||||
Arg("--no-autosquash").
|
||||
ArgIf(!self.version.IsOlderThan(2, 22, 0), "--rebase-merges").
|
||||
Arg(opts.baseShaOrRoot).
|
||||
ToString()
|
||||
|
||||
debug := "FALSE"
|
||||
if self.Debug {
|
||||
debug = "TRUE"
|
||||
}
|
||||
|
||||
emptyArg := " --empty=keep"
|
||||
if self.version.IsOlderThan(2, 26, 0) {
|
||||
emptyArg = ""
|
||||
}
|
||||
|
||||
rebaseMergesArg := " --rebase-merges"
|
||||
if self.version.IsOlderThan(2, 22, 0) {
|
||||
rebaseMergesArg = ""
|
||||
}
|
||||
|
||||
cmdStr := fmt.Sprintf("git rebase --interactive --autostash --keep-empty%s --no-autosquash%s %s",
|
||||
emptyArg, rebaseMergesArg, opts.baseShaOrRoot)
|
||||
self.Log.WithField("command", cmdStr).Debug("RunCommand")
|
||||
|
||||
cmdObj := self.cmd.New(cmdStr)
|
||||
@@ -228,7 +226,8 @@ func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) e
|
||||
}
|
||||
|
||||
// Get the sha of the commit we just created
|
||||
fixupSha, err := self.cmd.New("git rev-parse --verify HEAD").RunWithOutput()
|
||||
cmdStr := NewGitCmd("rev-parse").Arg("--verify", "HEAD").ToString()
|
||||
fixupSha, err := self.cmd.New(cmdStr).RunWithOutput()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -265,14 +264,11 @@ func (self *RebaseCommands) SquashAllAboveFixupCommits(commit *models.Commit) er
|
||||
shaOrRoot = "--root"
|
||||
}
|
||||
|
||||
return self.runSkipEditorCommand(
|
||||
self.cmd.New(
|
||||
fmt.Sprintf(
|
||||
"git rebase --interactive --rebase-merges --autostash --autosquash %s",
|
||||
shaOrRoot,
|
||||
),
|
||||
),
|
||||
)
|
||||
cmdStr := NewGitCmd("rebase").
|
||||
Arg("--interactive", "--rebase-merges", "--autostash", "--autosquash", shaOrRoot).
|
||||
ToString()
|
||||
|
||||
return self.runSkipEditorCommand(self.cmd.New(cmdStr))
|
||||
}
|
||||
|
||||
// BeginInteractiveRebaseForCommit starts an interactive rebase to edit the current
|
||||
@@ -308,7 +304,9 @@ func (self *RebaseCommands) RebaseBranch(branchName string) error {
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) GenericMergeOrRebaseActionCmdObj(commandType string, command string) oscommands.ICmdObj {
|
||||
return self.cmd.New("git " + commandType + " --" + command)
|
||||
cmdStr := NewGitCmd(commandType).Arg("--" + command).ToString()
|
||||
|
||||
return self.cmd.New(cmdStr)
|
||||
}
|
||||
|
||||
func (self *RebaseCommands) ContinueRebase() error {
|
||||
@@ -365,7 +363,9 @@ func (self *RebaseCommands) DiscardOldFileChanges(commits []*models.Commit, comm
|
||||
}
|
||||
|
||||
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
|
||||
if err := self.cmd.New("git cat-file -e HEAD^:" + self.cmd.Quote(fileName)).Run(); err != nil {
|
||||
cmdStr := NewGitCmd("cat-file").Arg("-e", "HEAD^:"+self.cmd.Quote(fileName)).ToString()
|
||||
|
||||
if err := self.cmd.New(cmdStr).Run(); err != nil {
|
||||
if err := self.os.Remove(fileName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -15,44 +15,52 @@ func NewRemoteCommands(gitCommon *GitCommon) *RemoteCommands {
|
||||
}
|
||||
|
||||
func (self *RemoteCommands) AddRemote(name string, url string) error {
|
||||
return self.cmd.
|
||||
New(fmt.Sprintf("git remote add %s %s", self.cmd.Quote(name), self.cmd.Quote(url))).
|
||||
Run()
|
||||
cmdStr := NewGitCmd("remote").
|
||||
Arg("add", self.cmd.Quote(name), self.cmd.Quote(url)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *RemoteCommands) RemoveRemote(name string) error {
|
||||
return self.cmd.
|
||||
New(fmt.Sprintf("git remote remove %s", self.cmd.Quote(name))).
|
||||
Run()
|
||||
cmdStr := NewGitCmd("remote").
|
||||
Arg("remove", self.cmd.Quote(name)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *RemoteCommands) RenameRemote(oldRemoteName string, newRemoteName string) error {
|
||||
return self.cmd.
|
||||
New(fmt.Sprintf("git remote rename %s %s", self.cmd.Quote(oldRemoteName), self.cmd.Quote(newRemoteName))).
|
||||
Run()
|
||||
cmdStr := NewGitCmd("remote").
|
||||
Arg("rename", self.cmd.Quote(oldRemoteName), self.cmd.Quote(newRemoteName)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *RemoteCommands) UpdateRemoteUrl(remoteName string, updatedUrl string) error {
|
||||
return self.cmd.
|
||||
New(fmt.Sprintf("git remote set-url %s %s", self.cmd.Quote(remoteName), self.cmd.Quote(updatedUrl))).
|
||||
Run()
|
||||
cmdStr := NewGitCmd("remote").
|
||||
Arg("set-url", self.cmd.Quote(remoteName), self.cmd.Quote(updatedUrl)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *RemoteCommands) DeleteRemoteBranch(remoteName string, branchName string) error {
|
||||
command := fmt.Sprintf("git push %s --delete %s", self.cmd.Quote(remoteName), self.cmd.Quote(branchName))
|
||||
return self.cmd.New(command).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
|
||||
cmdStr := NewGitCmd("push").
|
||||
Arg(self.cmd.Quote(remoteName), "--delete", self.cmd.Quote(branchName)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
|
||||
}
|
||||
|
||||
// CheckRemoteBranchExists Returns remote branch
|
||||
func (self *RemoteCommands) CheckRemoteBranchExists(branchName string) bool {
|
||||
_, err := self.cmd.
|
||||
New(
|
||||
fmt.Sprintf("git show-ref --verify -- refs/remotes/origin/%s",
|
||||
self.cmd.Quote(branchName),
|
||||
),
|
||||
).
|
||||
DontLog().
|
||||
RunWithOutput()
|
||||
cmdStr := NewGitCmd("show-ref").
|
||||
Arg("--verify", "--", fmt.Sprintf("refs/remotes/origin/%s", self.cmd.Quote(branchName))).
|
||||
ToString()
|
||||
|
||||
_, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ func NewRemoteLoader(
|
||||
}
|
||||
|
||||
func (self *RemoteLoader) GetRemotes() ([]*models.Remote, error) {
|
||||
remoteBranchesStr, err := self.cmd.New("git branch -r").DontLog().RunWithOutput()
|
||||
cmdStr := NewGitCmd("branch").Arg("-r").ToString()
|
||||
remoteBranchesStr, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -26,57 +26,94 @@ func NewStashCommands(
|
||||
}
|
||||
|
||||
func (self *StashCommands) DropNewest() error {
|
||||
return self.cmd.New("git stash drop").Run()
|
||||
cmdStr := NewGitCmd("stash").Arg("drop").ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *StashCommands) Drop(index int) error {
|
||||
return self.cmd.New(fmt.Sprintf("git stash drop stash@{%d}", index)).Run()
|
||||
cmdStr := NewGitCmd("stash").Arg("drop", fmt.Sprintf("stash@{%d}", index)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *StashCommands) Pop(index int) error {
|
||||
return self.cmd.New(fmt.Sprintf("git stash pop stash@{%d}", index)).Run()
|
||||
cmdStr := NewGitCmd("stash").Arg("pop", fmt.Sprintf("stash@{%d}", index)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *StashCommands) Apply(index int) error {
|
||||
return self.cmd.New(fmt.Sprintf("git stash apply stash@{%d}", index)).Run()
|
||||
cmdStr := NewGitCmd("stash").Arg("apply", fmt.Sprintf("stash@{%d}", index)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// Save save stash
|
||||
func (self *StashCommands) Save(message string) error {
|
||||
return self.cmd.New("git stash save " + self.cmd.Quote(message)).Run()
|
||||
cmdStr := NewGitCmd("stash").Arg("save", self.cmd.Quote(message)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *StashCommands) Store(sha string, message string) error {
|
||||
trimmedMessage := strings.Trim(message, " \t")
|
||||
if len(trimmedMessage) > 0 {
|
||||
return self.cmd.New(fmt.Sprintf("git stash store %s -m %s", self.cmd.Quote(sha), self.cmd.Quote(trimmedMessage))).Run()
|
||||
}
|
||||
return self.cmd.New(fmt.Sprintf("git stash store %s", self.cmd.Quote(sha))).Run()
|
||||
|
||||
cmdStr := NewGitCmd("stash").Arg("store", self.cmd.Quote(sha)).
|
||||
ArgIf(trimmedMessage != "", "-m", self.cmd.Quote(trimmedMessage)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *StashCommands) Sha(index int) (string, error) {
|
||||
sha, _, err := self.cmd.New(fmt.Sprintf("git rev-parse refs/stash@{%d}", index)).DontLog().RunWithOutputs()
|
||||
cmdStr := NewGitCmd("rev-parse").
|
||||
Arg(fmt.Sprintf("refs/stash@{%d}", index)).
|
||||
ToString()
|
||||
|
||||
sha, _, err := self.cmd.New(cmdStr).DontLog().RunWithOutputs()
|
||||
return strings.Trim(sha, "\r\n"), err
|
||||
}
|
||||
|
||||
func (self *StashCommands) ShowStashEntryCmdObj(index int) oscommands.ICmdObj {
|
||||
cmdStr := fmt.Sprintf("git stash show -p --stat --color=%s --unified=%d stash@{%d}", self.UserConfig.Git.Paging.ColorArg, self.UserConfig.Git.DiffContextSize, index)
|
||||
func (self *StashCommands) ShowStashEntryCmdObj(index int, ignoreWhitespace bool) oscommands.ICmdObj {
|
||||
cmdStr := NewGitCmd("stash").Arg("show").
|
||||
Arg("-p").
|
||||
Arg("--stat").
|
||||
Arg(fmt.Sprintf("--color=%s", self.UserConfig.Git.Paging.ColorArg)).
|
||||
Arg(fmt.Sprintf("--unified=%d", self.UserConfig.Git.DiffContextSize)).
|
||||
ArgIf(ignoreWhitespace, "--ignore-all-space").
|
||||
Arg(fmt.Sprintf("stash@{%d}", index)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).DontLog()
|
||||
}
|
||||
|
||||
func (self *StashCommands) StashAndKeepIndex(message string) error {
|
||||
return self.cmd.New(fmt.Sprintf("git stash save %s --keep-index", self.cmd.Quote(message))).Run()
|
||||
cmdStr := NewGitCmd("stash").Arg("save", self.cmd.Quote(message), "--keep-index").
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *StashCommands) StashUnstagedChanges(message string) error {
|
||||
if err := self.cmd.New("git commit --no-verify -m \"[lazygit] stashing unstaged changes\"").Run(); err != nil {
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("commit").
|
||||
Arg("--no-verify", "-m", self.cmd.Quote("[lazygit] stashing unstaged changes")).
|
||||
ToString(),
|
||||
).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := self.Save(message); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := self.cmd.New("git reset --soft HEAD^").Run(); err != nil {
|
||||
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("reset").Arg("--soft", "HEAD^").ToString(),
|
||||
).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -86,7 +123,9 @@ func (self *StashCommands) StashUnstagedChanges(message string) error {
|
||||
// shoutouts to Joe on https://stackoverflow.com/questions/14759748/stashing-only-staged-changes-in-git-is-it-possible
|
||||
func (self *StashCommands) SaveStagedChanges(message string) error {
|
||||
// wrap in 'writing', which uses a mutex
|
||||
if err := self.cmd.New("git stash --keep-index").Run(); err != nil {
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("stash").Arg("--keep-index").ToString(),
|
||||
).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -94,15 +133,22 @@ func (self *StashCommands) SaveStagedChanges(message string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.cmd.New("git stash apply stash@{1}").Run(); err != nil {
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("stash").Arg("apply", "stash@{1}").ToString(),
|
||||
).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.os.PipeCommands("git stash show -p", "git apply -R"); err != nil {
|
||||
if err := self.os.PipeCommands(
|
||||
NewGitCmd("stash").Arg("show", "-p").ToString(),
|
||||
NewGitCmd("apply").Arg("-R").ToString(),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.cmd.New("git stash drop stash@{1}").Run(); err != nil {
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("stash").Arg("drop", "stash@{1}").ToString(),
|
||||
).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -124,7 +170,10 @@ func (self *StashCommands) SaveStagedChanges(message string) error {
|
||||
}
|
||||
|
||||
func (self *StashCommands) StashIncludeUntrackedChanges(message string) error {
|
||||
return self.cmd.New(fmt.Sprintf("git stash save %s --include-untracked", self.cmd.Quote(message))).Run()
|
||||
return self.cmd.New(
|
||||
NewGitCmd("stash").Arg("save", self.cmd.Quote(message), "--include-untracked").
|
||||
ToString(),
|
||||
).Run()
|
||||
}
|
||||
|
||||
func (self *StashCommands) Rename(index int, message string) error {
|
||||
|
||||
@@ -32,7 +32,8 @@ func (self *StashLoader) GetStashEntries(filterPath string) []*models.StashEntry
|
||||
return self.getUnfilteredStashEntries()
|
||||
}
|
||||
|
||||
rawString, err := self.cmd.New("git stash list --name-only").DontLog().RunWithOutput()
|
||||
cmdStr := NewGitCmd("stash").Arg("list", "--name-only").ToString()
|
||||
rawString, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return self.getUnfilteredStashEntries()
|
||||
}
|
||||
@@ -65,7 +66,9 @@ outer:
|
||||
}
|
||||
|
||||
func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
|
||||
rawString, _ := self.cmd.New("git stash list -z --pretty='%gs'").DontLog().RunWithOutput()
|
||||
cmdStr := NewGitCmd("stash").Arg("list", "-z", "--pretty='%gs'").ToString()
|
||||
|
||||
rawString, _ := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
return slices.MapWithIndex(utils.SplitNul(rawString), func(line string, index int) *models.StashEntry {
|
||||
return self.stashEntryFromLine(line, index)
|
||||
})
|
||||
|
||||
@@ -99,24 +99,34 @@ func TestStashSha(t *testing.T) {
|
||||
|
||||
func TestStashStashEntryCmdObj(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
index int
|
||||
contextSize int
|
||||
expected string
|
||||
testName string
|
||||
index int
|
||||
contextSize int
|
||||
ignoreWhitespace bool
|
||||
expected string
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "Default case",
|
||||
index: 5,
|
||||
contextSize: 3,
|
||||
expected: "git stash show -p --stat --color=always --unified=3 stash@{5}",
|
||||
testName: "Default case",
|
||||
index: 5,
|
||||
contextSize: 3,
|
||||
ignoreWhitespace: false,
|
||||
expected: "git stash show -p --stat --color=always --unified=3 stash@{5}",
|
||||
},
|
||||
{
|
||||
testName: "Show diff with custom context size",
|
||||
index: 5,
|
||||
contextSize: 77,
|
||||
expected: "git stash show -p --stat --color=always --unified=77 stash@{5}",
|
||||
testName: "Show diff with custom context size",
|
||||
index: 5,
|
||||
contextSize: 77,
|
||||
ignoreWhitespace: false,
|
||||
expected: "git stash show -p --stat --color=always --unified=77 stash@{5}",
|
||||
},
|
||||
{
|
||||
testName: "Default case",
|
||||
index: 5,
|
||||
contextSize: 3,
|
||||
ignoreWhitespace: true,
|
||||
expected: "git stash show -p --stat --color=always --unified=3 --ignore-all-space stash@{5}",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -127,7 +137,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
|
||||
userConfig.Git.DiffContextSize = s.contextSize
|
||||
instance := buildStashCommands(commonDeps{userConfig: userConfig})
|
||||
|
||||
cmdStr := instance.ShowStashEntryCmdObj(s.index).ToString()
|
||||
cmdStr := instance.ShowStashEntryCmdObj(s.index, s.ignoreWhitespace).ToString()
|
||||
assert.Equal(t, s.expected, cmdStr)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -56,7 +56,9 @@ func (self *StatusCommands) IsBareRepo() (bool, error) {
|
||||
}
|
||||
|
||||
func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
|
||||
res, err := osCommand.Cmd.New("git rev-parse --is-bare-repository").DontLog().RunWithOutput()
|
||||
res, err := osCommand.Cmd.New(
|
||||
NewGitCmd("rev-parse").Arg("--is-bare-repository").ToString(),
|
||||
).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package git_commands
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -82,38 +81,60 @@ func (self *SubmoduleCommands) Stash(submodule *models.SubmoduleConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return self.cmd.New("git -C " + self.cmd.Quote(submodule.Path) + " stash --include-untracked").Run()
|
||||
cmdStr := NewGitCmd("stash").
|
||||
RepoPath(self.cmd.Quote(submodule.Path)).
|
||||
Arg("--include-untracked").
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) Reset(submodule *models.SubmoduleConfig) error {
|
||||
return self.cmd.New("git submodule update --init --force -- " + self.cmd.Quote(submodule.Path)).Run()
|
||||
cmdStr := NewGitCmd("submodule").
|
||||
Arg("update", "--init", "--force", "--", self.cmd.Quote(submodule.Path)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) UpdateAll() error {
|
||||
// not doing an --init here because the user probably doesn't want that
|
||||
return self.cmd.New("git submodule update --force").Run()
|
||||
cmdStr := NewGitCmd("submodule").Arg("update", "--force").ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
|
||||
// based on https://gist.github.com/myusuf3/7f645819ded92bda6677
|
||||
|
||||
if err := self.cmd.New("git submodule deinit --force -- " + self.cmd.Quote(submodule.Path)).Run(); err != nil {
|
||||
if strings.Contains(err.Error(), "did not match any file(s) known to git") {
|
||||
if err := self.cmd.New("git config --file .gitmodules --remove-section submodule." + self.cmd.Quote(submodule.Name)).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("submodule").
|
||||
Arg("deinit", "--force", "--", self.cmd.Quote(submodule.Path)).ToString(),
|
||||
).Run(); err != nil {
|
||||
if !strings.Contains(err.Error(), "did not match any file(s) known to git") {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.cmd.New("git config --remove-section submodule." + self.cmd.Quote(submodule.Name)).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("config").
|
||||
Arg("--file", ".gitmodules", "--remove-section", "submodule."+self.cmd.Quote(submodule.Path)).
|
||||
ToString(),
|
||||
).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// if there's an error here about it not existing then we'll just continue to do `git rm`
|
||||
} else {
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("config").
|
||||
Arg("--remove-section", "submodule."+self.cmd.Quote(submodule.Path)).
|
||||
ToString(),
|
||||
).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := self.cmd.New("git rm --force -r " + submodule.Path).Run(); err != nil {
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("rm").Arg("--force", "-r", submodule.Path).ToString(),
|
||||
).Run(); err != nil {
|
||||
// if the directory isn't there then that's fine
|
||||
self.Log.Error(err)
|
||||
}
|
||||
@@ -122,24 +143,35 @@ func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) Add(name string, path string, url string) error {
|
||||
return self.cmd.
|
||||
New(
|
||||
fmt.Sprintf(
|
||||
"git submodule add --force --name %s -- %s %s ",
|
||||
self.cmd.Quote(name),
|
||||
self.cmd.Quote(url),
|
||||
self.cmd.Quote(path),
|
||||
)).
|
||||
Run()
|
||||
cmdStr := NewGitCmd("submodule").
|
||||
Arg("add").
|
||||
Arg("--force").
|
||||
Arg("--name").
|
||||
Arg(self.cmd.Quote(name)).
|
||||
Arg("--").
|
||||
Arg(self.cmd.Quote(url)).
|
||||
Arg(self.cmd.Quote(path)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) UpdateUrl(name string, path string, newUrl string) error {
|
||||
setUrlCmdStr := NewGitCmd("config").
|
||||
Arg(
|
||||
"--file", ".gitmodules", "submodule."+self.cmd.Quote(name)+".url", self.cmd.Quote(newUrl),
|
||||
).
|
||||
ToString()
|
||||
|
||||
// the set-url command is only for later git versions so we're doing it manually here
|
||||
if err := self.cmd.New("git config --file .gitmodules submodule." + self.cmd.Quote(name) + ".url " + self.cmd.Quote(newUrl)).Run(); err != nil {
|
||||
if err := self.cmd.New(setUrlCmdStr).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.cmd.New("git submodule sync -- " + self.cmd.Quote(path)).Run(); err != nil {
|
||||
syncCmdStr := NewGitCmd("submodule").Arg("sync", "--", self.cmd.Quote(path)).
|
||||
ToString()
|
||||
|
||||
if err := self.cmd.New(syncCmdStr).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -147,27 +179,45 @@ func (self *SubmoduleCommands) UpdateUrl(name string, path string, newUrl string
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) Init(path string) error {
|
||||
return self.cmd.New("git submodule init -- " + self.cmd.Quote(path)).Run()
|
||||
cmdStr := NewGitCmd("submodule").Arg("init", "--", self.cmd.Quote(path)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) Update(path string) error {
|
||||
return self.cmd.New("git submodule update --init -- " + self.cmd.Quote(path)).Run()
|
||||
cmdStr := NewGitCmd("submodule").Arg("update", "--init", "--", self.cmd.Quote(path)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) BulkInitCmdObj() oscommands.ICmdObj {
|
||||
return self.cmd.New("git submodule init")
|
||||
cmdStr := NewGitCmd("submodule").Arg("init").
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr)
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) BulkUpdateCmdObj() oscommands.ICmdObj {
|
||||
return self.cmd.New("git submodule update")
|
||||
cmdStr := NewGitCmd("submodule").Arg("update").
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr)
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) ForceBulkUpdateCmdObj() oscommands.ICmdObj {
|
||||
return self.cmd.New("git submodule update --force")
|
||||
cmdStr := NewGitCmd("submodule").Arg("update", "--force").
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr)
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) BulkDeinitCmdObj() oscommands.ICmdObj {
|
||||
return self.cmd.New("git submodule deinit --all --force")
|
||||
cmdStr := NewGitCmd("submodule").Arg("deinit", "--all", "--force").
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr)
|
||||
}
|
||||
|
||||
func (self *SubmoduleCommands) ResetSubmodules(submodules []*models.SubmoduleConfig) error {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
)
|
||||
@@ -26,26 +24,16 @@ type PushOpts struct {
|
||||
}
|
||||
|
||||
func (self *SyncCommands) PushCmdObj(opts PushOpts) (oscommands.ICmdObj, error) {
|
||||
cmdStr := "git push"
|
||||
|
||||
if opts.Force {
|
||||
cmdStr += " --force-with-lease"
|
||||
if opts.UpstreamBranch != "" && opts.UpstreamRemote == "" {
|
||||
return nil, errors.New(self.Tr.MustSpecifyOriginError)
|
||||
}
|
||||
|
||||
if opts.SetUpstream {
|
||||
cmdStr += " --set-upstream"
|
||||
}
|
||||
|
||||
if opts.UpstreamRemote != "" {
|
||||
cmdStr += " " + self.cmd.Quote(opts.UpstreamRemote)
|
||||
}
|
||||
|
||||
if opts.UpstreamBranch != "" {
|
||||
if opts.UpstreamRemote == "" {
|
||||
return nil, errors.New(self.Tr.MustSpecifyOriginError)
|
||||
}
|
||||
cmdStr += " " + self.cmd.Quote(opts.UpstreamBranch)
|
||||
}
|
||||
cmdStr := NewGitCmd("push").
|
||||
ArgIf(opts.Force, "--force-with-lease").
|
||||
ArgIf(opts.SetUpstream, "--set-upstream").
|
||||
ArgIf(opts.UpstreamRemote != "", self.cmd.Quote(opts.UpstreamRemote)).
|
||||
ArgIf(opts.UpstreamBranch != "", self.cmd.Quote(opts.UpstreamBranch)).
|
||||
ToString()
|
||||
|
||||
cmdObj := self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex)
|
||||
return cmdObj, nil
|
||||
@@ -68,14 +56,10 @@ type FetchOptions struct {
|
||||
|
||||
// Fetch fetch git repo
|
||||
func (self *SyncCommands) Fetch(opts FetchOptions) error {
|
||||
cmdStr := "git fetch"
|
||||
|
||||
if opts.RemoteName != "" {
|
||||
cmdStr = fmt.Sprintf("%s %s", cmdStr, self.cmd.Quote(opts.RemoteName))
|
||||
}
|
||||
if opts.BranchName != "" {
|
||||
cmdStr = fmt.Sprintf("%s %s", cmdStr, self.cmd.Quote(opts.BranchName))
|
||||
}
|
||||
cmdStr := NewGitCmd("fetch").
|
||||
ArgIf(opts.RemoteName != "", self.cmd.Quote(opts.RemoteName)).
|
||||
ArgIf(opts.BranchName != "", self.cmd.Quote(opts.BranchName)).
|
||||
ToString()
|
||||
|
||||
cmdObj := self.cmd.New(cmdStr)
|
||||
if opts.Background {
|
||||
@@ -93,18 +77,12 @@ type PullOptions struct {
|
||||
}
|
||||
|
||||
func (self *SyncCommands) Pull(opts PullOptions) error {
|
||||
cmdStr := "git pull --no-edit"
|
||||
|
||||
if opts.FastForwardOnly {
|
||||
cmdStr += " --ff-only"
|
||||
}
|
||||
|
||||
if opts.RemoteName != "" {
|
||||
cmdStr = fmt.Sprintf("%s %s", cmdStr, self.cmd.Quote(opts.RemoteName))
|
||||
}
|
||||
if opts.BranchName != "" {
|
||||
cmdStr = fmt.Sprintf("%s %s", cmdStr, self.cmd.Quote(opts.BranchName))
|
||||
}
|
||||
cmdStr := NewGitCmd("pull").
|
||||
Arg("--no-edit").
|
||||
ArgIf(opts.FastForwardOnly, "--ff-only").
|
||||
ArgIf(opts.RemoteName != "", self.cmd.Quote(opts.RemoteName)).
|
||||
ArgIf(opts.BranchName != "", self.cmd.Quote(opts.BranchName)).
|
||||
ToString()
|
||||
|
||||
// setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user
|
||||
// has 'pull.rebase = interactive' configured.
|
||||
@@ -112,11 +90,18 @@ func (self *SyncCommands) Pull(opts PullOptions) error {
|
||||
}
|
||||
|
||||
func (self *SyncCommands) FastForward(branchName string, remoteName string, remoteBranchName string) error {
|
||||
cmdStr := fmt.Sprintf("git fetch %s %s:%s", self.cmd.Quote(remoteName), self.cmd.Quote(remoteBranchName), self.cmd.Quote(branchName))
|
||||
cmdStr := NewGitCmd("fetch").
|
||||
Arg(self.cmd.Quote(remoteName)).
|
||||
Arg(self.cmd.Quote(remoteBranchName) + ":" + self.cmd.Quote(branchName)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
|
||||
}
|
||||
|
||||
func (self *SyncCommands) FetchRemote(remoteName string) error {
|
||||
cmdStr := fmt.Sprintf("git fetch %s", self.cmd.Quote(remoteName))
|
||||
cmdStr := NewGitCmd("fetch").
|
||||
Arg(self.cmd.Quote(remoteName)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
|
||||
}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type TagCommands struct {
|
||||
*GitCommon
|
||||
}
|
||||
@@ -15,25 +11,32 @@ func NewTagCommands(gitCommon *GitCommon) *TagCommands {
|
||||
}
|
||||
|
||||
func (self *TagCommands) CreateLightweight(tagName string, ref string) error {
|
||||
if len(ref) > 0 {
|
||||
return self.cmd.New(fmt.Sprintf("git tag -- %s %s", self.cmd.Quote(tagName), self.cmd.Quote(ref))).Run()
|
||||
} else {
|
||||
return self.cmd.New(fmt.Sprintf("git tag -- %s", self.cmd.Quote(tagName))).Run()
|
||||
}
|
||||
cmdStr := NewGitCmd("tag").Arg("--", self.cmd.Quote(tagName)).
|
||||
ArgIf(len(ref) > 0, self.cmd.Quote(ref)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *TagCommands) CreateAnnotated(tagName, ref, msg string) error {
|
||||
if len(ref) > 0 {
|
||||
return self.cmd.New(fmt.Sprintf("git tag %s %s -m %s", self.cmd.Quote(tagName), self.cmd.Quote(ref), self.cmd.Quote(msg))).Run()
|
||||
} else {
|
||||
return self.cmd.New(fmt.Sprintf("git tag %s -m %s", self.cmd.Quote(tagName), self.cmd.Quote(msg))).Run()
|
||||
}
|
||||
cmdStr := NewGitCmd("tag").Arg(self.cmd.Quote(tagName)).
|
||||
ArgIf(len(ref) > 0, self.cmd.Quote(ref)).
|
||||
Arg("-m", self.cmd.Quote(msg)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *TagCommands) Delete(tagName string) error {
|
||||
return self.cmd.New(fmt.Sprintf("git tag -d %s", self.cmd.Quote(tagName))).Run()
|
||||
cmdStr := NewGitCmd("tag").Arg("-d", self.cmd.Quote(tagName)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *TagCommands) Push(remoteName string, tagName string) error {
|
||||
return self.cmd.New(fmt.Sprintf("git push %s tag %s", self.cmd.Quote(remoteName), self.cmd.Quote(tagName))).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
|
||||
cmdStr := NewGitCmd("push").Arg(self.cmd.Quote(remoteName), "tag", self.cmd.Quote(tagName)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
|
||||
}
|
||||
|
||||
@@ -28,7 +28,8 @@ func NewTagLoader(
|
||||
func (self *TagLoader) GetTags() ([]*models.Tag, error) {
|
||||
// get remote branches, sorted by creation date (descending)
|
||||
// see: https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---sortltkeygt
|
||||
tagsOutput, err := self.cmd.New(`git tag --list -n --sort=-creatordate`).DontLog().RunWithOutput()
|
||||
cmdStr := NewGitCmd("tag").Arg("--list", "-n", "--sort=-creatordate").ToString()
|
||||
tagsOutput, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type GitVersion struct {
|
||||
}
|
||||
|
||||
func GetGitVersion(osCommand *oscommands.OSCommand) (*GitVersion, error) {
|
||||
versionStr, _, err := osCommand.Cmd.New("git --version").RunWithOutputs()
|
||||
versionStr, _, err := osCommand.Cmd.New(NewGitCmd("--version").ToString()).RunWithOutputs()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -3,15 +3,11 @@ package git_commands
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type WorkingTreeCommands struct {
|
||||
@@ -33,7 +29,7 @@ func NewWorkingTreeCommands(
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) OpenMergeToolCmdObj() oscommands.ICmdObj {
|
||||
return self.cmd.New("git mergetool")
|
||||
return self.cmd.New(NewGitCmd("mergetool").ToString())
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) OpenMergeTool() error {
|
||||
@@ -49,30 +45,37 @@ func (self *WorkingTreeCommands) StageFiles(paths []string) error {
|
||||
quotedPaths := slices.Map(paths, func(path string) string {
|
||||
return self.cmd.Quote(path)
|
||||
})
|
||||
return self.cmd.New(fmt.Sprintf("git add -- %s", strings.Join(quotedPaths, " "))).Run()
|
||||
|
||||
cmdStr := NewGitCmd("add").Arg("--").Arg(quotedPaths...).ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// StageAll stages all files
|
||||
func (self *WorkingTreeCommands) StageAll() error {
|
||||
return self.cmd.New("git add -A").Run()
|
||||
cmdStr := NewGitCmd("add").Arg("-A").ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// UnstageAll unstages all files
|
||||
func (self *WorkingTreeCommands) UnstageAll() error {
|
||||
return self.cmd.New("git reset").Run()
|
||||
return self.cmd.New(NewGitCmd("reset").ToString()).Run()
|
||||
}
|
||||
|
||||
// UnStageFile unstages a file
|
||||
// we accept an array of filenames for the cases where a file has been renamed i.e.
|
||||
// we accept the current name and the previous name
|
||||
func (self *WorkingTreeCommands) UnStageFile(fileNames []string, reset bool) error {
|
||||
command := "git rm --cached --force -- %s"
|
||||
if reset {
|
||||
command = "git reset HEAD -- %s"
|
||||
}
|
||||
|
||||
for _, name := range fileNames {
|
||||
err := self.cmd.New(fmt.Sprintf(command, self.cmd.Quote(name))).Run()
|
||||
var cmdStr string
|
||||
if reset {
|
||||
cmdStr = NewGitCmd("reset").Arg("HEAD", "--", self.cmd.Quote(name)).ToString()
|
||||
} else {
|
||||
cmdStr = NewGitCmd("rm").Arg("--cached", "--force", "--", self.cmd.Quote(name)).ToString()
|
||||
}
|
||||
|
||||
err := self.cmd.New(cmdStr).Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -137,22 +140,31 @@ func (self *WorkingTreeCommands) DiscardAllFileChanges(file *models.File) error
|
||||
quotedFileName := self.cmd.Quote(file.Name)
|
||||
|
||||
if file.ShortStatus == "AA" {
|
||||
if err := self.cmd.New("git checkout --ours -- " + quotedFileName).Run(); err != nil {
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("checkout").Arg("--ours", "--", quotedFileName).ToString(),
|
||||
).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := self.cmd.New("git add -- " + quotedFileName).Run(); err != nil {
|
||||
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("add").Arg("--", quotedFileName).ToString(),
|
||||
).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if file.ShortStatus == "DU" {
|
||||
return self.cmd.New("git rm -- " + quotedFileName).Run()
|
||||
return self.cmd.New(
|
||||
NewGitCmd("rm").Arg("rm", "--", quotedFileName).ToString(),
|
||||
).Run()
|
||||
}
|
||||
|
||||
// if the file isn't tracked, we assume you want to delete it
|
||||
if file.HasStagedChanges || file.HasMergeConflicts {
|
||||
if err := self.cmd.New("git reset -- " + quotedFileName).Run(); err != nil {
|
||||
if err := self.cmd.New(
|
||||
NewGitCmd("reset").Arg("--", quotedFileName).ToString(),
|
||||
).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -184,7 +196,8 @@ func (self *WorkingTreeCommands) DiscardUnstagedDirChanges(node IFileNode) error
|
||||
}
|
||||
|
||||
quotedPath := self.cmd.Quote(node.GetPath())
|
||||
if err := self.cmd.New("git checkout -- " + quotedPath).Run(); err != nil {
|
||||
cmdStr := NewGitCmd("checkout").Arg("--", quotedPath).ToString()
|
||||
if err := self.cmd.New(cmdStr).Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -209,7 +222,8 @@ func (self *WorkingTreeCommands) RemoveUntrackedDirFiles(node IFileNode) error {
|
||||
// DiscardUnstagedFileChanges directly
|
||||
func (self *WorkingTreeCommands) DiscardUnstagedFileChanges(file *models.File) error {
|
||||
quotedFileName := self.cmd.Quote(file.Name)
|
||||
return self.cmd.New("git checkout -- " + quotedFileName).Run()
|
||||
cmdStr := NewGitCmd("checkout").Arg("--", quotedFileName).ToString()
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// Ignore adds a file to the gitignore for the repo
|
||||
@@ -230,61 +244,32 @@ func (self *WorkingTreeCommands) WorktreeFileDiff(file *models.File, plain bool,
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain bool, cached bool, ignoreWhitespace bool) oscommands.ICmdObj {
|
||||
cachedArg := ""
|
||||
trackedArg := "--"
|
||||
colorArg := self.UserConfig.Git.Paging.ColorArg
|
||||
quotedPath := self.cmd.Quote(node.GetPath())
|
||||
quotedPrevPath := ""
|
||||
ignoreWhitespaceArg := ""
|
||||
contextSize := self.UserConfig.Git.DiffContextSize
|
||||
if cached {
|
||||
cachedArg = " --cached"
|
||||
}
|
||||
if !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile() {
|
||||
trackedArg = "--no-index -- /dev/null"
|
||||
}
|
||||
if plain {
|
||||
colorArg = "never"
|
||||
}
|
||||
if ignoreWhitespace {
|
||||
ignoreWhitespaceArg = " --ignore-all-space"
|
||||
}
|
||||
if prevPath := node.GetPreviousPath(); prevPath != "" {
|
||||
quotedPrevPath = " " + self.cmd.Quote(prevPath)
|
||||
}
|
||||
|
||||
cmdStr := fmt.Sprintf("git diff --submodule --no-ext-diff --unified=%d --color=%s%s%s %s %s%s", contextSize, colorArg, ignoreWhitespaceArg, cachedArg, trackedArg, quotedPath, quotedPrevPath)
|
||||
contextSize := self.UserConfig.Git.DiffContextSize
|
||||
prevPath := node.GetPreviousPath()
|
||||
noIndex := !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile()
|
||||
|
||||
cmdStr := NewGitCmd("diff").
|
||||
Arg("--submodule").
|
||||
Arg("--no-ext-diff").
|
||||
Arg(fmt.Sprintf("--unified=%d", contextSize)).
|
||||
Arg(fmt.Sprintf("--color=%s", colorArg)).
|
||||
ArgIf(ignoreWhitespace, "--ignore-all-space").
|
||||
ArgIf(cached, "--cached").
|
||||
ArgIf(noIndex, "--no-index").
|
||||
Arg("--").
|
||||
ArgIf(noIndex, "/dev/null").
|
||||
Arg(self.cmd.Quote(node.GetPath())).
|
||||
ArgIf(prevPath != "", self.cmd.Quote(prevPath)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).DontLog()
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) ApplyPatch(patch string, flags ...string) error {
|
||||
filepath, err := self.SaveTemporaryPatch(patch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.ApplyPatchFile(filepath, flags...)
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) ApplyPatchFile(filepath string, flags ...string) error {
|
||||
flagStr := ""
|
||||
for _, flag := range flags {
|
||||
flagStr += " --" + flag
|
||||
}
|
||||
|
||||
return self.cmd.New(fmt.Sprintf("git apply%s %s", flagStr, self.cmd.Quote(filepath))).Run()
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) SaveTemporaryPatch(patch string) (string, error) {
|
||||
filepath := filepath.Join(self.os.GetTempDir(), utils.GetCurrentRepoName(), time.Now().Format("Jan _2 15.04.05.000000000")+".patch")
|
||||
self.Log.Infof("saving temporary patch to %s", filepath)
|
||||
if err := self.os.CreateFileWithContent(filepath, patch); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath, nil
|
||||
}
|
||||
|
||||
// ShowFileDiff get the diff of specified from and to. Typically this will be used for a single commit so it'll be 123abc^..123abc
|
||||
// but when we're in diff mode it could be any 'from' to any 'to'. The reverse flag is also here thanks to diff mode.
|
||||
func (self *WorkingTreeCommands) ShowFileDiff(from string, to string, reverse bool, fileName string, plain bool,
|
||||
@@ -296,49 +281,59 @@ func (self *WorkingTreeCommands) ShowFileDiff(from string, to string, reverse bo
|
||||
func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reverse bool, fileName string, plain bool,
|
||||
ignoreWhitespace bool,
|
||||
) oscommands.ICmdObj {
|
||||
colorArg := self.UserConfig.Git.Paging.ColorArg
|
||||
contextSize := self.UserConfig.Git.DiffContextSize
|
||||
|
||||
colorArg := self.UserConfig.Git.Paging.ColorArg
|
||||
if plain {
|
||||
colorArg = "never"
|
||||
}
|
||||
|
||||
reverseFlag := ""
|
||||
if reverse {
|
||||
reverseFlag = " -R"
|
||||
}
|
||||
cmdStr := NewGitCmd("diff").
|
||||
Arg("--submodule").
|
||||
Arg("--no-ext-diff").
|
||||
Arg(fmt.Sprintf("--unified=%d", contextSize)).
|
||||
Arg("--no-renames").
|
||||
Arg(fmt.Sprintf("--color=%s", colorArg)).
|
||||
Arg(from).
|
||||
Arg(to).
|
||||
ArgIf(reverse, "-R").
|
||||
ArgIf(ignoreWhitespace, "--ignore-all-space").
|
||||
Arg("--").
|
||||
Arg(self.cmd.Quote(fileName)).
|
||||
ToString()
|
||||
|
||||
ignoreWhitespaceFlag := ""
|
||||
if ignoreWhitespace {
|
||||
ignoreWhitespaceFlag = " --ignore-all-space"
|
||||
}
|
||||
|
||||
return self.cmd.
|
||||
New(
|
||||
fmt.Sprintf(
|
||||
"git diff --submodule --no-ext-diff --unified=%d --no-renames --color=%s%s%s%s%s -- %s",
|
||||
contextSize, colorArg, pad(from), pad(to), reverseFlag, ignoreWhitespaceFlag, self.cmd.Quote(fileName)),
|
||||
).
|
||||
DontLog()
|
||||
return self.cmd.New(cmdStr).DontLog()
|
||||
}
|
||||
|
||||
// CheckoutFile checks out the file for the given commit
|
||||
func (self *WorkingTreeCommands) CheckoutFile(commitSha, fileName string) error {
|
||||
return self.cmd.New(fmt.Sprintf("git checkout %s -- %s", commitSha, self.cmd.Quote(fileName))).Run()
|
||||
cmdStr := NewGitCmd("checkout").Arg(commitSha, "--", self.cmd.Quote(fileName)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// DiscardAnyUnstagedFileChanges discards any unstages file changes via `git checkout -- .`
|
||||
// DiscardAnyUnstagedFileChanges discards any unstaged file changes via `git checkout -- .`
|
||||
func (self *WorkingTreeCommands) DiscardAnyUnstagedFileChanges() error {
|
||||
return self.cmd.New("git checkout -- .").Run()
|
||||
cmdStr := NewGitCmd("checkout").Arg("--", ".").
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// RemoveTrackedFiles will delete the given file(s) even if they are currently tracked
|
||||
func (self *WorkingTreeCommands) RemoveTrackedFiles(name string) error {
|
||||
return self.cmd.New("git rm -r --cached -- " + self.cmd.Quote(name)).Run()
|
||||
cmdStr := NewGitCmd("rm").Arg("-r", "--cached", "--", self.cmd.Quote(name)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// RemoveUntrackedFiles runs `git clean -fd`
|
||||
func (self *WorkingTreeCommands) RemoveUntrackedFiles() error {
|
||||
return self.cmd.New("git clean -fd").Run()
|
||||
cmdStr := NewGitCmd("clean").Arg("-fd").ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// ResetAndClean removes all unstaged changes and removes all untracked files
|
||||
@@ -363,23 +358,23 @@ func (self *WorkingTreeCommands) ResetAndClean() error {
|
||||
|
||||
// ResetHardHead runs `git reset --hard`
|
||||
func (self *WorkingTreeCommands) ResetHard(ref string) error {
|
||||
return self.cmd.New("git reset --hard " + self.cmd.Quote(ref)).Run()
|
||||
cmdStr := NewGitCmd("reset").Arg("--hard", self.cmd.Quote(ref)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
// ResetSoft runs `git reset --soft HEAD`
|
||||
func (self *WorkingTreeCommands) ResetSoft(ref string) error {
|
||||
return self.cmd.New("git reset --soft " + self.cmd.Quote(ref)).Run()
|
||||
cmdStr := NewGitCmd("reset").Arg("--soft", self.cmd.Quote(ref)).
|
||||
ToString()
|
||||
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
func (self *WorkingTreeCommands) ResetMixed(ref string) error {
|
||||
return self.cmd.New("git reset --mixed " + self.cmd.Quote(ref)).Run()
|
||||
}
|
||||
cmdStr := NewGitCmd("reset").Arg("--mixed", self.cmd.Quote(ref)).
|
||||
ToString()
|
||||
|
||||
// so that we don't have unnecessary space in our commands we use this helper function to prepend spaces to args so that in the format string we can go '%s%s%s' and if any args are missing we won't have gaps.
|
||||
func pad(str string) string {
|
||||
if str == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return " " + str
|
||||
return self.cmd.New(cmdStr).Run()
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package git_commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
@@ -430,60 +428,6 @@ func TestWorkingTreeCheckoutFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkingTreeApplyPatch(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
runner *oscommands.FakeCmdObjRunner
|
||||
test func(error)
|
||||
}
|
||||
|
||||
expectFn := func(regexStr string, errToReturn error) func(cmdObj oscommands.ICmdObj) (string, error) {
|
||||
return func(cmdObj oscommands.ICmdObj) (string, error) {
|
||||
re := regexp.MustCompile(regexStr)
|
||||
cmdStr := cmdObj.ToString()
|
||||
matches := re.FindStringSubmatch(cmdStr)
|
||||
assert.Equal(t, 2, len(matches), fmt.Sprintf("unexpected command: %s", cmdStr))
|
||||
|
||||
filename := matches[1]
|
||||
|
||||
content, err := os.ReadFile(filename)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "test", string(content))
|
||||
|
||||
return "", errToReturn
|
||||
}
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "valid case",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectFunc(expectFn(`git apply --cached "(.*)"`, nil)),
|
||||
test: func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "command returns error",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
ExpectFunc(expectFn(`git apply --cached "(.*)"`, errors.New("error"))),
|
||||
test: func(err error) {
|
||||
assert.Error(t, err)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
instance := buildWorkingTreeCommands(commonDeps{runner: s.runner})
|
||||
s.test(instance.ApplyPatch("test", "cached"))
|
||||
s.runner.CheckForMissingCalls()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkingTreeDiscardUnstagedFileChanges(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
|
||||
@@ -29,7 +29,6 @@ type fileInfo struct {
|
||||
}
|
||||
|
||||
type (
|
||||
applyPatchFunc func(patch string, flags ...string) error
|
||||
loadFileDiffFunc func(from string, to string, reverse bool, filename string, plain bool) (string, error)
|
||||
)
|
||||
|
||||
@@ -47,17 +46,14 @@ type PatchBuilder struct {
|
||||
// fileInfoMap starts empty but you add files to it as you go along
|
||||
fileInfoMap map[string]*fileInfo
|
||||
Log *logrus.Entry
|
||||
applyPatch applyPatchFunc
|
||||
|
||||
// loadFileDiff loads the diff of a file, for a given to (typically a commit SHA)
|
||||
loadFileDiff loadFileDiffFunc
|
||||
}
|
||||
|
||||
// NewPatchBuilder returns a new PatchBuilder
|
||||
func NewPatchBuilder(log *logrus.Entry, applyPatch applyPatchFunc, loadFileDiff loadFileDiffFunc) *PatchBuilder {
|
||||
func NewPatchBuilder(log *logrus.Entry, loadFileDiff loadFileDiffFunc) *PatchBuilder {
|
||||
return &PatchBuilder{
|
||||
Log: log,
|
||||
applyPatch: applyPatch,
|
||||
loadFileDiff: loadFileDiff,
|
||||
}
|
||||
}
|
||||
@@ -70,6 +66,20 @@ func (p *PatchBuilder) Start(from, to string, reverse bool, canRebase bool) {
|
||||
p.fileInfoMap = map[string]*fileInfo{}
|
||||
}
|
||||
|
||||
func (p *PatchBuilder) PatchToApply(reverse bool) string {
|
||||
patch := ""
|
||||
|
||||
for filename, info := range p.fileInfoMap {
|
||||
if info.mode == UNSELECTED {
|
||||
continue
|
||||
}
|
||||
|
||||
patch += p.RenderPatchForFile(filename, true, reverse)
|
||||
}
|
||||
|
||||
return patch
|
||||
}
|
||||
|
||||
func (p *PatchBuilder) addFileWhole(info *fileInfo) {
|
||||
info.mode = WHOLE
|
||||
lineCount := len(strings.Split(info.diff, "\n"))
|
||||
@@ -234,25 +244,6 @@ func (p *PatchBuilder) GetFileIncLineIndices(filename string) ([]int, error) {
|
||||
return info.includedLineIndices, nil
|
||||
}
|
||||
|
||||
func (p *PatchBuilder) ApplyPatches(reverse bool) error {
|
||||
patch := ""
|
||||
|
||||
applyFlags := []string{"index", "3way"}
|
||||
if reverse {
|
||||
applyFlags = append(applyFlags, "reverse")
|
||||
}
|
||||
|
||||
for filename, info := range p.fileInfoMap {
|
||||
if info.mode == UNSELECTED {
|
||||
continue
|
||||
}
|
||||
|
||||
patch += p.RenderPatchForFile(filename, true, reverse)
|
||||
}
|
||||
|
||||
return p.applyPatch(patch, applyFlags...)
|
||||
}
|
||||
|
||||
// clears the patch
|
||||
func (p *PatchBuilder) Reset() {
|
||||
p.To = ""
|
||||
|
||||
@@ -35,6 +35,7 @@ type editPreset struct {
|
||||
editInTerminal bool
|
||||
}
|
||||
|
||||
// IF YOU ADD A PRESET TO THIS FUNCTION YOU MUST UPDATE THE `Supported presets` SECTION OF docs/Config.md
|
||||
func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset {
|
||||
presets := map[string]*editPreset{
|
||||
"vi": standardTerminalEditorPreset("vi"),
|
||||
|
||||
+44
-40
@@ -27,33 +27,34 @@ type RefresherConfig struct {
|
||||
}
|
||||
|
||||
type GuiConfig struct {
|
||||
AuthorColors map[string]string `yaml:"authorColors"`
|
||||
BranchColors map[string]string `yaml:"branchColors"`
|
||||
ScrollHeight int `yaml:"scrollHeight"`
|
||||
ScrollPastBottom bool `yaml:"scrollPastBottom"`
|
||||
MouseEvents bool `yaml:"mouseEvents"`
|
||||
SkipUnstageLineWarning bool `yaml:"skipUnstageLineWarning"`
|
||||
SkipStashWarning bool `yaml:"skipStashWarning"`
|
||||
SidePanelWidth float64 `yaml:"sidePanelWidth"`
|
||||
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
|
||||
MainPanelSplitMode string `yaml:"mainPanelSplitMode"`
|
||||
Language string `yaml:"language"`
|
||||
TimeFormat string `yaml:"timeFormat"`
|
||||
Theme ThemeConfig `yaml:"theme"`
|
||||
CommitLength CommitLengthConfig `yaml:"commitLength"`
|
||||
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
|
||||
ShowListFooter bool `yaml:"showListFooter"`
|
||||
ShowFileTree bool `yaml:"showFileTree"`
|
||||
ShowRandomTip bool `yaml:"showRandomTip"`
|
||||
ShowCommandLog bool `yaml:"showCommandLog"`
|
||||
ShowBottomLine bool `yaml:"showBottomLine"`
|
||||
ShowIcons bool `yaml:"showIcons"`
|
||||
ExperimentalShowBranchHeads bool `yaml:"experimentalShowBranchHeads"`
|
||||
CommandLogSize int `yaml:"commandLogSize"`
|
||||
SplitDiff string `yaml:"splitDiff"`
|
||||
SkipRewordInEditorWarning bool `yaml:"skipRewordInEditorWarning"`
|
||||
WindowSize string `yaml:"windowSize"`
|
||||
Border string `yaml:"border"`
|
||||
AuthorColors map[string]string `yaml:"authorColors"`
|
||||
BranchColors map[string]string `yaml:"branchColors"`
|
||||
ScrollHeight int `yaml:"scrollHeight"`
|
||||
ScrollPastBottom bool `yaml:"scrollPastBottom"`
|
||||
MouseEvents bool `yaml:"mouseEvents"`
|
||||
SkipUnstageLineWarning bool `yaml:"skipUnstageLineWarning"`
|
||||
SkipStashWarning bool `yaml:"skipStashWarning"`
|
||||
SidePanelWidth float64 `yaml:"sidePanelWidth"`
|
||||
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
|
||||
MainPanelSplitMode string `yaml:"mainPanelSplitMode"`
|
||||
Language string `yaml:"language"`
|
||||
TimeFormat string `yaml:"timeFormat"`
|
||||
Theme ThemeConfig `yaml:"theme"`
|
||||
CommitLength CommitLengthConfig `yaml:"commitLength"`
|
||||
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
|
||||
ShowListFooter bool `yaml:"showListFooter"`
|
||||
ShowFileTree bool `yaml:"showFileTree"`
|
||||
ShowRandomTip bool `yaml:"showRandomTip"`
|
||||
ShowCommandLog bool `yaml:"showCommandLog"`
|
||||
ShowBottomLine bool `yaml:"showBottomLine"`
|
||||
ShowIcons bool `yaml:"showIcons"`
|
||||
ExperimentalShowBranchHeads bool `yaml:"experimentalShowBranchHeads"`
|
||||
CommandLogSize int `yaml:"commandLogSize"`
|
||||
SplitDiff string `yaml:"splitDiff"`
|
||||
SkipRewordInEditorWarning bool `yaml:"skipRewordInEditorWarning"`
|
||||
WindowSize string `yaml:"windowSize"`
|
||||
Border string `yaml:"border"`
|
||||
ExperimentalMarkCommitsWhichChangedFile bool `yaml:"experimentalMarkCommitsWhichChangedFile"`
|
||||
}
|
||||
|
||||
type ThemeConfig struct {
|
||||
@@ -76,6 +77,7 @@ type GitConfig struct {
|
||||
Paging PagingConfig `yaml:"paging"`
|
||||
Commit CommitConfig `yaml:"commit"`
|
||||
Merging MergingConfig `yaml:"merging"`
|
||||
MainBranches []string `yaml:"mainBranches"`
|
||||
SkipHookPrefix string `yaml:"skipHookPrefix"`
|
||||
AutoFetch bool `yaml:"autoFetch"`
|
||||
AutoRefresh bool `yaml:"autoRefresh"`
|
||||
@@ -409,19 +411,20 @@ func GetDefaultConfig() *UserConfig {
|
||||
UnstagedChangesColor: []string{"red"},
|
||||
DefaultFgColor: []string{"default"},
|
||||
},
|
||||
CommitLength: CommitLengthConfig{Show: true},
|
||||
SkipNoStagedFilesWarning: false,
|
||||
ShowListFooter: true,
|
||||
ShowCommandLog: true,
|
||||
ShowBottomLine: true,
|
||||
ShowFileTree: true,
|
||||
ShowRandomTip: true,
|
||||
ShowIcons: false,
|
||||
ExperimentalShowBranchHeads: false,
|
||||
CommandLogSize: 8,
|
||||
SplitDiff: "auto",
|
||||
SkipRewordInEditorWarning: false,
|
||||
Border: "single",
|
||||
CommitLength: CommitLengthConfig{Show: true},
|
||||
SkipNoStagedFilesWarning: false,
|
||||
ShowListFooter: true,
|
||||
ShowCommandLog: true,
|
||||
ShowBottomLine: true,
|
||||
ShowFileTree: true,
|
||||
ShowRandomTip: true,
|
||||
ShowIcons: false,
|
||||
ExperimentalShowBranchHeads: false,
|
||||
CommandLogSize: 8,
|
||||
SplitDiff: "auto",
|
||||
SkipRewordInEditorWarning: false,
|
||||
Border: "single",
|
||||
ExperimentalMarkCommitsWhichChangedFile: false,
|
||||
},
|
||||
Git: GitConfig{
|
||||
Paging: PagingConfig{
|
||||
@@ -443,6 +446,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
ShowWholeGraph: false,
|
||||
},
|
||||
SkipHookPrefix: "WIP",
|
||||
MainBranches: []string{"master", "main"},
|
||||
AutoFetch: true,
|
||||
AutoRefresh: true,
|
||||
BranchLogCmd: "git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --",
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
)
|
||||
|
||||
// statusManager's job is to handle rendering of loading states and toast notifications
|
||||
// that you see at the bottom left of the screen.
|
||||
type statusManager struct {
|
||||
statuses []appStatus
|
||||
nextId int
|
||||
mutex deadlock.Mutex
|
||||
}
|
||||
|
||||
type appStatus struct {
|
||||
message string
|
||||
statusType string
|
||||
id int
|
||||
}
|
||||
|
||||
func (m *statusManager) removeStatus(id int) {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
m.statuses = slices.Filter(m.statuses, func(status appStatus) bool {
|
||||
return status.id != id
|
||||
})
|
||||
}
|
||||
|
||||
func (m *statusManager) addWaitingStatus(message string) int {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
m.nextId += 1
|
||||
id := m.nextId
|
||||
|
||||
newStatus := appStatus{
|
||||
message: message,
|
||||
statusType: "waiting",
|
||||
id: id,
|
||||
}
|
||||
m.statuses = append([]appStatus{newStatus}, m.statuses...)
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func (m *statusManager) addToastStatus(message string) int {
|
||||
m.mutex.Lock()
|
||||
defer m.mutex.Unlock()
|
||||
|
||||
m.nextId++
|
||||
id := m.nextId
|
||||
|
||||
newStatus := appStatus{
|
||||
message: message,
|
||||
statusType: "toast",
|
||||
id: id,
|
||||
}
|
||||
m.statuses = append([]appStatus{newStatus}, m.statuses...)
|
||||
|
||||
go func() {
|
||||
time.Sleep(time.Second * 2)
|
||||
|
||||
m.removeStatus(id)
|
||||
}()
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func (m *statusManager) getStatusString() string {
|
||||
if len(m.statuses) == 0 {
|
||||
return ""
|
||||
}
|
||||
topStatus := m.statuses[0]
|
||||
if topStatus.statusType == "waiting" {
|
||||
return topStatus.message + " " + utils.Loader()
|
||||
}
|
||||
return topStatus.message
|
||||
}
|
||||
|
||||
func (m *statusManager) showStatus() bool {
|
||||
return len(m.statuses) > 0
|
||||
}
|
||||
|
||||
func (gui *Gui) toast(message string) {
|
||||
gui.statusManager.addToastStatus(message)
|
||||
|
||||
gui.renderAppStatus()
|
||||
}
|
||||
|
||||
func (gui *Gui) renderAppStatus() {
|
||||
go utils.Safe(func() {
|
||||
ticker := time.NewTicker(time.Millisecond * 50)
|
||||
defer ticker.Stop()
|
||||
for range ticker.C {
|
||||
appStatus := gui.statusManager.getStatusString()
|
||||
gui.c.OnUIThread(func() error {
|
||||
return gui.renderString(gui.Views.AppStatus, appStatus)
|
||||
})
|
||||
|
||||
if appStatus == "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// withWaitingStatus wraps a function and shows a waiting status while the function is still executing
|
||||
func (gui *Gui) withWaitingStatus(message string, f func() error) error {
|
||||
go utils.Safe(func() {
|
||||
id := gui.statusManager.addWaitingStatus(message)
|
||||
|
||||
defer func() {
|
||||
gui.statusManager.removeStatus(id)
|
||||
}()
|
||||
|
||||
gui.renderAppStatus()
|
||||
|
||||
if err := f(); err != nil {
|
||||
gui.c.OnUIThread(func() error {
|
||||
return gui.c.Error(err)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
+42
-17
@@ -4,18 +4,33 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func (gui *Gui) startBackgroundRoutines() {
|
||||
userConfig := gui.UserConfig
|
||||
type BackgroundRoutineMgr struct {
|
||||
gui *Gui
|
||||
|
||||
// if we've suspended the gui (e.g. because we've switched to a subprocess)
|
||||
// we typically want to pause some things that are running like background
|
||||
// file refreshes
|
||||
pauseBackgroundThreads bool
|
||||
}
|
||||
|
||||
func (self *BackgroundRoutineMgr) PauseBackgroundThreads(pause bool) {
|
||||
self.pauseBackgroundThreads = pause
|
||||
}
|
||||
|
||||
func (self *BackgroundRoutineMgr) startBackgroundRoutines() {
|
||||
userConfig := self.gui.UserConfig
|
||||
|
||||
if userConfig.Git.AutoFetch {
|
||||
fetchInterval := userConfig.Refresher.FetchInterval
|
||||
if fetchInterval > 0 {
|
||||
go utils.Safe(gui.startBackgroundFetch)
|
||||
go utils.Safe(self.startBackgroundFetch)
|
||||
} else {
|
||||
gui.c.Log.Errorf(
|
||||
self.gui.c.Log.Errorf(
|
||||
"Value of config option 'refresher.fetchInterval' (%d) is invalid, disabling auto-fetch",
|
||||
fetchInterval)
|
||||
}
|
||||
@@ -24,42 +39,44 @@ func (gui *Gui) startBackgroundRoutines() {
|
||||
if userConfig.Git.AutoRefresh {
|
||||
refreshInterval := userConfig.Refresher.RefreshInterval
|
||||
if refreshInterval > 0 {
|
||||
gui.goEvery(time.Second*time.Duration(refreshInterval), gui.stopChan, gui.refreshFilesAndSubmodules)
|
||||
self.goEvery(time.Second*time.Duration(refreshInterval), self.gui.stopChan, func() error {
|
||||
return self.gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}})
|
||||
})
|
||||
} else {
|
||||
gui.c.Log.Errorf(
|
||||
self.gui.c.Log.Errorf(
|
||||
"Value of config option 'refresher.refreshInterval' (%d) is invalid, disabling auto-refresh",
|
||||
refreshInterval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) startBackgroundFetch() {
|
||||
gui.waitForIntro.Wait()
|
||||
isNew := gui.IsNewRepo
|
||||
userConfig := gui.UserConfig
|
||||
func (self *BackgroundRoutineMgr) startBackgroundFetch() {
|
||||
self.gui.waitForIntro.Wait()
|
||||
isNew := self.gui.IsNewRepo
|
||||
userConfig := self.gui.UserConfig
|
||||
if !isNew {
|
||||
time.After(time.Duration(userConfig.Refresher.FetchInterval) * time.Second)
|
||||
}
|
||||
err := gui.backgroundFetch()
|
||||
err := self.backgroundFetch()
|
||||
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
|
||||
_ = gui.c.Alert(gui.c.Tr.NoAutomaticGitFetchTitle, gui.c.Tr.NoAutomaticGitFetchBody)
|
||||
_ = self.gui.c.Alert(self.gui.c.Tr.NoAutomaticGitFetchTitle, self.gui.c.Tr.NoAutomaticGitFetchBody)
|
||||
} else {
|
||||
gui.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), gui.stopChan, func() error {
|
||||
err := gui.backgroundFetch()
|
||||
gui.render()
|
||||
self.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), self.gui.stopChan, func() error {
|
||||
err := self.backgroundFetch()
|
||||
self.gui.c.Render()
|
||||
return err
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) goEvery(interval time.Duration, stop chan struct{}, function func() error) {
|
||||
func (self *BackgroundRoutineMgr) goEvery(interval time.Duration, stop chan struct{}, function func() error) {
|
||||
go utils.Safe(func() {
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if gui.PauseBackgroundThreads {
|
||||
if self.pauseBackgroundThreads {
|
||||
continue
|
||||
}
|
||||
_ = function()
|
||||
@@ -69,3 +86,11 @@ func (gui *Gui) goEvery(interval time.Duration, stop chan struct{}, function fun
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (self *BackgroundRoutineMgr) backgroundFetch() (err error) {
|
||||
err = self.gui.git.Sync.Fetch(git_commands.FetchOptions{Background: true})
|
||||
|
||||
_ = self.gui.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.BRANCHES, types.COMMITS, types.REMOTES, types.TAGS}, Mode: types.ASYNC})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
package gui
|
||||
|
||||
import "github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
|
||||
func (gui *Gui) branchesRenderToMain() error {
|
||||
var task types.UpdateTask
|
||||
branch := gui.State.Contexts.Branches.GetSelected()
|
||||
if branch == nil {
|
||||
task = types.NewRenderStringTask(gui.c.Tr.NoBranchesThisRepo)
|
||||
} else {
|
||||
cmdObj := gui.git.Branch.GetGraphCmdObj(branch.FullRefName())
|
||||
|
||||
task = types.NewRunPtyTask(cmdObj.GetCmd())
|
||||
}
|
||||
|
||||
return gui.c.RenderToMainViews(types.RefreshMainOpts{
|
||||
Pair: gui.c.MainViewPairs().Normal,
|
||||
Main: &types.ViewUpdateOpts{
|
||||
Title: gui.c.Tr.LogTitle,
|
||||
Task: task,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -30,6 +30,7 @@ func (gui *Gui) LogAction(action string) {
|
||||
|
||||
gui.Views.Extras.Autoscroll = true
|
||||
|
||||
gui.GuiLog = append(gui.GuiLog, action)
|
||||
fmt.Fprint(gui.Views.Extras, "\n"+style.FgYellow.Sprint(action))
|
||||
}
|
||||
|
||||
@@ -46,7 +47,7 @@ func (gui *Gui) LogCommand(cmdStr string, commandLine bool) {
|
||||
// we style it differently to communicate that
|
||||
textStyle = style.FgMagenta
|
||||
}
|
||||
gui.CmdLog = append(gui.CmdLog, cmdStr)
|
||||
gui.GuiLog = append(gui.GuiLog, cmdStr)
|
||||
indentedCmdStr := " " + strings.Replace(cmdStr, "\n", "\n ", -1)
|
||||
fmt.Fprint(gui.Views.Extras, "\n"+textStyle.Sprint(indentedCmdStr))
|
||||
}
|
||||
@@ -103,7 +104,7 @@ func (gui *Gui) getRandomTip() string {
|
||||
),
|
||||
fmt.Sprintf(
|
||||
"to hard reset onto your current upstream branch, press '%s' in the files panel",
|
||||
formattedKey(config.Files.ViewResetOptions),
|
||||
formattedKey(config.Commits.ViewResetOptions),
|
||||
),
|
||||
fmt.Sprintf(
|
||||
"To push a tag, navigate to the tag in the tags tab and press '%s'",
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
func (gui *Gui) commitFilesRenderToMain() error {
|
||||
node := gui.State.Contexts.CommitFiles.GetSelected()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ref := gui.State.Contexts.CommitFiles.GetRef()
|
||||
to := ref.RefName()
|
||||
from, reverse := gui.State.Modes.Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
||||
|
||||
cmdObj := gui.git.WorkingTree.ShowFileDiffCmdObj(from, to, reverse, node.GetPath(), false,
|
||||
gui.IgnoreWhitespaceInDiffView)
|
||||
task := types.NewRunPtyTask(cmdObj.GetCmd())
|
||||
|
||||
pair := gui.c.MainViewPairs().Normal
|
||||
if node.File != nil {
|
||||
pair = gui.c.MainViewPairs().PatchBuilding
|
||||
}
|
||||
|
||||
return gui.c.RenderToMainViews(types.RefreshMainOpts{
|
||||
Pair: pair,
|
||||
Main: &types.ViewUpdateOpts{
|
||||
Title: gui.Tr.Patch,
|
||||
Task: task,
|
||||
},
|
||||
Secondary: gui.secondaryPatchPanelUpdateOpts(),
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) SwitchToCommitFilesContext(opts controllers.SwitchToCommitFilesContextOpts) error {
|
||||
gui.State.Contexts.CommitFiles.SetSelectedLineIdx(0)
|
||||
gui.State.Contexts.CommitFiles.SetRef(opts.Ref)
|
||||
gui.State.Contexts.CommitFiles.SetTitleRef(opts.Ref.Description())
|
||||
gui.State.Contexts.CommitFiles.SetCanRebase(opts.CanRebase)
|
||||
gui.State.Contexts.CommitFiles.SetParentContext(opts.Context)
|
||||
gui.State.Contexts.CommitFiles.SetWindowName(opts.Context.GetWindowName())
|
||||
|
||||
if err := gui.c.Refresh(types.RefreshOptions{
|
||||
Scope: []types.RefreshableView{types.COMMIT_FILES},
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.c.PushContext(gui.State.Contexts.CommitFiles)
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func (gui *Gui) handleCommitMessageFocused() error {
|
||||
message := utils.ResolvePlaceholderString(
|
||||
gui.c.Tr.CommitMessageConfirm,
|
||||
map[string]string{
|
||||
"keyBindClose": keybindings.Label(gui.c.UserConfig.Keybinding.Universal.Return),
|
||||
"keyBindConfirm": keybindings.Label(gui.c.UserConfig.Keybinding.Universal.Confirm),
|
||||
},
|
||||
)
|
||||
|
||||
gui.RenderCommitLength()
|
||||
|
||||
return gui.renderString(gui.Views.Options, message)
|
||||
}
|
||||
|
||||
func (gui *Gui) RenderCommitLength() {
|
||||
if !gui.c.UserConfig.Gui.CommitLength.Show {
|
||||
return
|
||||
}
|
||||
|
||||
gui.Views.CommitMessage.Subtitle = getBufferLength(gui.Views.CommitMessage)
|
||||
}
|
||||
|
||||
func getBufferLength(view *gocui.View) string {
|
||||
return " " + strconv.Itoa(strings.Count(view.TextArea.GetContent(), "")-1) + " "
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"github.com/fsmiamoto/git-todo-parser/todo"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// after selecting the 200th commit, we'll load in all the rest
|
||||
const COMMIT_THRESHOLD = 200
|
||||
|
||||
// list panel functions
|
||||
|
||||
func (gui *Gui) getSelectedLocalCommit() *models.Commit {
|
||||
return gui.State.Contexts.LocalCommits.GetSelected()
|
||||
}
|
||||
|
||||
func (gui *Gui) onCommitFocus() error {
|
||||
context := gui.State.Contexts.LocalCommits
|
||||
if context.GetSelectedLineIdx() > COMMIT_THRESHOLD && context.GetLimitCommits() {
|
||||
context.SetLimitCommits(false)
|
||||
go utils.Safe(func() {
|
||||
if err := gui.refreshCommitsWithLimit(); err != nil {
|
||||
_ = gui.c.Error(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) branchCommitsRenderToMain() error {
|
||||
var task types.UpdateTask
|
||||
commit := gui.State.Contexts.LocalCommits.GetSelected()
|
||||
if commit == nil {
|
||||
task = types.NewRenderStringTask(gui.c.Tr.NoCommitsThisBranch)
|
||||
} else if commit.Action == todo.UpdateRef {
|
||||
task = types.NewRenderStringTask(
|
||||
utils.ResolvePlaceholderString(
|
||||
gui.c.Tr.UpdateRefHere,
|
||||
map[string]string{
|
||||
"ref": commit.Name,
|
||||
}))
|
||||
} else {
|
||||
cmdObj := gui.git.Commit.ShowCmdObj(commit.Sha, gui.State.Modes.Filtering.GetPath(),
|
||||
gui.IgnoreWhitespaceInDiffView)
|
||||
task = types.NewRunPtyTask(cmdObj.GetCmd())
|
||||
}
|
||||
|
||||
return gui.c.RenderToMainViews(types.RefreshMainOpts{
|
||||
Pair: gui.c.MainViewPairs().Normal,
|
||||
Main: &types.ViewUpdateOpts{
|
||||
Title: "Patch",
|
||||
Task: task,
|
||||
},
|
||||
Secondary: gui.secondaryPatchPanelUpdateOpts(),
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) secondaryPatchPanelUpdateOpts() *types.ViewUpdateOpts {
|
||||
if gui.git.Patch.PatchBuilder.Active() {
|
||||
patch := gui.git.Patch.PatchBuilder.RenderAggregatedPatch(false)
|
||||
|
||||
return &types.ViewUpdateOpts{
|
||||
Task: types.NewRenderStringWithoutScrollTask(patch),
|
||||
Title: gui.Tr.CustomPatch,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) refForLog() string {
|
||||
bisectInfo := gui.git.Bisect.GetInfo()
|
||||
gui.State.Model.BisectInfo = bisectInfo
|
||||
|
||||
if !bisectInfo.Started() {
|
||||
return "HEAD"
|
||||
}
|
||||
|
||||
// need to see if our bisect's current commit is reachable from our 'new' ref.
|
||||
if bisectInfo.Bisecting() && !gui.git.Bisect.ReachableFromStart(bisectInfo) {
|
||||
return bisectInfo.GetNewSha()
|
||||
}
|
||||
|
||||
return bisectInfo.GetStartSha()
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
// This file is for the rendering of confirmation panels along with setting and handling associated
|
||||
// keybindings.
|
||||
|
||||
func (gui *Gui) wrappedConfirmationFunction(cancel context.CancelFunc, function func() error) func() error {
|
||||
return func() error {
|
||||
cancel()
|
||||
|
||||
if err := gui.c.PopContext(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if function != nil {
|
||||
if err := function(); err != nil {
|
||||
return gui.c.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) wrappedPromptConfirmationFunction(cancel context.CancelFunc, function func(string) error, getResponse func() string) func() error {
|
||||
return func() error {
|
||||
cancel()
|
||||
|
||||
if err := gui.c.PopContext(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if function != nil {
|
||||
if err := function(getResponse()); err != nil {
|
||||
return gui.c.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) deactivateConfirmationPrompt() {
|
||||
gui.Mutexes.PopupMutex.Lock()
|
||||
gui.State.CurrentPopupOpts = nil
|
||||
gui.Mutexes.PopupMutex.Unlock()
|
||||
|
||||
gui.Views.Confirmation.Visible = false
|
||||
gui.Views.Suggestions.Visible = false
|
||||
|
||||
gui.clearConfirmationViewKeyBindings()
|
||||
}
|
||||
|
||||
func (gui *Gui) getMessageHeight(wrap bool, message string, width int) int {
|
||||
lines := strings.Split(message, "\n")
|
||||
lineCount := 0
|
||||
// if we need to wrap, calculate height to fit content within view's width
|
||||
if wrap {
|
||||
for _, line := range lines {
|
||||
lineCount += runewidth.StringWidth(line)/width + 1
|
||||
}
|
||||
} else {
|
||||
lineCount = len(lines)
|
||||
}
|
||||
return lineCount
|
||||
}
|
||||
|
||||
func (gui *Gui) getPopupPanelDimensionsForContentHeight(panelWidth, contentHeight int) (int, int, int, int) {
|
||||
return gui.getPopupPanelDimensionsAux(panelWidth, contentHeight)
|
||||
}
|
||||
|
||||
func (gui *Gui) getPopupPanelDimensionsAux(panelWidth int, panelHeight int) (int, int, int, int) {
|
||||
width, height := gui.g.Size()
|
||||
if panelHeight > height*3/4 {
|
||||
panelHeight = height * 3 / 4
|
||||
}
|
||||
return width/2 - panelWidth/2,
|
||||
height/2 - panelHeight/2 - panelHeight%2 - 1,
|
||||
width/2 + panelWidth/2,
|
||||
height/2 + panelHeight/2
|
||||
}
|
||||
|
||||
func (gui *Gui) getConfirmationPanelWidth() int {
|
||||
width, _ := gui.g.Size()
|
||||
// we want a minimum width up to a point, then we do it based on ratio.
|
||||
panelWidth := 4 * width / 7
|
||||
minWidth := 80
|
||||
if panelWidth < minWidth {
|
||||
if width-2 < minWidth {
|
||||
panelWidth = width - 2
|
||||
} else {
|
||||
panelWidth = minWidth
|
||||
}
|
||||
}
|
||||
|
||||
return panelWidth
|
||||
}
|
||||
|
||||
func (gui *Gui) prepareConfirmationPanel(
|
||||
ctx context.Context,
|
||||
opts types.ConfirmOpts,
|
||||
) error {
|
||||
gui.Views.Confirmation.HasLoader = opts.HasLoader
|
||||
if opts.HasLoader {
|
||||
gui.g.StartTicking(ctx)
|
||||
}
|
||||
gui.Views.Confirmation.Title = opts.Title
|
||||
// for now we do not support wrapping in our editor
|
||||
gui.Views.Confirmation.Wrap = !opts.Editable
|
||||
gui.Views.Confirmation.FgColor = theme.GocuiDefaultTextColor
|
||||
gui.Views.Confirmation.Mask = runeForMask(opts.Mask)
|
||||
_ = gui.Views.Confirmation.SetOrigin(0, 0)
|
||||
|
||||
gui.findSuggestions = opts.FindSuggestionsFunc
|
||||
if opts.FindSuggestionsFunc != nil {
|
||||
suggestionsView := gui.Views.Suggestions
|
||||
suggestionsView.Wrap = false
|
||||
suggestionsView.FgColor = theme.GocuiDefaultTextColor
|
||||
gui.setSuggestions(opts.FindSuggestionsFunc(""))
|
||||
suggestionsView.Visible = true
|
||||
suggestionsView.Title = fmt.Sprintf(gui.c.Tr.SuggestionsTitle, gui.c.UserConfig.Keybinding.Universal.TogglePanel)
|
||||
}
|
||||
|
||||
gui.resizeConfirmationPanel()
|
||||
return nil
|
||||
}
|
||||
|
||||
func runeForMask(mask bool) rune {
|
||||
if mask {
|
||||
return '*'
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (gui *Gui) createPopupPanel(ctx context.Context, opts types.CreatePopupPanelOpts) error {
|
||||
gui.Mutexes.PopupMutex.Lock()
|
||||
defer gui.Mutexes.PopupMutex.Unlock()
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
||||
// we don't allow interruptions of non-loader popups in case we get stuck somehow
|
||||
// e.g. a credentials popup never gets its required user input so a process hangs
|
||||
// forever.
|
||||
// The proper solution is to have a queue of popup options
|
||||
if gui.State.CurrentPopupOpts != nil && !gui.State.CurrentPopupOpts.HasLoader {
|
||||
gui.Log.Error("ignoring create popup panel because a popup panel is already open")
|
||||
cancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
// remove any previous keybindings
|
||||
gui.clearConfirmationViewKeyBindings()
|
||||
|
||||
err := gui.prepareConfirmationPanel(
|
||||
ctx,
|
||||
types.ConfirmOpts{
|
||||
Title: opts.Title,
|
||||
Prompt: opts.Prompt,
|
||||
HasLoader: opts.HasLoader,
|
||||
FindSuggestionsFunc: opts.FindSuggestionsFunc,
|
||||
Editable: opts.Editable,
|
||||
Mask: opts.Mask,
|
||||
})
|
||||
if err != nil {
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
confirmationView := gui.Views.Confirmation
|
||||
confirmationView.Editable = opts.Editable
|
||||
confirmationView.Editor = gocui.EditorFunc(gui.promptEditor)
|
||||
|
||||
if opts.Editable {
|
||||
textArea := confirmationView.TextArea
|
||||
textArea.Clear()
|
||||
textArea.TypeString(opts.Prompt)
|
||||
gui.resizeConfirmationPanel()
|
||||
confirmationView.RenderTextArea()
|
||||
} else {
|
||||
if err := gui.renderString(confirmationView, style.AttrBold.Sprint(opts.Prompt)); err != nil {
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := gui.setKeyBindings(cancel, opts); err != nil {
|
||||
cancel()
|
||||
return err
|
||||
}
|
||||
|
||||
gui.State.CurrentPopupOpts = &opts
|
||||
|
||||
return gui.c.PushContext(gui.State.Contexts.Confirmation)
|
||||
}
|
||||
|
||||
func (gui *Gui) setKeyBindings(cancel context.CancelFunc, opts types.CreatePopupPanelOpts) error {
|
||||
actions := utils.ResolvePlaceholderString(
|
||||
gui.c.Tr.CloseConfirm,
|
||||
map[string]string{
|
||||
"keyBindClose": "esc",
|
||||
"keyBindConfirm": "enter",
|
||||
},
|
||||
)
|
||||
|
||||
_ = gui.renderString(gui.Views.Options, actions)
|
||||
var onConfirm func() error
|
||||
if opts.HandleConfirmPrompt != nil {
|
||||
onConfirm = gui.wrappedPromptConfirmationFunction(cancel, opts.HandleConfirmPrompt, func() string { return gui.Views.Confirmation.TextArea.GetContent() })
|
||||
} else {
|
||||
onConfirm = gui.wrappedConfirmationFunction(cancel, opts.HandleConfirm)
|
||||
}
|
||||
|
||||
keybindingConfig := gui.c.UserConfig.Keybinding
|
||||
onSuggestionConfirm := gui.wrappedPromptConfirmationFunction(
|
||||
cancel,
|
||||
opts.HandleConfirmPrompt,
|
||||
gui.getSelectedSuggestionValue,
|
||||
)
|
||||
|
||||
bindings := []*types.Binding{
|
||||
{
|
||||
ViewName: "confirmation",
|
||||
Key: keybindings.GetKey(keybindingConfig.Universal.Confirm),
|
||||
Handler: onConfirm,
|
||||
},
|
||||
{
|
||||
ViewName: "confirmation",
|
||||
Key: keybindings.GetKey(keybindingConfig.Universal.Return),
|
||||
Handler: gui.wrappedConfirmationFunction(cancel, opts.HandleClose),
|
||||
},
|
||||
{
|
||||
ViewName: "confirmation",
|
||||
Key: keybindings.GetKey(keybindingConfig.Universal.TogglePanel),
|
||||
Handler: func() error {
|
||||
if len(gui.State.Suggestions) > 0 {
|
||||
return gui.replaceContext(gui.State.Contexts.Suggestions)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
ViewName: "suggestions",
|
||||
Key: keybindings.GetKey(keybindingConfig.Universal.Confirm),
|
||||
Handler: onSuggestionConfirm,
|
||||
},
|
||||
{
|
||||
ViewName: "suggestions",
|
||||
Key: keybindings.GetKey(keybindingConfig.Universal.Return),
|
||||
Handler: gui.wrappedConfirmationFunction(cancel, opts.HandleClose),
|
||||
},
|
||||
{
|
||||
ViewName: "suggestions",
|
||||
Key: keybindings.GetKey(keybindingConfig.Universal.TogglePanel),
|
||||
Handler: func() error { return gui.replaceContext(gui.State.Contexts.Confirmation) },
|
||||
},
|
||||
}
|
||||
|
||||
for _, binding := range bindings {
|
||||
if err := gui.SetKeybinding(binding); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) clearConfirmationViewKeyBindings() {
|
||||
keybindingConfig := gui.c.UserConfig.Keybinding
|
||||
_ = gui.g.DeleteKeybinding("confirmation", keybindings.GetKey(keybindingConfig.Universal.Confirm), gocui.ModNone)
|
||||
_ = gui.g.DeleteKeybinding("confirmation", keybindings.GetKey(keybindingConfig.Universal.Return), gocui.ModNone)
|
||||
_ = gui.g.DeleteKeybinding("suggestions", keybindings.GetKey(keybindingConfig.Universal.Confirm), gocui.ModNone)
|
||||
_ = gui.g.DeleteKeybinding("suggestions", keybindings.GetKey(keybindingConfig.Universal.Return), gocui.ModNone)
|
||||
}
|
||||
|
||||
func (gui *Gui) refreshSuggestions() {
|
||||
gui.suggestionsAsyncHandler.Do(func() func() {
|
||||
findSuggestionsFn := gui.findSuggestions
|
||||
if findSuggestionsFn != nil {
|
||||
suggestions := gui.findSuggestions(gui.c.GetPromptInput())
|
||||
return func() { gui.setSuggestions(suggestions) }
|
||||
} else {
|
||||
return func() {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleAskFocused() error {
|
||||
keybindingConfig := gui.c.UserConfig.Keybinding
|
||||
|
||||
message := utils.ResolvePlaceholderString(
|
||||
gui.c.Tr.CloseConfirm,
|
||||
map[string]string{
|
||||
"keyBindClose": keybindings.Label(keybindingConfig.Universal.Return),
|
||||
"keyBindConfirm": keybindings.Label(keybindingConfig.Universal.Confirm),
|
||||
},
|
||||
)
|
||||
|
||||
return gui.renderString(gui.Views.Options, message)
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package constants
|
||||
|
||||
const SEARCH_PREFIX = "search: "
|
||||
+132
-215
@@ -1,12 +1,10 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"strings"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/jesseduffield/generics/maps"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/samber/lo"
|
||||
@@ -17,46 +15,66 @@ import (
|
||||
// you in the menu context. When contexts are activated/deactivated certain things need
|
||||
// to happen like showing/hiding views and rendering content.
|
||||
|
||||
func (gui *Gui) popupViewNames() []string {
|
||||
popups := slices.Filter(gui.State.Contexts.Flatten(), func(c types.Context) bool {
|
||||
return c.GetKind() == types.PERSISTENT_POPUP || c.GetKind() == types.TEMPORARY_POPUP
|
||||
})
|
||||
type ContextMgr struct {
|
||||
ContextStack []types.Context
|
||||
sync.RWMutex
|
||||
gui *Gui
|
||||
|
||||
return slices.Map(popups, func(c types.Context) string {
|
||||
return c.GetViewName()
|
||||
})
|
||||
allContexts *context.ContextTree
|
||||
}
|
||||
|
||||
// use replaceContext when you don't want to return to the original context upon
|
||||
func NewContextMgr(
|
||||
gui *Gui,
|
||||
allContexts *context.ContextTree,
|
||||
) *ContextMgr {
|
||||
return &ContextMgr{
|
||||
ContextStack: []types.Context{},
|
||||
RWMutex: sync.RWMutex{},
|
||||
gui: gui,
|
||||
allContexts: allContexts,
|
||||
}
|
||||
}
|
||||
|
||||
// use when you don't want to return to the original context upon
|
||||
// hitting escape: you want to go that context's parent instead.
|
||||
func (gui *Gui) replaceContext(c types.Context) error {
|
||||
func (self *ContextMgr) Replace(c types.Context) error {
|
||||
if !c.IsFocusable() {
|
||||
return nil
|
||||
}
|
||||
|
||||
gui.State.ContextManager.Lock()
|
||||
self.Lock()
|
||||
|
||||
if len(gui.State.ContextManager.ContextStack) == 0 {
|
||||
gui.State.ContextManager.ContextStack = []types.Context{c}
|
||||
if len(self.ContextStack) == 0 {
|
||||
self.ContextStack = []types.Context{c}
|
||||
} else {
|
||||
// replace the last item with the given item
|
||||
gui.State.ContextManager.ContextStack = append(gui.State.ContextManager.ContextStack[0:len(gui.State.ContextManager.ContextStack)-1], c)
|
||||
self.ContextStack = append(self.ContextStack[0:len(self.ContextStack)-1], c)
|
||||
}
|
||||
|
||||
defer gui.State.ContextManager.Unlock()
|
||||
defer self.Unlock()
|
||||
|
||||
return gui.activateContext(c, types.OnFocusOpts{})
|
||||
return self.ActivateContext(c, types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (gui *Gui) pushContext(c types.Context, opts types.OnFocusOpts) error {
|
||||
func (self *ContextMgr) Push(c types.Context, opts ...types.OnFocusOpts) error {
|
||||
if len(opts) > 1 {
|
||||
return errors.New("cannot pass multiple opts to Push")
|
||||
}
|
||||
|
||||
singleOpts := types.OnFocusOpts{}
|
||||
if len(opts) > 0 {
|
||||
// using triple dot but you should only ever pass one of these opt structs
|
||||
singleOpts = opts[0]
|
||||
}
|
||||
|
||||
if !c.IsFocusable() {
|
||||
return nil
|
||||
}
|
||||
|
||||
contextsToDeactivate, contextToActivate := gui.pushToContextStack(c)
|
||||
contextsToDeactivate, contextToActivate := self.pushToContextStack(c)
|
||||
|
||||
for _, contextToDeactivate := range contextsToDeactivate {
|
||||
if err := gui.deactivateContext(contextToDeactivate, types.OnFocusLostOpts{NewContextKey: c.GetKey()}); err != nil {
|
||||
if err := self.deactivateContext(contextToDeactivate, types.OnFocusLostOpts{NewContextKey: c.GetKey()}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -65,43 +83,45 @@ func (gui *Gui) pushContext(c types.Context, opts types.OnFocusOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return gui.activateContext(contextToActivate, opts)
|
||||
return self.ActivateContext(contextToActivate, singleOpts)
|
||||
}
|
||||
|
||||
// Adjusts the context stack based on the context that's being pushed and
|
||||
// returns (contexts to deactivate, context to activate)
|
||||
func (gui *Gui) pushToContextStack(c types.Context) ([]types.Context, types.Context) {
|
||||
func (self *ContextMgr) pushToContextStack(c types.Context) ([]types.Context, types.Context) {
|
||||
contextsToDeactivate := []types.Context{}
|
||||
|
||||
gui.State.ContextManager.Lock()
|
||||
defer gui.State.ContextManager.Unlock()
|
||||
self.Lock()
|
||||
defer self.Unlock()
|
||||
|
||||
if len(gui.State.ContextManager.ContextStack) > 0 &&
|
||||
c == gui.State.ContextManager.ContextStack[len(gui.State.ContextManager.ContextStack)-1] {
|
||||
if len(self.ContextStack) > 0 &&
|
||||
c.GetKey() == self.ContextStack[len(self.ContextStack)-1].GetKey() {
|
||||
// Context being pushed is already on top of the stack: nothing to
|
||||
// deactivate or activate
|
||||
return contextsToDeactivate, nil
|
||||
}
|
||||
|
||||
if len(gui.State.ContextManager.ContextStack) == 0 {
|
||||
gui.State.ContextManager.ContextStack = append(gui.State.ContextManager.ContextStack, c)
|
||||
if len(self.ContextStack) == 0 {
|
||||
self.ContextStack = append(self.ContextStack, c)
|
||||
} else if c.GetKind() == types.SIDE_CONTEXT {
|
||||
// if we are switching to a side context, remove all other contexts in the stack
|
||||
contextsToDeactivate = gui.State.ContextManager.ContextStack
|
||||
gui.State.ContextManager.ContextStack = []types.Context{c}
|
||||
contextsToDeactivate = lo.Filter(self.ContextStack, func(context types.Context, _ int) bool {
|
||||
return context.GetKey() != c.GetKey()
|
||||
})
|
||||
self.ContextStack = []types.Context{c}
|
||||
} else if c.GetKind() == types.MAIN_CONTEXT {
|
||||
// if we're switching to a main context, remove all other main contexts in the stack
|
||||
contextsToKeep := []types.Context{}
|
||||
for _, stackContext := range gui.State.ContextManager.ContextStack {
|
||||
for _, stackContext := range self.ContextStack {
|
||||
if stackContext.GetKind() == types.MAIN_CONTEXT {
|
||||
contextsToDeactivate = append(contextsToDeactivate, stackContext)
|
||||
} else {
|
||||
contextsToKeep = append(contextsToKeep, stackContext)
|
||||
}
|
||||
}
|
||||
gui.State.ContextManager.ContextStack = append(contextsToKeep, c)
|
||||
self.ContextStack = append(contextsToKeep, c)
|
||||
} else {
|
||||
topContext := gui.currentContextWithoutLock()
|
||||
topContext := self.currentContextWithoutLock()
|
||||
|
||||
// if we're pushing the same context on, we do nothing.
|
||||
if topContext.GetKey() != c.GetKey() {
|
||||
@@ -114,48 +134,48 @@ func (gui *Gui) pushToContextStack(c types.Context) ([]types.Context, types.Cont
|
||||
(topContext.GetKind() == types.MAIN_CONTEXT && c.GetKind() == types.MAIN_CONTEXT) {
|
||||
|
||||
contextsToDeactivate = append(contextsToDeactivate, topContext)
|
||||
_, gui.State.ContextManager.ContextStack = slices.Pop(gui.State.ContextManager.ContextStack)
|
||||
_, self.ContextStack = slices.Pop(self.ContextStack)
|
||||
}
|
||||
|
||||
gui.State.ContextManager.ContextStack = append(gui.State.ContextManager.ContextStack, c)
|
||||
self.ContextStack = append(self.ContextStack, c)
|
||||
}
|
||||
}
|
||||
|
||||
return contextsToDeactivate, c
|
||||
}
|
||||
|
||||
func (gui *Gui) popContext() error {
|
||||
gui.State.ContextManager.Lock()
|
||||
func (self *ContextMgr) Pop() error {
|
||||
self.Lock()
|
||||
|
||||
if len(gui.State.ContextManager.ContextStack) == 1 {
|
||||
if len(self.ContextStack) == 1 {
|
||||
// cannot escape from bottommost context
|
||||
gui.State.ContextManager.Unlock()
|
||||
self.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
var currentContext types.Context
|
||||
currentContext, gui.State.ContextManager.ContextStack = slices.Pop(gui.State.ContextManager.ContextStack)
|
||||
currentContext, self.ContextStack = slices.Pop(self.ContextStack)
|
||||
|
||||
newContext := gui.State.ContextManager.ContextStack[len(gui.State.ContextManager.ContextStack)-1]
|
||||
newContext := self.ContextStack[len(self.ContextStack)-1]
|
||||
|
||||
gui.State.ContextManager.Unlock()
|
||||
self.Unlock()
|
||||
|
||||
if err := gui.deactivateContext(currentContext, types.OnFocusLostOpts{NewContextKey: newContext.GetKey()}); err != nil {
|
||||
if err := self.deactivateContext(currentContext, types.OnFocusLostOpts{NewContextKey: newContext.GetKey()}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return gui.activateContext(newContext, types.OnFocusOpts{})
|
||||
return self.ActivateContext(newContext, types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (gui *Gui) removeContexts(contextsToRemove []types.Context) error {
|
||||
gui.State.ContextManager.Lock()
|
||||
func (self *ContextMgr) RemoveContexts(contextsToRemove []types.Context) error {
|
||||
self.Lock()
|
||||
|
||||
if len(gui.State.ContextManager.ContextStack) == 1 {
|
||||
gui.State.ContextManager.Unlock()
|
||||
if len(self.ContextStack) == 1 {
|
||||
self.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
rest := lo.Filter(gui.State.ContextManager.ContextStack, func(context types.Context, _ int) bool {
|
||||
rest := lo.Filter(self.ContextStack, func(context types.Context, _ int) bool {
|
||||
for _, contextToRemove := range contextsToRemove {
|
||||
if context.GetKey() == contextToRemove.GetKey() {
|
||||
return false
|
||||
@@ -163,25 +183,25 @@ func (gui *Gui) removeContexts(contextsToRemove []types.Context) error {
|
||||
}
|
||||
return true
|
||||
})
|
||||
gui.State.ContextManager.ContextStack = rest
|
||||
self.ContextStack = rest
|
||||
contextToActivate := rest[len(rest)-1]
|
||||
gui.State.ContextManager.Unlock()
|
||||
self.Unlock()
|
||||
|
||||
for _, context := range contextsToRemove {
|
||||
if err := gui.deactivateContext(context, types.OnFocusLostOpts{NewContextKey: contextToActivate.GetKey()}); err != nil {
|
||||
if err := self.deactivateContext(context, types.OnFocusLostOpts{NewContextKey: contextToActivate.GetKey()}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// activate the item at the top of the stack
|
||||
return gui.activateContext(contextToActivate, types.OnFocusOpts{})
|
||||
return self.ActivateContext(contextToActivate, types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (gui *Gui) deactivateContext(c types.Context, opts types.OnFocusLostOpts) error {
|
||||
view, _ := gui.g.View(c.GetViewName())
|
||||
func (self *ContextMgr) deactivateContext(c types.Context, opts types.OnFocusLostOpts) error {
|
||||
view, _ := self.gui.c.GocuiGui().View(c.GetViewName())
|
||||
|
||||
if view != nil && view.IsSearching() {
|
||||
if err := gui.onSearchEscape(); err != nil {
|
||||
if err := self.gui.onSearchEscape(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -200,34 +220,17 @@ func (gui *Gui) deactivateContext(c types.Context, opts types.OnFocusLostOpts) e
|
||||
return nil
|
||||
}
|
||||
|
||||
// postRefreshUpdate is to be called on a context after the state that it depends on has been refreshed
|
||||
// if the context's view is set to another context we do nothing.
|
||||
// if the context's view is the current view we trigger a focus; re-selecting the current item.
|
||||
func (gui *Gui) postRefreshUpdate(c types.Context) error {
|
||||
if err := c.HandleRender(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if gui.currentViewName() == c.GetViewName() {
|
||||
if err := c.HandleFocus(types.OnFocusOpts{}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) activateContext(c types.Context, opts types.OnFocusOpts) error {
|
||||
func (self *ContextMgr) ActivateContext(c types.Context, opts types.OnFocusOpts) error {
|
||||
viewName := c.GetViewName()
|
||||
v, err := gui.g.View(viewName)
|
||||
v, err := self.gui.c.GocuiGui().View(viewName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gui.setWindowContext(c)
|
||||
self.gui.helpers.Window.SetWindowContext(c)
|
||||
|
||||
gui.moveToTopOfWindow(c)
|
||||
if _, err := gui.g.SetCurrentView(viewName); err != nil {
|
||||
self.gui.helpers.Window.MoveToTopOfWindow(c)
|
||||
if _, err := self.gui.c.GocuiGui().SetCurrentView(viewName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -238,14 +241,9 @@ func (gui *Gui) activateContext(c types.Context, opts types.OnFocusOpts) error {
|
||||
|
||||
v.Visible = true
|
||||
|
||||
gui.g.Cursor = v.Editable
|
||||
self.gui.c.GocuiGui().Cursor = v.Editable
|
||||
|
||||
// render the options available for the current context at the bottom of the screen
|
||||
optionsMap := c.GetOptionsMap()
|
||||
if optionsMap == nil {
|
||||
optionsMap = gui.globalOptionsMap()
|
||||
}
|
||||
gui.renderOptionsMap(optionsMap)
|
||||
self.gui.renderContextOptionsMap(c)
|
||||
|
||||
if err := c.HandleFocus(opts); err != nil {
|
||||
return err
|
||||
@@ -254,63 +252,27 @@ func (gui *Gui) activateContext(c types.Context, opts types.OnFocusOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gui *Gui) optionsMapToString(optionsMap map[string]string) string {
|
||||
options := maps.MapToSlice(optionsMap, func(key string, description string) string {
|
||||
return key + ": " + description
|
||||
})
|
||||
sort.Strings(options)
|
||||
return strings.Join(options, ", ")
|
||||
func (self *ContextMgr) Current() types.Context {
|
||||
self.RLock()
|
||||
defer self.RUnlock()
|
||||
|
||||
return self.currentContextWithoutLock()
|
||||
}
|
||||
|
||||
func (gui *Gui) renderOptionsMap(optionsMap map[string]string) {
|
||||
_ = gui.renderString(gui.Views.Options, gui.optionsMapToString(optionsMap))
|
||||
}
|
||||
|
||||
// // currently unused
|
||||
// func (gui *Gui) renderContextStack() string {
|
||||
// result := ""
|
||||
// for _, context := range gui.State.ContextManager.ContextStack {
|
||||
// result += string(context.GetKey()) + "\n"
|
||||
// }
|
||||
// return result
|
||||
// }
|
||||
|
||||
func (gui *Gui) currentContext() types.Context {
|
||||
gui.State.ContextManager.RLock()
|
||||
defer gui.State.ContextManager.RUnlock()
|
||||
|
||||
return gui.currentContextWithoutLock()
|
||||
}
|
||||
|
||||
func (gui *Gui) currentContextWithoutLock() types.Context {
|
||||
if len(gui.State.ContextManager.ContextStack) == 0 {
|
||||
return gui.defaultSideContext()
|
||||
func (self *ContextMgr) currentContextWithoutLock() types.Context {
|
||||
if len(self.ContextStack) == 0 {
|
||||
return self.gui.defaultSideContext()
|
||||
}
|
||||
|
||||
return gui.State.ContextManager.ContextStack[len(gui.State.ContextManager.ContextStack)-1]
|
||||
return self.ContextStack[len(self.ContextStack)-1]
|
||||
}
|
||||
|
||||
// the status panel is not yet a list context (and may never be), so this method is not
|
||||
// quite the same as currentSideContext()
|
||||
func (gui *Gui) currentSideListContext() types.IListContext {
|
||||
context := gui.currentSideContext()
|
||||
listContext, ok := context.(types.IListContext)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return listContext
|
||||
}
|
||||
// Note that this could return the 'status' context which is not itself a list context.
|
||||
func (self *ContextMgr) CurrentSide() types.Context {
|
||||
self.RLock()
|
||||
defer self.RUnlock()
|
||||
|
||||
func (gui *Gui) currentSideContext() types.Context {
|
||||
gui.State.ContextManager.RLock()
|
||||
defer gui.State.ContextManager.RUnlock()
|
||||
|
||||
stack := gui.State.ContextManager.ContextStack
|
||||
|
||||
// on startup the stack can be empty so we'll return an empty string in that case
|
||||
if len(stack) == 0 {
|
||||
return gui.defaultSideContext()
|
||||
}
|
||||
stack := self.ContextStack
|
||||
|
||||
// find the first context in the stack with the type of types.SIDE_CONTEXT
|
||||
for i := range stack {
|
||||
@@ -321,22 +283,22 @@ func (gui *Gui) currentSideContext() types.Context {
|
||||
}
|
||||
}
|
||||
|
||||
return gui.defaultSideContext()
|
||||
return self.gui.defaultSideContext()
|
||||
}
|
||||
|
||||
// static as opposed to popup
|
||||
func (gui *Gui) currentStaticContext() types.Context {
|
||||
gui.State.ContextManager.RLock()
|
||||
defer gui.State.ContextManager.RUnlock()
|
||||
func (self *ContextMgr) CurrentStatic() types.Context {
|
||||
self.RLock()
|
||||
defer self.RUnlock()
|
||||
|
||||
return gui.currentStaticContextWithoutLock()
|
||||
return self.currentStaticContextWithoutLock()
|
||||
}
|
||||
|
||||
func (gui *Gui) currentStaticContextWithoutLock() types.Context {
|
||||
stack := gui.State.ContextManager.ContextStack
|
||||
func (self *ContextMgr) currentStaticContextWithoutLock() types.Context {
|
||||
stack := self.ContextStack
|
||||
|
||||
if len(stack) == 0 {
|
||||
return gui.defaultSideContext()
|
||||
return self.gui.defaultSideContext()
|
||||
}
|
||||
|
||||
// find the first context in the stack without a popup type
|
||||
@@ -348,88 +310,43 @@ func (gui *Gui) currentStaticContextWithoutLock() types.Context {
|
||||
}
|
||||
}
|
||||
|
||||
return gui.defaultSideContext()
|
||||
return self.gui.defaultSideContext()
|
||||
}
|
||||
|
||||
func (gui *Gui) defaultSideContext() types.Context {
|
||||
if gui.State.Modes.Filtering.Active() {
|
||||
return gui.State.Contexts.LocalCommits
|
||||
} else {
|
||||
return gui.State.Contexts.Files
|
||||
func (self *ContextMgr) ForEach(f func(types.Context)) {
|
||||
self.RLock()
|
||||
defer self.RUnlock()
|
||||
|
||||
for _, context := range self.gui.State.ContextMgr.ContextStack {
|
||||
f(context)
|
||||
}
|
||||
}
|
||||
|
||||
// getFocusLayout returns a manager function for when view gain and lose focus
|
||||
func (gui *Gui) getFocusLayout() func(g *gocui.Gui) error {
|
||||
var previousView *gocui.View
|
||||
return func(g *gocui.Gui) error {
|
||||
newView := gui.g.CurrentView()
|
||||
// for now we don't consider losing focus to a popup panel as actually losing focus
|
||||
if newView != previousView && !gui.isPopupPanel(newView.Name()) {
|
||||
if err := gui.onViewFocusLost(previousView); err != nil {
|
||||
return err
|
||||
}
|
||||
func (self *ContextMgr) IsCurrent(c types.Context) bool {
|
||||
return self.Current().GetKey() == c.GetKey()
|
||||
}
|
||||
|
||||
previousView = newView
|
||||
// all list contexts
|
||||
func (self *ContextMgr) AllList() []types.IListContext {
|
||||
var listContexts []types.IListContext
|
||||
|
||||
for _, context := range self.allContexts.Flatten() {
|
||||
if listContext, ok := context.(types.IListContext); ok {
|
||||
listContexts = append(listContexts, listContext)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) onViewFocusLost(oldView *gocui.View) error {
|
||||
if oldView == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
oldView.Highlight = false
|
||||
|
||||
_ = oldView.SetOriginX(0)
|
||||
|
||||
return nil
|
||||
return listContexts
|
||||
}
|
||||
|
||||
func (gui *Gui) TransientContexts() []types.Context {
|
||||
return slices.Filter(gui.State.Contexts.Flatten(), func(context types.Context) bool {
|
||||
return context.IsTransient()
|
||||
})
|
||||
}
|
||||
func (self *ContextMgr) AllPatchExplorer() []types.IPatchExplorerContext {
|
||||
var listContexts []types.IPatchExplorerContext
|
||||
|
||||
func (gui *Gui) rerenderView(view *gocui.View) error {
|
||||
context, ok := gui.contextForView(view.Name())
|
||||
if !ok {
|
||||
gui.Log.Errorf("no context found for view %s", view.Name())
|
||||
return nil
|
||||
for _, context := range self.allContexts.Flatten() {
|
||||
if listContext, ok := context.(types.IPatchExplorerContext); ok {
|
||||
listContexts = append(listContexts, listContext)
|
||||
}
|
||||
}
|
||||
|
||||
return context.HandleRender()
|
||||
return listContexts
|
||||
}
|
||||
|
||||
func (gui *Gui) getSideContextSelectedItemId() string {
|
||||
currentSideContext := gui.currentSideListContext()
|
||||
if currentSideContext == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return currentSideContext.GetSelectedItemId()
|
||||
}
|
||||
|
||||
// currently unused
|
||||
// func (gui *Gui) getCurrentSideView() *gocui.View {
|
||||
// currentSideContext := gui.currentSideContext()
|
||||
// if currentSideContext == nil {
|
||||
// return nil
|
||||
// }
|
||||
|
||||
// view, _ := gui.g.View(currentSideContext.GetViewName())
|
||||
|
||||
// return view
|
||||
// }
|
||||
|
||||
// currently unused
|
||||
// func (gui *Gui) renderContextStack() string {
|
||||
// result := ""
|
||||
// for _, context := range gui.State.ContextManager.ContextStack {
|
||||
// result += context.GetViewName() + "\n"
|
||||
// }
|
||||
// return result
|
||||
// }
|
||||
|
||||
@@ -16,6 +16,9 @@ type BaseContext struct {
|
||||
keybindingsFns []types.KeybindingsFn
|
||||
mouseKeybindingsFns []types.MouseKeybindingsFn
|
||||
onClickFn func() error
|
||||
onRenderToMainFn func() error
|
||||
onFocusFn onFocusFn
|
||||
onFocusLostFn onFocusLostFn
|
||||
|
||||
focusable bool
|
||||
transient bool
|
||||
@@ -25,6 +28,11 @@ type BaseContext struct {
|
||||
*ParentContextMgr
|
||||
}
|
||||
|
||||
type (
|
||||
onFocusFn = func(types.OnFocusOpts) error
|
||||
onFocusLostFn = func(types.OnFocusLostOpts) error
|
||||
)
|
||||
|
||||
var _ types.IBaseContext = &BaseContext{}
|
||||
|
||||
type NewBaseContextOpts struct {
|
||||
@@ -129,6 +137,36 @@ func (self *BaseContext) GetOnClick() func() error {
|
||||
return self.onClickFn
|
||||
}
|
||||
|
||||
func (self *BaseContext) AddOnRenderToMainFn(fn func() error) {
|
||||
if fn != nil {
|
||||
self.onRenderToMainFn = fn
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BaseContext) GetOnRenderToMain() func() error {
|
||||
return self.onRenderToMainFn
|
||||
}
|
||||
|
||||
func (self *BaseContext) AddOnFocusFn(fn onFocusFn) {
|
||||
if fn != nil {
|
||||
self.onFocusFn = fn
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BaseContext) GetOnFocus() onFocusFn {
|
||||
return self.onFocusFn
|
||||
}
|
||||
|
||||
func (self *BaseContext) AddOnFocusLostFn(fn onFocusLostFn) {
|
||||
if fn != nil {
|
||||
self.onFocusLostFn = fn
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BaseContext) GetOnFocusLost() onFocusLostFn {
|
||||
return self.onFocusLostFn
|
||||
}
|
||||
|
||||
func (self *BaseContext) GetMouseKeybindings(opts types.KeybindingsOpts) []*gocui.ViewMouseBinding {
|
||||
bindings := []*gocui.ViewMouseBinding{}
|
||||
for i := range self.mouseKeybindingsFns {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@@ -11,40 +11,40 @@ type BranchesContext struct {
|
||||
*ListContextTrait
|
||||
}
|
||||
|
||||
var _ types.IListContext = (*BranchesContext)(nil)
|
||||
var (
|
||||
_ types.IListContext = (*BranchesContext)(nil)
|
||||
_ types.DiffableContext = (*BranchesContext)(nil)
|
||||
)
|
||||
|
||||
func NewBranchesContext(
|
||||
getModel func() []*models.Branch,
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
func NewBranchesContext(c *ContextCommon) *BranchesContext {
|
||||
viewModel := NewBasicViewModel(func() []*models.Branch { return c.Model().Branches })
|
||||
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
return presentation.GetBranchListDisplayStrings(
|
||||
c.Model().Branches,
|
||||
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||
c.Modes().Diffing.Ref,
|
||||
c.Tr,
|
||||
)
|
||||
}
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *BranchesContext {
|
||||
viewModel := NewBasicViewModel(getModel)
|
||||
|
||||
return &BranchesContext{
|
||||
self := &BranchesContext{
|
||||
BasicViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: view,
|
||||
View: c.Views().Branches,
|
||||
WindowName: "branches",
|
||||
Key: LOCAL_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
OnFocusLost: onFocusLost,
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
})),
|
||||
list: viewModel,
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
},
|
||||
}
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *BranchesContext) GetSelectedItemId() string {
|
||||
@@ -63,3 +63,16 @@ func (self *BranchesContext) GetSelectedRef() types.Ref {
|
||||
}
|
||||
return branch
|
||||
}
|
||||
|
||||
func (self *BranchesContext) GetDiffTerminals() []string {
|
||||
// for our local branches we want to include both the branch and its upstream
|
||||
branch := self.GetSelected()
|
||||
if branch != nil {
|
||||
names := []string{branch.ID()}
|
||||
if branch.IsTrackingRemote() {
|
||||
names = append(names, branch.ID()+"@{u}")
|
||||
}
|
||||
return names
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@@ -13,20 +15,28 @@ type CommitFilesContext struct {
|
||||
*DynamicTitleBuilder
|
||||
}
|
||||
|
||||
var _ types.IListContext = (*CommitFilesContext)(nil)
|
||||
var (
|
||||
_ types.IListContext = (*CommitFilesContext)(nil)
|
||||
_ types.DiffableContext = (*CommitFilesContext)(nil)
|
||||
)
|
||||
|
||||
func NewCommitFilesContext(
|
||||
getModel func() []*models.CommitFile,
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
func NewCommitFilesContext(c *ContextCommon) *CommitFilesContext {
|
||||
viewModel := filetree.NewCommitFileTreeViewModel(
|
||||
func() []*models.CommitFile { return c.Model().CommitFiles },
|
||||
c.Log,
|
||||
c.UserConfig.Gui.ShowFileTree,
|
||||
)
|
||||
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
if viewModel.Len() == 0 {
|
||||
return [][]string{{style.FgRed.Sprint("(none)")}}
|
||||
}
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *CommitFilesContext {
|
||||
viewModel := filetree.NewCommitFileTreeViewModel(getModel, c.Log, c.UserConfig.Gui.ShowFileTree)
|
||||
lines := presentation.RenderCommitFileTree(viewModel, c.Modes().Diffing.Ref, c.Git().Patch.PatchBuilder)
|
||||
return slices.Map(lines, func(line string) []string {
|
||||
return []string{line}
|
||||
})
|
||||
}
|
||||
|
||||
return &CommitFilesContext{
|
||||
CommitFileTreeViewModel: viewModel,
|
||||
@@ -34,18 +44,14 @@ func NewCommitFilesContext(
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
View: view,
|
||||
View: c.Views().CommitFiles,
|
||||
WindowName: "commits",
|
||||
Key: COMMIT_FILES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
Transient: true,
|
||||
}),
|
||||
ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
OnFocusLost: onFocusLost,
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
),
|
||||
list: viewModel,
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
@@ -61,3 +67,7 @@ func (self *CommitFilesContext) GetSelectedItemId() string {
|
||||
|
||||
return item.ID()
|
||||
}
|
||||
|
||||
func (self *CommitFilesContext) GetDiffTerminals() []string {
|
||||
return []string{self.GetRef().RefName()}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type CommitMessageContext struct {
|
||||
c *ContextCommon
|
||||
types.Context
|
||||
viewModel *CommitMessageViewModel
|
||||
}
|
||||
|
||||
var _ types.Context = (*CommitMessageContext)(nil)
|
||||
|
||||
// when selectedIndex (see below) is set to this value, it means that we're not
|
||||
// currently viewing a commit message of an existing commit: instead we're making our own
|
||||
// new commit message
|
||||
@@ -35,22 +41,21 @@ type CommitMessageViewModel struct {
|
||||
}
|
||||
|
||||
func NewCommitMessageContext(
|
||||
view *gocui.View,
|
||||
opts ContextCallbackOpts,
|
||||
c *ContextCommon,
|
||||
) *CommitMessageContext {
|
||||
viewModel := &CommitMessageViewModel{}
|
||||
return &CommitMessageContext{
|
||||
c: c,
|
||||
viewModel: viewModel,
|
||||
Context: NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
Kind: types.PERSISTENT_POPUP,
|
||||
View: view,
|
||||
View: c.Views().CommitMessage,
|
||||
WindowName: "commitMessage",
|
||||
Key: COMMIT_MESSAGE_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
HasUncontrolledBounds: true,
|
||||
}),
|
||||
opts,
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -93,3 +98,15 @@ func (self *CommitMessageContext) SetPanelState(index int, title string, preserv
|
||||
self.viewModel.onConfirm = onConfirm
|
||||
self.GetView().Title = title
|
||||
}
|
||||
|
||||
func (self *CommitMessageContext) RenderCommitLength() {
|
||||
if !self.c.UserConfig.Gui.CommitLength.Show {
|
||||
return
|
||||
}
|
||||
|
||||
self.c.Views().CommitMessage.Subtitle = getBufferLength(self.c.Views().CommitMessage)
|
||||
}
|
||||
|
||||
func getBufferLength(view *gocui.View) string {
|
||||
return " " + strconv.Itoa(strings.Count(view.TextArea.GetContent(), "")-1) + " "
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type ConfirmationContext struct {
|
||||
*SimpleContext
|
||||
c *ContextCommon
|
||||
|
||||
State ConfirmationContextState
|
||||
}
|
||||
|
||||
type ConfirmationContextState struct {
|
||||
OnConfirm func() error
|
||||
OnClose func() error
|
||||
}
|
||||
|
||||
var _ types.Context = (*ConfirmationContext)(nil)
|
||||
|
||||
func NewConfirmationContext(
|
||||
c *ContextCommon,
|
||||
) *ConfirmationContext {
|
||||
return &ConfirmationContext{
|
||||
c: c,
|
||||
SimpleContext: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: c.Views().Confirmation,
|
||||
WindowName: "confirmation",
|
||||
Key: CONFIRMATION_CONTEXT_KEY,
|
||||
Kind: types.TEMPORARY_POPUP,
|
||||
Focusable: true,
|
||||
HasUncontrolledBounds: true,
|
||||
})),
|
||||
}
|
||||
}
|
||||
@@ -97,7 +97,7 @@ type ContextTree struct {
|
||||
CustomPatchBuilder *PatchExplorerContext
|
||||
CustomPatchBuilderSecondary types.Context
|
||||
MergeConflicts *MergeConflictsContext
|
||||
Confirmation types.Context
|
||||
Confirmation *ConfirmationContext
|
||||
CommitMessage *CommitMessageContext
|
||||
CommitDescription types.Context
|
||||
CommandLog types.Context
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/common"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type ContextCommon struct {
|
||||
*common.Common
|
||||
types.IGuiCommon
|
||||
}
|
||||
@@ -10,11 +10,15 @@ import (
|
||||
type ListContextTrait struct {
|
||||
types.Context
|
||||
|
||||
c *types.HelperCommon
|
||||
c *ContextCommon
|
||||
list types.IList
|
||||
getDisplayStrings func(startIdx int, length int) [][]string
|
||||
// alignment for each column. If nil, the default is left alignment
|
||||
columnAlignments []utils.Alignment
|
||||
}
|
||||
|
||||
func (self *ListContextTrait) IsListContext() {}
|
||||
|
||||
func (self *ListContextTrait) GetList() types.IList {
|
||||
return self.list
|
||||
}
|
||||
@@ -50,7 +54,10 @@ func (self *ListContextTrait) HandleFocusLost(opts types.OnFocusLostOpts) error
|
||||
// OnFocus assumes that the content of the context has already been rendered to the view. OnRender is the function which actually renders the content to the view
|
||||
func (self *ListContextTrait) HandleRender() error {
|
||||
self.list.RefreshSelectedIdx()
|
||||
content := utils.RenderDisplayStrings(self.getDisplayStrings(0, self.list.Len()))
|
||||
content := utils.RenderDisplayStrings(
|
||||
self.getDisplayStrings(0, self.list.Len()),
|
||||
self.columnAlignments,
|
||||
)
|
||||
self.GetViewTrait().SetContent(content)
|
||||
self.c.Render()
|
||||
self.setFooter()
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"log"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@@ -11,36 +15,51 @@ type LocalCommitsContext struct {
|
||||
*ViewportListContextTrait
|
||||
}
|
||||
|
||||
var _ types.IListContext = (*LocalCommitsContext)(nil)
|
||||
var (
|
||||
_ types.IListContext = (*LocalCommitsContext)(nil)
|
||||
_ types.DiffableContext = (*LocalCommitsContext)(nil)
|
||||
)
|
||||
|
||||
func NewLocalCommitsContext(
|
||||
getModel func() []*models.Commit,
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
|
||||
viewModel := NewLocalCommitsViewModel(
|
||||
func() []*models.Commit { return c.Model().Commits },
|
||||
c,
|
||||
)
|
||||
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
selectedCommitSha := ""
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *LocalCommitsContext {
|
||||
viewModel := NewLocalCommitsViewModel(getModel, c)
|
||||
if c.CurrentContext().GetKey() == LOCAL_COMMITS_CONTEXT_KEY {
|
||||
selectedCommit := viewModel.GetSelected()
|
||||
if selectedCommit != nil {
|
||||
selectedCommitSha = selectedCommit.Sha
|
||||
}
|
||||
}
|
||||
|
||||
showYouAreHereLabel := c.Model().WorkingTreeStateAtLastCommitRefresh == enums.REBASE_MODE_REBASING
|
||||
|
||||
return getCommitsDisplayStrings(
|
||||
c,
|
||||
c.Model().Commits,
|
||||
startIdx,
|
||||
length,
|
||||
selectedCommitSha,
|
||||
showYouAreHereLabel,
|
||||
c.Model().BisectInfo,
|
||||
)
|
||||
}
|
||||
|
||||
return &LocalCommitsContext{
|
||||
LocalCommitsViewModel: viewModel,
|
||||
ViewportListContextTrait: &ViewportListContextTrait{
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: view,
|
||||
View: c.Views().Commits,
|
||||
WindowName: "commits",
|
||||
Key: LOCAL_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
OnFocusLost: onFocusLost,
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
})),
|
||||
list: viewModel,
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
@@ -69,7 +88,7 @@ type LocalCommitsViewModel struct {
|
||||
showWholeGitGraph bool
|
||||
}
|
||||
|
||||
func NewLocalCommitsViewModel(getModel func() []*models.Commit, c *types.HelperCommon) *LocalCommitsViewModel {
|
||||
func NewLocalCommitsViewModel(getModel func() []*models.Commit, c *ContextCommon) *LocalCommitsViewModel {
|
||||
self := &LocalCommitsViewModel{
|
||||
BasicViewModel: NewBasicViewModel(getModel),
|
||||
limitCommits: true,
|
||||
@@ -91,6 +110,12 @@ func (self *LocalCommitsContext) GetSelectedRef() types.Ref {
|
||||
return commit
|
||||
}
|
||||
|
||||
func (self *LocalCommitsContext) GetDiffTerminals() []string {
|
||||
itemId := self.GetSelectedItemId()
|
||||
|
||||
return []string{itemId}
|
||||
}
|
||||
|
||||
func (self *LocalCommitsViewModel) SetLimitCommits(value bool) {
|
||||
self.limitCommits = value
|
||||
}
|
||||
@@ -110,3 +135,50 @@ func (self *LocalCommitsViewModel) GetShowWholeGitGraph() bool {
|
||||
func (self *LocalCommitsViewModel) GetCommits() []*models.Commit {
|
||||
return self.getModel()
|
||||
}
|
||||
|
||||
func shouldShowGraph(c *ContextCommon) bool {
|
||||
if c.Modes().Filtering.Active() {
|
||||
return false
|
||||
}
|
||||
|
||||
value := c.UserConfig.Git.Log.ShowGraph
|
||||
switch value {
|
||||
case "always":
|
||||
return true
|
||||
case "never":
|
||||
return false
|
||||
case "when-maximised":
|
||||
return c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL
|
||||
}
|
||||
|
||||
log.Fatalf("Unknown value for git.log.showGraph: %s. Expected one of: 'always', 'never', 'when-maximised'", value)
|
||||
return false
|
||||
}
|
||||
|
||||
func getCommitsDisplayStrings(
|
||||
c *ContextCommon,
|
||||
commits []*models.Commit,
|
||||
startIdx int,
|
||||
length int,
|
||||
selectedCommitSha string,
|
||||
showYouAreHereLabel bool,
|
||||
bisectInfo *git_commands.BisectInfo,
|
||||
) [][]string {
|
||||
return presentation.GetCommitListDisplayStrings(
|
||||
c.Common,
|
||||
commits,
|
||||
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||
c.Modes().CherryPicking.SelectedShaSet(),
|
||||
c.Modes().Diffing.Ref,
|
||||
c.UserConfig.Gui.TimeFormat,
|
||||
c.UserConfig.Git.ParseEmoji,
|
||||
selectedCommitSha,
|
||||
startIdx,
|
||||
length,
|
||||
shouldShowGraph(c),
|
||||
bisectInfo,
|
||||
showYouAreHereLabel,
|
||||
c.State().GetRepoState().GetRecentCommitsWhichChangedFile(),
|
||||
c.UserConfig.Gui.ExperimentalMarkCommitsWhichChangedFile,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,13 +2,16 @@ package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/keybindings"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type MenuContext struct {
|
||||
c *ContextCommon
|
||||
|
||||
*MenuViewModel
|
||||
*ListContextTrait
|
||||
}
|
||||
@@ -16,37 +19,26 @@ type MenuContext struct {
|
||||
var _ types.IListContext = (*MenuContext)(nil)
|
||||
|
||||
func NewMenuContext(
|
||||
view *gocui.View,
|
||||
|
||||
c *types.HelperCommon,
|
||||
getOptionsMap func() map[string]string,
|
||||
renderToDescriptionView func(string),
|
||||
c *ContextCommon,
|
||||
) *MenuContext {
|
||||
viewModel := NewMenuViewModel()
|
||||
|
||||
onFocus := func(types.OnFocusOpts) error {
|
||||
selectedMenuItem := viewModel.GetSelected()
|
||||
renderToDescriptionView(selectedMenuItem.Tooltip)
|
||||
return nil
|
||||
}
|
||||
viewModel := NewMenuViewModel(c)
|
||||
|
||||
return &MenuContext{
|
||||
c: c,
|
||||
MenuViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: view,
|
||||
View: c.Views().Menu,
|
||||
WindowName: "menu",
|
||||
Key: "menu",
|
||||
Kind: types.TEMPORARY_POPUP,
|
||||
OnGetOptionsMap: getOptionsMap,
|
||||
Focusable: true,
|
||||
HasUncontrolledBounds: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
}),
|
||||
})),
|
||||
getDisplayStrings: viewModel.GetDisplayStrings,
|
||||
list: viewModel,
|
||||
c: c,
|
||||
columnAlignments: []utils.Alignment{utils.AlignRight, utils.AlignLeft},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -62,13 +54,15 @@ func (self *MenuContext) GetSelectedItemId() string {
|
||||
}
|
||||
|
||||
type MenuViewModel struct {
|
||||
c *ContextCommon
|
||||
menuItems []*types.MenuItem
|
||||
*BasicViewModel[*types.MenuItem]
|
||||
}
|
||||
|
||||
func NewMenuViewModel() *MenuViewModel {
|
||||
func NewMenuViewModel(c *ContextCommon) *MenuViewModel {
|
||||
self := &MenuViewModel{
|
||||
menuItems: nil,
|
||||
c: c,
|
||||
}
|
||||
|
||||
self.BasicViewModel = NewBasicViewModel(func() []*types.MenuItem { return self.menuItems })
|
||||
@@ -88,9 +82,25 @@ func (self *MenuViewModel) GetDisplayStrings(_startIdx int, _length int) [][]str
|
||||
|
||||
return slices.Map(self.menuItems, func(item *types.MenuItem) []string {
|
||||
displayStrings := item.LabelColumns
|
||||
if showKeys {
|
||||
displayStrings = slices.Prepend(displayStrings, style.FgCyan.Sprint(keybindings.LabelFromKey(item.Key)))
|
||||
|
||||
if !showKeys {
|
||||
return displayStrings
|
||||
}
|
||||
|
||||
// These keys are used for general navigation so we'll strike them out to
|
||||
// avoid confusion
|
||||
reservedKeys := []string{
|
||||
self.c.UserConfig.Keybinding.Universal.Confirm,
|
||||
self.c.UserConfig.Keybinding.Universal.Select,
|
||||
self.c.UserConfig.Keybinding.Universal.Return,
|
||||
}
|
||||
keyLabel := keybindings.LabelFromKey(item.Key)
|
||||
keyStyle := style.FgCyan
|
||||
if lo.Contains(reservedKeys, keyLabel) {
|
||||
keyStyle = style.FgDefault.SetStrikethrough()
|
||||
}
|
||||
|
||||
displayStrings = slices.Prepend(displayStrings, keyStyle.Sprint(keyLabel))
|
||||
return displayStrings
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package context
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/sasha-s/go-deadlock"
|
||||
@@ -12,7 +11,7 @@ import (
|
||||
type MergeConflictsContext struct {
|
||||
types.Context
|
||||
viewModel *ConflictsViewModel
|
||||
c *types.HelperCommon
|
||||
c *ContextCommon
|
||||
mutex *deadlock.Mutex
|
||||
}
|
||||
|
||||
@@ -25,12 +24,7 @@ type ConflictsViewModel struct {
|
||||
}
|
||||
|
||||
func NewMergeConflictsContext(
|
||||
view *gocui.View,
|
||||
|
||||
opts ContextCallbackOpts,
|
||||
|
||||
c *types.HelperCommon,
|
||||
getOptionsMap func() map[string]string,
|
||||
c *ContextCommon,
|
||||
) *MergeConflictsContext {
|
||||
viewModel := &ConflictsViewModel{
|
||||
state: mergeconflicts.NewState(),
|
||||
@@ -43,14 +37,12 @@ func NewMergeConflictsContext(
|
||||
Context: NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
Kind: types.MAIN_CONTEXT,
|
||||
View: view,
|
||||
View: c.Views().MergeConflicts,
|
||||
WindowName: "main",
|
||||
Key: MERGE_CONFLICTS_CONTEXT_KEY,
|
||||
OnGetOptionsMap: getOptionsMap,
|
||||
Focusable: true,
|
||||
HighlightOnFocus: true,
|
||||
}),
|
||||
opts,
|
||||
),
|
||||
c: c,
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ type PatchExplorerContext struct {
|
||||
state *patch_exploring.State
|
||||
viewTrait *ViewTrait
|
||||
getIncludedLineIndices func() []int
|
||||
c *types.HelperCommon
|
||||
c *ContextCommon
|
||||
mutex *deadlock.Mutex
|
||||
}
|
||||
|
||||
@@ -24,11 +24,9 @@ func NewPatchExplorerContext(
|
||||
windowName string,
|
||||
key types.ContextKey,
|
||||
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
getIncludedLineIndices func() []int,
|
||||
|
||||
c *types.HelperCommon,
|
||||
c *ContextCommon,
|
||||
) *PatchExplorerContext {
|
||||
return &PatchExplorerContext{
|
||||
state: nil,
|
||||
@@ -43,13 +41,12 @@ func NewPatchExplorerContext(
|
||||
Kind: types.MAIN_CONTEXT,
|
||||
Focusable: true,
|
||||
HighlightOnFocus: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
OnFocusLost: onFocusLost,
|
||||
}),
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *PatchExplorerContext) IsPatchExplorerContext() {}
|
||||
|
||||
func (self *PatchExplorerContext) GetState() *patch_exploring.State {
|
||||
return self.state
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@@ -11,35 +11,35 @@ type ReflogCommitsContext struct {
|
||||
*ListContextTrait
|
||||
}
|
||||
|
||||
var _ types.IListContext = (*ReflogCommitsContext)(nil)
|
||||
var (
|
||||
_ types.IListContext = (*ReflogCommitsContext)(nil)
|
||||
_ types.DiffableContext = (*ReflogCommitsContext)(nil)
|
||||
)
|
||||
|
||||
func NewReflogCommitsContext(
|
||||
getModel func() []*models.Commit,
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
|
||||
viewModel := NewBasicViewModel(func() []*models.Commit { return c.Model().FilteredReflogCommits })
|
||||
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *ReflogCommitsContext {
|
||||
viewModel := NewBasicViewModel(getModel)
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
return presentation.GetReflogCommitListDisplayStrings(
|
||||
c.Model().FilteredReflogCommits,
|
||||
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
|
||||
c.Modes().CherryPicking.SelectedShaSet(),
|
||||
c.Modes().Diffing.Ref,
|
||||
c.UserConfig.Gui.TimeFormat,
|
||||
c.UserConfig.Git.ParseEmoji,
|
||||
)
|
||||
}
|
||||
|
||||
return &ReflogCommitsContext{
|
||||
BasicViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: view,
|
||||
View: c.Views().ReflogCommits,
|
||||
WindowName: "commits",
|
||||
Key: REFLOG_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
OnFocusLost: onFocusLost,
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
})),
|
||||
list: viewModel,
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
@@ -71,3 +71,9 @@ func (self *ReflogCommitsContext) GetSelectedRef() types.Ref {
|
||||
func (self *ReflogCommitsContext) GetCommits() []*models.Commit {
|
||||
return self.getModel()
|
||||
}
|
||||
|
||||
func (self *ReflogCommitsContext) GetDiffTerminals() []string {
|
||||
itemId := self.GetSelectedItemId()
|
||||
|
||||
return []string{itemId}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@@ -12,37 +12,32 @@ type RemoteBranchesContext struct {
|
||||
*DynamicTitleBuilder
|
||||
}
|
||||
|
||||
var _ types.IListContext = (*RemoteBranchesContext)(nil)
|
||||
var (
|
||||
_ types.IListContext = (*RemoteBranchesContext)(nil)
|
||||
_ types.DiffableContext = (*RemoteBranchesContext)(nil)
|
||||
)
|
||||
|
||||
func NewRemoteBranchesContext(
|
||||
getModel func() []*models.RemoteBranch,
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
c *ContextCommon,
|
||||
) *RemoteBranchesContext {
|
||||
viewModel := NewBasicViewModel(getModel)
|
||||
viewModel := NewBasicViewModel(func() []*models.RemoteBranch { return c.Model().RemoteBranches })
|
||||
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
return presentation.GetRemoteBranchListDisplayStrings(c.Model().RemoteBranches, c.Modes().Diffing.Ref)
|
||||
}
|
||||
|
||||
return &RemoteBranchesContext{
|
||||
BasicViewModel: viewModel,
|
||||
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.RemoteBranchesDynamicTitle),
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: view,
|
||||
View: c.Views().RemoteBranches,
|
||||
WindowName: "branches",
|
||||
Key: REMOTE_BRANCHES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
Transient: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
OnFocusLost: onFocusLost,
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
})),
|
||||
list: viewModel,
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
@@ -66,3 +61,9 @@ func (self *RemoteBranchesContext) GetSelectedRef() types.Ref {
|
||||
}
|
||||
return remoteBranch
|
||||
}
|
||||
|
||||
func (self *RemoteBranchesContext) GetDiffTerminals() []string {
|
||||
itemId := self.GetSelectedItemId()
|
||||
|
||||
return []string{itemId}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@@ -11,35 +11,28 @@ type RemotesContext struct {
|
||||
*ListContextTrait
|
||||
}
|
||||
|
||||
var _ types.IListContext = (*RemotesContext)(nil)
|
||||
var (
|
||||
_ types.IListContext = (*RemotesContext)(nil)
|
||||
_ types.DiffableContext = (*RemotesContext)(nil)
|
||||
)
|
||||
|
||||
func NewRemotesContext(
|
||||
getModel func() []*models.Remote,
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
func NewRemotesContext(c *ContextCommon) *RemotesContext {
|
||||
viewModel := NewBasicViewModel(func() []*models.Remote { return c.Model().Remotes })
|
||||
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *RemotesContext {
|
||||
viewModel := NewBasicViewModel(getModel)
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
return presentation.GetRemoteListDisplayStrings(c.Model().Remotes, c.Modes().Diffing.Ref)
|
||||
}
|
||||
|
||||
return &RemotesContext{
|
||||
BasicViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: view,
|
||||
View: c.Views().Remotes,
|
||||
WindowName: "branches",
|
||||
Key: REMOTES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
OnFocusLost: onFocusLost,
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
})),
|
||||
list: viewModel,
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
@@ -55,3 +48,9 @@ func (self *RemotesContext) GetSelectedItemId() string {
|
||||
|
||||
return item.ID()
|
||||
}
|
||||
|
||||
func (self *RemotesContext) GetDiffTerminals() []string {
|
||||
itemId := self.GetSelectedItemId()
|
||||
|
||||
return []string{itemId}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
package context
|
||||
|
||||
import "github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
|
||||
func NewContextTree(c *ContextCommon) *ContextTree {
|
||||
commitFilesContext := NewCommitFilesContext(c)
|
||||
|
||||
return &ContextTree{
|
||||
Global: NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
Kind: types.GLOBAL_CONTEXT,
|
||||
View: nil, // TODO: see if this breaks anything
|
||||
WindowName: "",
|
||||
Key: GLOBAL_CONTEXT_KEY,
|
||||
Focusable: false,
|
||||
HasUncontrolledBounds: true, // setting to true because the global context doesn't even have a view
|
||||
}),
|
||||
),
|
||||
Status: NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
View: c.Views().Status,
|
||||
WindowName: "status",
|
||||
Key: STATUS_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
}),
|
||||
),
|
||||
Files: NewWorkingTreeContext(c),
|
||||
Submodules: NewSubmodulesContext(c),
|
||||
Menu: NewMenuContext(c),
|
||||
Remotes: NewRemotesContext(c),
|
||||
RemoteBranches: NewRemoteBranchesContext(c),
|
||||
LocalCommits: NewLocalCommitsContext(c),
|
||||
CommitFiles: commitFilesContext,
|
||||
ReflogCommits: NewReflogCommitsContext(c),
|
||||
SubCommits: NewSubCommitsContext(c),
|
||||
Branches: NewBranchesContext(c),
|
||||
Tags: NewTagsContext(c),
|
||||
Stash: NewStashContext(c),
|
||||
Suggestions: NewSuggestionsContext(c),
|
||||
Normal: NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
Kind: types.MAIN_CONTEXT,
|
||||
View: c.Views().Main,
|
||||
WindowName: "main",
|
||||
Key: NORMAL_MAIN_CONTEXT_KEY,
|
||||
Focusable: false,
|
||||
}),
|
||||
),
|
||||
NormalSecondary: NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
Kind: types.MAIN_CONTEXT,
|
||||
View: c.Views().Secondary,
|
||||
WindowName: "secondary",
|
||||
Key: NORMAL_SECONDARY_CONTEXT_KEY,
|
||||
Focusable: false,
|
||||
}),
|
||||
),
|
||||
Staging: NewPatchExplorerContext(
|
||||
c.Views().Staging,
|
||||
"main",
|
||||
STAGING_MAIN_CONTEXT_KEY,
|
||||
func() []int { return nil },
|
||||
c,
|
||||
),
|
||||
StagingSecondary: NewPatchExplorerContext(
|
||||
c.Views().StagingSecondary,
|
||||
"secondary",
|
||||
STAGING_SECONDARY_CONTEXT_KEY,
|
||||
func() []int { return nil },
|
||||
c,
|
||||
),
|
||||
CustomPatchBuilder: NewPatchExplorerContext(
|
||||
c.Views().PatchBuilding,
|
||||
"main",
|
||||
PATCH_BUILDING_MAIN_CONTEXT_KEY,
|
||||
func() []int {
|
||||
filename := commitFilesContext.GetSelectedPath()
|
||||
includedLineIndices, err := c.Git().Patch.PatchBuilder.GetFileIncLineIndices(filename)
|
||||
if err != nil {
|
||||
c.Log.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return includedLineIndices
|
||||
},
|
||||
c,
|
||||
),
|
||||
CustomPatchBuilderSecondary: NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
Kind: types.MAIN_CONTEXT,
|
||||
View: c.Views().PatchBuildingSecondary,
|
||||
WindowName: "secondary",
|
||||
Key: PATCH_BUILDING_SECONDARY_CONTEXT_KEY,
|
||||
Focusable: false,
|
||||
}),
|
||||
),
|
||||
MergeConflicts: NewMergeConflictsContext(
|
||||
c,
|
||||
),
|
||||
Confirmation: NewConfirmationContext(c),
|
||||
CommitMessage: NewCommitMessageContext(c),
|
||||
CommitDescription: NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
Kind: types.PERSISTENT_POPUP,
|
||||
View: c.Views().CommitDescription,
|
||||
WindowName: "commitDescription",
|
||||
Key: COMMIT_DESCRIPTION_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
HasUncontrolledBounds: true,
|
||||
}),
|
||||
),
|
||||
Search: NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
Kind: types.PERSISTENT_POPUP,
|
||||
View: c.Views().Search,
|
||||
WindowName: "search",
|
||||
Key: SEARCH_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
}),
|
||||
),
|
||||
CommandLog: NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
Kind: types.EXTRAS_CONTEXT,
|
||||
View: c.Views().Extras,
|
||||
WindowName: "extras",
|
||||
Key: COMMAND_LOG_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
}),
|
||||
),
|
||||
Snake: NewSimpleContext(
|
||||
NewBaseContext(NewBaseContextOpts{
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
View: c.Views().Snake,
|
||||
WindowName: "files",
|
||||
Key: SNAKE_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
}),
|
||||
),
|
||||
Options: NewDisplayContext(OPTIONS_CONTEXT_KEY, c.Views().Options, "options"),
|
||||
AppStatus: NewDisplayContext(APP_STATUS_CONTEXT_KEY, c.Views().AppStatus, "appStatus"),
|
||||
SearchPrefix: NewDisplayContext(SEARCH_PREFIX_CONTEXT_KEY, c.Views().SearchPrefix, "searchPrefix"),
|
||||
Information: NewDisplayContext(INFORMATION_CONTEXT_KEY, c.Views().Information, "information"),
|
||||
Limit: NewDisplayContext(LIMIT_CONTEXT_KEY, c.Views().Limit, "limit"),
|
||||
}
|
||||
}
|
||||
@@ -6,29 +6,12 @@ import (
|
||||
)
|
||||
|
||||
type SimpleContext struct {
|
||||
OnFocus func(opts types.OnFocusOpts) error
|
||||
OnFocusLost func(opts types.OnFocusLostOpts) error
|
||||
OnRender func() error
|
||||
// this is for pushing some content to the main view
|
||||
OnRenderToMain func() error
|
||||
|
||||
*BaseContext
|
||||
}
|
||||
|
||||
type ContextCallbackOpts struct {
|
||||
OnFocus func(opts types.OnFocusOpts) error
|
||||
OnFocusLost func(opts types.OnFocusLostOpts) error
|
||||
OnRender func() error
|
||||
OnRenderToMain func() error
|
||||
}
|
||||
|
||||
func NewSimpleContext(baseContext *BaseContext, opts ContextCallbackOpts) *SimpleContext {
|
||||
func NewSimpleContext(baseContext *BaseContext) *SimpleContext {
|
||||
return &SimpleContext{
|
||||
OnFocus: opts.OnFocus,
|
||||
OnFocusLost: opts.OnFocusLost,
|
||||
OnRender: opts.OnRender,
|
||||
OnRenderToMain: opts.OnRenderToMain,
|
||||
BaseContext: baseContext,
|
||||
BaseContext: baseContext,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +28,6 @@ func NewDisplayContext(key types.ContextKey, view *gocui.View, windowName string
|
||||
Focusable: false,
|
||||
Transient: false,
|
||||
}),
|
||||
ContextCallbackOpts{},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -54,14 +36,14 @@ func (self *SimpleContext) HandleFocus(opts types.OnFocusOpts) error {
|
||||
self.GetViewTrait().SetHighlight(true)
|
||||
}
|
||||
|
||||
if self.OnFocus != nil {
|
||||
if err := self.OnFocus(opts); err != nil {
|
||||
if self.onFocusFn != nil {
|
||||
if err := self.onFocusFn(opts); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if self.OnRenderToMain != nil {
|
||||
if err := self.OnRenderToMain(); err != nil {
|
||||
if self.onRenderToMainFn != nil {
|
||||
if err := self.onRenderToMainFn(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -70,22 +52,19 @@ func (self *SimpleContext) HandleFocus(opts types.OnFocusOpts) error {
|
||||
}
|
||||
|
||||
func (self *SimpleContext) HandleFocusLost(opts types.OnFocusLostOpts) error {
|
||||
if self.OnFocusLost != nil {
|
||||
return self.OnFocusLost(opts)
|
||||
if self.onFocusLostFn != nil {
|
||||
return self.onFocusLostFn(opts)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SimpleContext) HandleRender() error {
|
||||
if self.OnRender != nil {
|
||||
return self.OnRender()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *SimpleContext) HandleRenderToMain() error {
|
||||
if self.OnRenderToMain != nil {
|
||||
return self.OnRenderToMain()
|
||||
if self.onRenderToMainFn != nil {
|
||||
return self.onRenderToMainFn()
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@@ -11,35 +11,30 @@ type StashContext struct {
|
||||
*ListContextTrait
|
||||
}
|
||||
|
||||
var _ types.IListContext = (*StashContext)(nil)
|
||||
var (
|
||||
_ types.IListContext = (*StashContext)(nil)
|
||||
_ types.DiffableContext = (*StashContext)(nil)
|
||||
)
|
||||
|
||||
func NewStashContext(
|
||||
getModel func() []*models.StashEntry,
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
c *ContextCommon,
|
||||
) *StashContext {
|
||||
viewModel := NewBasicViewModel(getModel)
|
||||
viewModel := NewBasicViewModel(func() []*models.StashEntry { return c.Model().StashEntries })
|
||||
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
return presentation.GetStashEntryListDisplayStrings(c.Model().StashEntries, c.Modes().Diffing.Ref)
|
||||
}
|
||||
|
||||
return &StashContext{
|
||||
BasicViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: view,
|
||||
View: c.Views().Stash,
|
||||
WindowName: "stash",
|
||||
Key: STASH_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
OnFocusLost: onFocusLost,
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
})),
|
||||
list: viewModel,
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
@@ -67,3 +62,9 @@ func (self *StashContext) GetSelectedRef() types.Ref {
|
||||
}
|
||||
return stash
|
||||
}
|
||||
|
||||
func (self *StashContext) GetDiffTerminals() []string {
|
||||
itemId := self.GetSelectedItemId()
|
||||
|
||||
return []string{itemId}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package context
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
@@ -15,23 +15,39 @@ type SubCommitsContext struct {
|
||||
*DynamicTitleBuilder
|
||||
}
|
||||
|
||||
var _ types.IListContext = (*SubCommitsContext)(nil)
|
||||
var (
|
||||
_ types.IListContext = (*SubCommitsContext)(nil)
|
||||
_ types.DiffableContext = (*SubCommitsContext)(nil)
|
||||
)
|
||||
|
||||
func NewSubCommitsContext(
|
||||
getModel func() []*models.Commit,
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
c *ContextCommon,
|
||||
) *SubCommitsContext {
|
||||
viewModel := &SubCommitsViewModel{
|
||||
BasicViewModel: NewBasicViewModel(getModel),
|
||||
ref: nil,
|
||||
limitCommits: true,
|
||||
BasicViewModel: NewBasicViewModel(
|
||||
func() []*models.Commit { return c.Model().SubCommits },
|
||||
),
|
||||
ref: nil,
|
||||
limitCommits: true,
|
||||
}
|
||||
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
selectedCommitSha := ""
|
||||
if c.CurrentContext().GetKey() == SUB_COMMITS_CONTEXT_KEY {
|
||||
selectedCommit := viewModel.GetSelected()
|
||||
if selectedCommit != nil {
|
||||
selectedCommitSha = selectedCommit.Sha
|
||||
}
|
||||
}
|
||||
return getCommitsDisplayStrings(
|
||||
c,
|
||||
c.Model().SubCommits,
|
||||
startIdx,
|
||||
length,
|
||||
selectedCommitSha,
|
||||
false,
|
||||
git_commands.NewNullBisectInfo(),
|
||||
)
|
||||
}
|
||||
|
||||
return &SubCommitsContext{
|
||||
@@ -40,17 +56,13 @@ func NewSubCommitsContext(
|
||||
ViewportListContextTrait: &ViewportListContextTrait{
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: view,
|
||||
View: c.Views().SubCommits,
|
||||
WindowName: "branches",
|
||||
Key: SUB_COMMITS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
Transient: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
OnFocusLost: onFocusLost,
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
})),
|
||||
list: viewModel,
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
@@ -111,3 +123,9 @@ func (self *SubCommitsContext) SetLimitCommits(value bool) {
|
||||
func (self *SubCommitsContext) GetLimitCommits() bool {
|
||||
return self.limitCommits
|
||||
}
|
||||
|
||||
func (self *SubCommitsContext) GetDiffTerminals() []string {
|
||||
itemId := self.GetSelectedItemId()
|
||||
|
||||
return []string{itemId}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@@ -13,33 +13,23 @@ type SubmodulesContext struct {
|
||||
|
||||
var _ types.IListContext = (*SubmodulesContext)(nil)
|
||||
|
||||
func NewSubmodulesContext(
|
||||
getModel func() []*models.SubmoduleConfig,
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
func NewSubmodulesContext(c *ContextCommon) *SubmodulesContext {
|
||||
viewModel := NewBasicViewModel(func() []*models.SubmoduleConfig { return c.Model().Submodules })
|
||||
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *SubmodulesContext {
|
||||
viewModel := NewBasicViewModel(getModel)
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
return presentation.GetSubmoduleListDisplayStrings(c.Model().Submodules)
|
||||
}
|
||||
|
||||
return &SubmodulesContext{
|
||||
BasicViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: view,
|
||||
View: c.Views().Submodules,
|
||||
WindowName: "files",
|
||||
Key: SUBMODULES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
OnFocusLost: onFocusLost,
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
})),
|
||||
list: viewModel,
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
|
||||
@@ -1,45 +1,59 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/tasks"
|
||||
)
|
||||
|
||||
type SuggestionsContext struct {
|
||||
*BasicViewModel[*types.Suggestion]
|
||||
*ListContextTrait
|
||||
|
||||
State *SuggestionsContextState
|
||||
}
|
||||
|
||||
type SuggestionsContextState struct {
|
||||
Suggestions []*types.Suggestion
|
||||
OnConfirm func() error
|
||||
OnClose func() error
|
||||
AsyncHandler *tasks.AsyncHandler
|
||||
|
||||
// FindSuggestions will take a string that the user has typed into a prompt
|
||||
// and return a slice of suggestions which match that string.
|
||||
FindSuggestions func(string) []*types.Suggestion
|
||||
}
|
||||
|
||||
var _ types.IListContext = (*SuggestionsContext)(nil)
|
||||
|
||||
func NewSuggestionsContext(
|
||||
getModel func() []*types.Suggestion,
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
c *ContextCommon,
|
||||
) *SuggestionsContext {
|
||||
state := &SuggestionsContextState{
|
||||
AsyncHandler: tasks.NewAsyncHandler(),
|
||||
}
|
||||
getModel := func() []*types.Suggestion {
|
||||
return state.Suggestions
|
||||
}
|
||||
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
return presentation.GetSuggestionListDisplayStrings(state.Suggestions)
|
||||
}
|
||||
|
||||
viewModel := NewBasicViewModel(getModel)
|
||||
|
||||
return &SuggestionsContext{
|
||||
State: state,
|
||||
BasicViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: view,
|
||||
View: c.Views().Suggestions,
|
||||
WindowName: "suggestions",
|
||||
Key: SUGGESTIONS_CONTEXT_KEY,
|
||||
Kind: types.PERSISTENT_POPUP,
|
||||
Focusable: true,
|
||||
HasUncontrolledBounds: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
OnFocusLost: onFocusLost,
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
})),
|
||||
list: viewModel,
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
@@ -55,3 +69,22 @@ func (self *SuggestionsContext) GetSelectedItemId() string {
|
||||
|
||||
return item.Value
|
||||
}
|
||||
|
||||
func (self *SuggestionsContext) SetSuggestions(suggestions []*types.Suggestion) {
|
||||
self.State.Suggestions = suggestions
|
||||
self.SetSelectedLineIdx(0)
|
||||
self.c.ResetViewOrigin(self.GetView())
|
||||
_ = self.HandleRender()
|
||||
}
|
||||
|
||||
func (self *SuggestionsContext) RefreshSuggestions() {
|
||||
self.State.AsyncHandler.Do(func() func() {
|
||||
findSuggestionsFn := self.State.FindSuggestions
|
||||
if findSuggestionsFn != nil {
|
||||
suggestions := findSuggestionsFn(self.c.GetPromptInput())
|
||||
return func() { self.SetSuggestions(suggestions) }
|
||||
} else {
|
||||
return func() {}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@@ -11,35 +11,30 @@ type TagsContext struct {
|
||||
*ListContextTrait
|
||||
}
|
||||
|
||||
var _ types.IListContext = (*TagsContext)(nil)
|
||||
var (
|
||||
_ types.IListContext = (*TagsContext)(nil)
|
||||
_ types.DiffableContext = (*TagsContext)(nil)
|
||||
)
|
||||
|
||||
func NewTagsContext(
|
||||
getModel func() []*models.Tag,
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
c *ContextCommon,
|
||||
) *TagsContext {
|
||||
viewModel := NewBasicViewModel(getModel)
|
||||
viewModel := NewBasicViewModel(func() []*models.Tag { return c.Model().Tags })
|
||||
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
return presentation.GetTagListDisplayStrings(c.Model().Tags, c.Modes().Diffing.Ref)
|
||||
}
|
||||
|
||||
return &TagsContext{
|
||||
BasicViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: view,
|
||||
View: c.Views().Tags,
|
||||
WindowName: "branches",
|
||||
Key: TAGS_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
OnFocusLost: onFocusLost,
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
})),
|
||||
list: viewModel,
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
@@ -63,3 +58,9 @@ func (self *TagsContext) GetSelectedRef() types.Ref {
|
||||
}
|
||||
return tag
|
||||
}
|
||||
|
||||
func (self *TagsContext) GetDiffTerminals() []string {
|
||||
itemId := self.GetSelectedItemId()
|
||||
|
||||
return []string{itemId}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,6 @@ func (self *ViewportListContextTrait) FocusLine() {
|
||||
|
||||
startIdx, length := self.GetViewTrait().ViewPortYBounds()
|
||||
displayStrings := self.ListContextTrait.getDisplayStrings(startIdx, length)
|
||||
content := utils.RenderDisplayStrings(displayStrings)
|
||||
content := utils.RenderDisplayStrings(displayStrings, nil)
|
||||
self.GetViewTrait().SetViewPortContent(content)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package context
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
@@ -14,33 +15,30 @@ type WorkingTreeContext struct {
|
||||
|
||||
var _ types.IListContext = (*WorkingTreeContext)(nil)
|
||||
|
||||
func NewWorkingTreeContext(
|
||||
getModel func() []*models.File,
|
||||
view *gocui.View,
|
||||
getDisplayStrings func(startIdx int, length int) [][]string,
|
||||
func NewWorkingTreeContext(c *ContextCommon) *WorkingTreeContext {
|
||||
viewModel := filetree.NewFileTreeViewModel(
|
||||
func() []*models.File { return c.Model().Files },
|
||||
c.Log,
|
||||
c.UserConfig.Gui.ShowFileTree,
|
||||
)
|
||||
|
||||
onFocus func(types.OnFocusOpts) error,
|
||||
onRenderToMain func() error,
|
||||
onFocusLost func(opts types.OnFocusLostOpts) error,
|
||||
|
||||
c *types.HelperCommon,
|
||||
) *WorkingTreeContext {
|
||||
viewModel := filetree.NewFileTreeViewModel(getModel, c.Log, c.UserConfig.Gui.ShowFileTree)
|
||||
getDisplayStrings := func(startIdx int, length int) [][]string {
|
||||
lines := presentation.RenderFileTree(viewModel, c.Modes().Diffing.Ref, c.Model().Submodules)
|
||||
return slices.Map(lines, func(line string) []string {
|
||||
return []string{line}
|
||||
})
|
||||
}
|
||||
|
||||
return &WorkingTreeContext{
|
||||
FileTreeViewModel: viewModel,
|
||||
ListContextTrait: &ListContextTrait{
|
||||
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
|
||||
View: view,
|
||||
View: c.Views().Files,
|
||||
WindowName: "files",
|
||||
Key: FILES_CONTEXT_KEY,
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
Focusable: true,
|
||||
}), ContextCallbackOpts{
|
||||
OnFocus: onFocus,
|
||||
OnFocusLost: onFocusLost,
|
||||
OnRenderToMain: onRenderToMain,
|
||||
}),
|
||||
})),
|
||||
list: viewModel,
|
||||
getDisplayStrings: getDisplayStrings,
|
||||
c: c,
|
||||
|
||||
+9
-271
@@ -6,273 +6,11 @@ import (
|
||||
)
|
||||
|
||||
func (gui *Gui) contextTree() *context.ContextTree {
|
||||
return &context.ContextTree{
|
||||
Global: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.GLOBAL_CONTEXT,
|
||||
View: nil, // TODO: see if this breaks anything
|
||||
WindowName: "",
|
||||
Key: context.GLOBAL_CONTEXT_KEY,
|
||||
Focusable: false,
|
||||
HasUncontrolledBounds: true, // setting to true because the global context doesn't even have a view
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnRenderToMain: gui.statusRenderToMain,
|
||||
},
|
||||
),
|
||||
Status: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
View: gui.Views.Status,
|
||||
WindowName: "status",
|
||||
Key: context.STATUS_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnRenderToMain: gui.statusRenderToMain,
|
||||
},
|
||||
),
|
||||
Snake: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.SIDE_CONTEXT,
|
||||
View: gui.Views.Snake,
|
||||
WindowName: "files",
|
||||
Key: context.SNAKE_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnFocus: func(opts types.OnFocusOpts) error {
|
||||
gui.startSnake()
|
||||
return nil
|
||||
},
|
||||
OnFocusLost: func(opts types.OnFocusLostOpts) error {
|
||||
gui.snakeGame.Exit()
|
||||
gui.moveToTopOfWindow(gui.State.Contexts.Submodules)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
),
|
||||
Files: gui.filesListContext(),
|
||||
Submodules: gui.submodulesListContext(),
|
||||
Menu: gui.menuListContext(),
|
||||
Remotes: gui.remotesListContext(),
|
||||
RemoteBranches: gui.remoteBranchesListContext(),
|
||||
LocalCommits: gui.branchCommitsListContext(),
|
||||
CommitFiles: gui.commitFilesListContext(),
|
||||
ReflogCommits: gui.reflogCommitsListContext(),
|
||||
SubCommits: gui.subCommitsListContext(),
|
||||
Branches: gui.branchesListContext(),
|
||||
Tags: gui.tagsListContext(),
|
||||
Stash: gui.stashListContext(),
|
||||
Suggestions: gui.suggestionsListContext(),
|
||||
Normal: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.MAIN_CONTEXT,
|
||||
View: gui.Views.Main,
|
||||
WindowName: "main",
|
||||
Key: context.NORMAL_MAIN_CONTEXT_KEY,
|
||||
Focusable: false,
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnFocus: func(opts types.OnFocusOpts) error {
|
||||
return nil // TODO: should we do something here? We should allow for scrolling the panel
|
||||
},
|
||||
},
|
||||
),
|
||||
NormalSecondary: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.MAIN_CONTEXT,
|
||||
View: gui.Views.Secondary,
|
||||
WindowName: "secondary",
|
||||
Key: context.NORMAL_SECONDARY_CONTEXT_KEY,
|
||||
Focusable: false,
|
||||
}),
|
||||
context.ContextCallbackOpts{},
|
||||
),
|
||||
Staging: context.NewPatchExplorerContext(
|
||||
gui.Views.Staging,
|
||||
"main",
|
||||
context.STAGING_MAIN_CONTEXT_KEY,
|
||||
func(opts types.OnFocusOpts) error {
|
||||
gui.Views.Staging.Wrap = false
|
||||
gui.Views.StagingSecondary.Wrap = false
|
||||
|
||||
return gui.refreshStagingPanel(opts)
|
||||
},
|
||||
func(opts types.OnFocusLostOpts) error {
|
||||
gui.State.Contexts.Staging.SetState(nil)
|
||||
|
||||
if opts.NewContextKey != context.STAGING_SECONDARY_CONTEXT_KEY {
|
||||
gui.Views.Staging.Wrap = true
|
||||
gui.Views.StagingSecondary.Wrap = true
|
||||
_ = gui.State.Contexts.Staging.Render(false)
|
||||
_ = gui.State.Contexts.StagingSecondary.Render(false)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
func() []int { return nil },
|
||||
gui.c,
|
||||
),
|
||||
StagingSecondary: context.NewPatchExplorerContext(
|
||||
gui.Views.StagingSecondary,
|
||||
"secondary",
|
||||
context.STAGING_SECONDARY_CONTEXT_KEY,
|
||||
func(opts types.OnFocusOpts) error {
|
||||
gui.Views.Staging.Wrap = false
|
||||
gui.Views.StagingSecondary.Wrap = false
|
||||
|
||||
return gui.refreshStagingPanel(opts)
|
||||
},
|
||||
func(opts types.OnFocusLostOpts) error {
|
||||
gui.State.Contexts.StagingSecondary.SetState(nil)
|
||||
|
||||
if opts.NewContextKey != context.STAGING_MAIN_CONTEXT_KEY {
|
||||
gui.Views.Staging.Wrap = true
|
||||
gui.Views.StagingSecondary.Wrap = true
|
||||
_ = gui.State.Contexts.Staging.Render(false)
|
||||
_ = gui.State.Contexts.StagingSecondary.Render(false)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
func() []int { return nil },
|
||||
gui.c,
|
||||
),
|
||||
CustomPatchBuilder: context.NewPatchExplorerContext(
|
||||
gui.Views.PatchBuilding,
|
||||
"main",
|
||||
context.PATCH_BUILDING_MAIN_CONTEXT_KEY,
|
||||
func(opts types.OnFocusOpts) error {
|
||||
// no need to change wrap on the secondary view because it can't be interacted with
|
||||
gui.Views.PatchBuilding.Wrap = false
|
||||
|
||||
return gui.refreshPatchBuildingPanel(opts)
|
||||
},
|
||||
func(opts types.OnFocusLostOpts) error {
|
||||
gui.Views.PatchBuilding.Wrap = true
|
||||
|
||||
if gui.git.Patch.PatchBuilder.IsEmpty() {
|
||||
gui.git.Patch.PatchBuilder.Reset()
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
func() []int {
|
||||
filename := gui.State.Contexts.CommitFiles.GetSelectedPath()
|
||||
includedLineIndices, err := gui.git.Patch.PatchBuilder.GetFileIncLineIndices(filename)
|
||||
if err != nil {
|
||||
gui.Log.Error(err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return includedLineIndices
|
||||
},
|
||||
gui.c,
|
||||
),
|
||||
CustomPatchBuilderSecondary: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.MAIN_CONTEXT,
|
||||
View: gui.Views.PatchBuildingSecondary,
|
||||
WindowName: "secondary",
|
||||
Key: context.PATCH_BUILDING_SECONDARY_CONTEXT_KEY,
|
||||
Focusable: false,
|
||||
}),
|
||||
context.ContextCallbackOpts{},
|
||||
),
|
||||
MergeConflicts: context.NewMergeConflictsContext(
|
||||
gui.Views.MergeConflicts,
|
||||
context.ContextCallbackOpts{
|
||||
OnFocus: OnFocusWrapper(func() error {
|
||||
gui.Views.MergeConflicts.Wrap = false
|
||||
|
||||
return gui.refreshMergePanel(true)
|
||||
}),
|
||||
OnFocusLost: func(opts types.OnFocusLostOpts) error {
|
||||
gui.State.Contexts.MergeConflicts.SetUserScrolling(false)
|
||||
gui.State.Contexts.MergeConflicts.GetState().ResetConflictSelection()
|
||||
gui.Views.MergeConflicts.Wrap = true
|
||||
|
||||
return nil
|
||||
},
|
||||
},
|
||||
gui.c,
|
||||
func() map[string]string {
|
||||
// wrapping in a function because contexts are initialized before helpers
|
||||
return gui.helpers.MergeConflicts.GetMergingOptions()
|
||||
},
|
||||
),
|
||||
Confirmation: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.TEMPORARY_POPUP,
|
||||
View: gui.Views.Confirmation,
|
||||
WindowName: "confirmation",
|
||||
Key: context.CONFIRMATION_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
HasUncontrolledBounds: true,
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnFocus: OnFocusWrapper(gui.handleAskFocused),
|
||||
OnFocusLost: func(types.OnFocusLostOpts) error {
|
||||
gui.deactivateConfirmationPrompt()
|
||||
return nil
|
||||
},
|
||||
},
|
||||
),
|
||||
CommitMessage: context.NewCommitMessageContext(
|
||||
gui.Views.CommitMessage,
|
||||
context.ContextCallbackOpts{
|
||||
OnFocus: OnFocusWrapper(gui.handleCommitMessageFocused),
|
||||
},
|
||||
),
|
||||
CommitDescription: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.PERSISTENT_POPUP,
|
||||
View: gui.Views.CommitDescription,
|
||||
WindowName: "commitDescription",
|
||||
Key: context.COMMIT_DESCRIPTION_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
HasUncontrolledBounds: true,
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnFocus: func(opts types.OnFocusOpts) error {
|
||||
_, err := gui.g.SetViewBeneath("commitDescription", "commitMessage", 10)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
),
|
||||
Search: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.PERSISTENT_POPUP,
|
||||
View: gui.Views.Search,
|
||||
WindowName: "search",
|
||||
Key: context.SEARCH_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
}),
|
||||
context.ContextCallbackOpts{},
|
||||
),
|
||||
CommandLog: context.NewSimpleContext(
|
||||
context.NewBaseContext(context.NewBaseContextOpts{
|
||||
Kind: types.EXTRAS_CONTEXT,
|
||||
View: gui.Views.Extras,
|
||||
WindowName: "extras",
|
||||
Key: context.COMMAND_LOG_CONTEXT_KEY,
|
||||
Focusable: true,
|
||||
}),
|
||||
context.ContextCallbackOpts{
|
||||
OnFocusLost: func(opts types.OnFocusLostOpts) error {
|
||||
gui.Views.Extras.Autoscroll = true
|
||||
return nil
|
||||
},
|
||||
},
|
||||
),
|
||||
Options: context.NewDisplayContext(context.OPTIONS_CONTEXT_KEY, gui.Views.Options, "options"),
|
||||
AppStatus: context.NewDisplayContext(context.APP_STATUS_CONTEXT_KEY, gui.Views.AppStatus, "appStatus"),
|
||||
SearchPrefix: context.NewDisplayContext(context.SEARCH_PREFIX_CONTEXT_KEY, gui.Views.SearchPrefix, "searchPrefix"),
|
||||
Information: context.NewDisplayContext(context.INFORMATION_CONTEXT_KEY, gui.Views.Information, "information"),
|
||||
Limit: context.NewDisplayContext(context.LIMIT_CONTEXT_KEY, gui.Views.Limit, "limit"),
|
||||
contextCommon := &context.ContextCommon{
|
||||
IGuiCommon: gui.c.IGuiCommon,
|
||||
Common: gui.c.Common,
|
||||
}
|
||||
return context.NewContextTree(contextCommon)
|
||||
}
|
||||
|
||||
// using this wrapper for when an onFocus function doesn't care about any potential
|
||||
@@ -283,10 +21,10 @@ func OnFocusWrapper(f func() error) func(opts types.OnFocusOpts) error {
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) getPatchExplorerContexts() []types.IPatchExplorerContext {
|
||||
return []types.IPatchExplorerContext{
|
||||
gui.State.Contexts.Staging,
|
||||
gui.State.Contexts.StagingSecondary,
|
||||
gui.State.Contexts.CustomPatchBuilder,
|
||||
func (gui *Gui) defaultSideContext() types.Context {
|
||||
if gui.State.Modes.Filtering.Active() {
|
||||
return gui.State.Contexts.LocalCommits
|
||||
} else {
|
||||
return gui.State.Contexts.Files
|
||||
}
|
||||
}
|
||||
|
||||
+139
-60
@@ -7,24 +7,22 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/modes/cherrypicking"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/services/custom_commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/snake"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/status"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
func (gui *Gui) resetControllers() {
|
||||
func (gui *Gui) Helpers() *helpers.Helpers {
|
||||
return gui.helpers
|
||||
}
|
||||
|
||||
func (gui *Gui) resetHelpersAndControllers() {
|
||||
helperCommon := gui.c
|
||||
osCommand := gui.os
|
||||
model := gui.State.Model
|
||||
refsHelper := helpers.NewRefsHelper(
|
||||
helperCommon,
|
||||
gui.git,
|
||||
gui.State.Contexts,
|
||||
model,
|
||||
)
|
||||
refsHelper := helpers.NewRefsHelper(helperCommon)
|
||||
|
||||
rebaseHelper := helpers.NewMergeAndRebaseHelper(helperCommon, refsHelper)
|
||||
suggestionsHelper := helpers.NewSuggestionsHelper(helperCommon)
|
||||
|
||||
rebaseHelper := helpers.NewMergeAndRebaseHelper(helperCommon, gui.State.Contexts, gui.git, refsHelper)
|
||||
suggestionsHelper := helpers.NewSuggestionsHelper(helperCommon, model, gui.refreshSuggestions)
|
||||
setCommitSummary := gui.getCommitMessageSetTextareaTextFn(func() *gocui.View { return gui.Views.CommitMessage })
|
||||
setCommitDescription := gui.getCommitMessageSetTextareaTextFn(func() *gocui.View { return gui.Views.CommitDescription })
|
||||
getCommitSummary := func() string {
|
||||
@@ -35,66 +33,86 @@ func (gui *Gui) resetControllers() {
|
||||
return strings.TrimSpace(gui.Views.CommitDescription.TextArea.GetContent())
|
||||
}
|
||||
commitsHelper := helpers.NewCommitsHelper(helperCommon,
|
||||
gui.State.Model,
|
||||
gui.State.Contexts,
|
||||
getCommitSummary,
|
||||
setCommitSummary,
|
||||
getCommitDescription,
|
||||
setCommitDescription,
|
||||
gui.RenderCommitLength,
|
||||
)
|
||||
gpgHelper := helpers.NewGpgHelper(helperCommon, gui.os, gui.git)
|
||||
|
||||
gpgHelper := helpers.NewGpgHelper(helperCommon)
|
||||
viewHelper := helpers.NewViewHelper(helperCommon, gui.State.Contexts)
|
||||
recordDirectoryHelper := helpers.NewRecordDirectoryHelper(helperCommon)
|
||||
patchBuildingHelper := helpers.NewPatchBuildingHelper(helperCommon)
|
||||
stagingHelper := helpers.NewStagingHelper(helperCommon)
|
||||
mergeConflictsHelper := helpers.NewMergeConflictsHelper(helperCommon)
|
||||
refreshHelper := helpers.NewRefreshHelper(helperCommon, refsHelper, rebaseHelper, patchBuildingHelper, stagingHelper, mergeConflictsHelper, gui.fileWatcher)
|
||||
diffHelper := helpers.NewDiffHelper(helperCommon)
|
||||
cherryPickHelper := helpers.NewCherryPickHelper(
|
||||
helperCommon,
|
||||
rebaseHelper,
|
||||
)
|
||||
bisectHelper := helpers.NewBisectHelper(helperCommon)
|
||||
windowHelper := helpers.NewWindowHelper(helperCommon, viewHelper)
|
||||
modeHelper := helpers.NewModeHelper(
|
||||
helperCommon,
|
||||
diffHelper,
|
||||
patchBuildingHelper,
|
||||
cherryPickHelper,
|
||||
rebaseHelper,
|
||||
bisectHelper,
|
||||
)
|
||||
appStatusHelper := helpers.NewAppStatusHelper(
|
||||
helperCommon,
|
||||
func() *status.StatusManager { return gui.statusManager },
|
||||
)
|
||||
gui.helpers = &helpers.Helpers{
|
||||
Refs: refsHelper,
|
||||
Host: helpers.NewHostHelper(helperCommon, gui.git),
|
||||
PatchBuilding: helpers.NewPatchBuildingHelper(helperCommon, gui.git, gui.State.Contexts),
|
||||
Bisect: helpers.NewBisectHelper(helperCommon, gui.git),
|
||||
Suggestions: suggestionsHelper,
|
||||
Files: helpers.NewFilesHelper(helperCommon, gui.git, osCommand),
|
||||
WorkingTree: helpers.NewWorkingTreeHelper(helperCommon, gui.git, gui.State.Contexts, refsHelper, model, setCommitSummary, commitsHelper, gpgHelper),
|
||||
Tags: helpers.NewTagsHelper(helperCommon, gui.git),
|
||||
GPG: gpgHelper,
|
||||
MergeAndRebase: rebaseHelper,
|
||||
MergeConflicts: helpers.NewMergeConflictsHelper(helperCommon, gui.State.Contexts, gui.git),
|
||||
CherryPick: helpers.NewCherryPickHelper(
|
||||
helperCommon,
|
||||
gui.git,
|
||||
gui.State.Contexts,
|
||||
func() *cherrypicking.CherryPicking { return gui.State.Modes.CherryPicking },
|
||||
rebaseHelper,
|
||||
Refs: refsHelper,
|
||||
Host: helpers.NewHostHelper(helperCommon),
|
||||
PatchBuilding: patchBuildingHelper,
|
||||
Staging: stagingHelper,
|
||||
Bisect: bisectHelper,
|
||||
Suggestions: suggestionsHelper,
|
||||
Files: helpers.NewFilesHelper(helperCommon),
|
||||
WorkingTree: helpers.NewWorkingTreeHelper(helperCommon, refsHelper, commitsHelper, gpgHelper),
|
||||
Tags: helpers.NewTagsHelper(helperCommon),
|
||||
GPG: helpers.NewGpgHelper(helperCommon),
|
||||
MergeAndRebase: rebaseHelper,
|
||||
MergeConflicts: mergeConflictsHelper,
|
||||
CherryPick: cherryPickHelper,
|
||||
Upstream: helpers.NewUpstreamHelper(helperCommon, suggestionsHelper.GetRemoteBranchesSuggestionsFunc),
|
||||
AmendHelper: helpers.NewAmendHelper(helperCommon, gpgHelper),
|
||||
Commits: commitsHelper,
|
||||
Snake: helpers.NewSnakeHelper(helperCommon),
|
||||
Diff: diffHelper,
|
||||
Repos: helpers.NewRecentReposHelper(helperCommon, recordDirectoryHelper, gui.onNewRepo),
|
||||
RecordDirectory: recordDirectoryHelper,
|
||||
Update: helpers.NewUpdateHelper(helperCommon, gui.Updater),
|
||||
Window: windowHelper,
|
||||
View: viewHelper,
|
||||
Refresh: refreshHelper,
|
||||
Confirmation: helpers.NewConfirmationHelper(helperCommon),
|
||||
Mode: modeHelper,
|
||||
AppStatus: appStatusHelper,
|
||||
WindowArrangement: helpers.NewWindowArrangementHelper(
|
||||
gui.c,
|
||||
windowHelper,
|
||||
modeHelper,
|
||||
appStatusHelper,
|
||||
),
|
||||
Upstream: helpers.NewUpstreamHelper(helperCommon, model, suggestionsHelper.GetRemoteBranchesSuggestionsFunc),
|
||||
AmendHelper: helpers.NewAmendHelper(helperCommon, gui.git, gpgHelper),
|
||||
Commits: commitsHelper,
|
||||
}
|
||||
|
||||
gui.CustomCommandsClient = custom_commands.NewClient(
|
||||
helperCommon,
|
||||
gui.os,
|
||||
gui.git,
|
||||
gui.State.Contexts,
|
||||
gui.helpers,
|
||||
)
|
||||
|
||||
common := controllers.NewControllerCommon(
|
||||
helperCommon,
|
||||
osCommand,
|
||||
gui.git,
|
||||
gui.helpers,
|
||||
model,
|
||||
gui.State.Contexts,
|
||||
gui.State.Modes,
|
||||
&gui.Mutexes,
|
||||
)
|
||||
common := controllers.NewControllerCommon(helperCommon, gui)
|
||||
|
||||
syncController := controllers.NewSyncController(
|
||||
common,
|
||||
)
|
||||
|
||||
submodulesController := controllers.NewSubmodulesController(
|
||||
common,
|
||||
gui.enterSubmodule,
|
||||
)
|
||||
submodulesController := controllers.NewSubmodulesController(common)
|
||||
|
||||
bisectController := controllers.NewBisectController(common)
|
||||
|
||||
@@ -113,8 +131,6 @@ func (gui *Gui) resetControllers() {
|
||||
tagsController := controllers.NewTagsController(common)
|
||||
filesController := controllers.NewFilesController(
|
||||
common,
|
||||
gui.enterSubmodule,
|
||||
setCommitSummary,
|
||||
)
|
||||
mergeConflictsController := controllers.NewMergeConflictsController(common)
|
||||
remotesController := controllers.NewRemotesController(
|
||||
@@ -135,7 +151,34 @@ func (gui *Gui) resetControllers() {
|
||||
stagingController := controllers.NewStagingController(common, gui.State.Contexts.Staging, gui.State.Contexts.StagingSecondary, false)
|
||||
stagingSecondaryController := controllers.NewStagingController(common, gui.State.Contexts.StagingSecondary, gui.State.Contexts.Staging, true)
|
||||
patchBuildingController := controllers.NewPatchBuildingController(common)
|
||||
snakeController := controllers.NewSnakeController(common, func() *snake.Game { return gui.snakeGame })
|
||||
snakeController := controllers.NewSnakeController(common)
|
||||
reflogCommitsController := controllers.NewReflogCommitsController(common)
|
||||
subCommitsController := controllers.NewSubCommitsController(common)
|
||||
statusController := controllers.NewStatusController(common)
|
||||
commandLogController := controllers.NewCommandLogController(common)
|
||||
confirmationController := controllers.NewConfirmationController(common)
|
||||
suggestionsController := controllers.NewSuggestionsController(common)
|
||||
jumpToSideWindowController := controllers.NewJumpToSideWindowController(common)
|
||||
|
||||
sideWindowControllerFactory := controllers.NewSideWindowControllerFactory(common)
|
||||
|
||||
// allow for navigating between side window contexts
|
||||
for _, context := range []types.Context{
|
||||
gui.State.Contexts.Status,
|
||||
gui.State.Contexts.Remotes,
|
||||
gui.State.Contexts.Tags,
|
||||
gui.State.Contexts.Branches,
|
||||
gui.State.Contexts.RemoteBranches,
|
||||
gui.State.Contexts.Files,
|
||||
gui.State.Contexts.Submodules,
|
||||
gui.State.Contexts.ReflogCommits,
|
||||
gui.State.Contexts.LocalCommits,
|
||||
gui.State.Contexts.CommitFiles,
|
||||
gui.State.Contexts.SubCommits,
|
||||
gui.State.Contexts.Stash,
|
||||
} {
|
||||
controllers.AttachControllers(context, sideWindowControllerFactory.Create(context))
|
||||
}
|
||||
|
||||
setSubCommits := func(commits []*models.Commit) {
|
||||
gui.Mutexes.SubCommitsMutex.Lock()
|
||||
@@ -161,7 +204,7 @@ func (gui *Gui) resetControllers() {
|
||||
gui.State.Contexts.Stash,
|
||||
} {
|
||||
controllers.AttachControllers(context, controllers.NewSwitchToDiffFilesController(
|
||||
common, gui.SwitchToCommitFilesContext, context,
|
||||
common, context, gui.State.Contexts.CommitFiles,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -173,6 +216,14 @@ func (gui *Gui) resetControllers() {
|
||||
controllers.AttachControllers(context, controllers.NewBasicCommitsController(common, context))
|
||||
}
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.ReflogCommits,
|
||||
reflogCommitsController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.SubCommits,
|
||||
subCommitsController,
|
||||
)
|
||||
|
||||
// TODO: add scroll controllers for main panels (need to bring some more functionality across for that e.g. reading more from the currently displayed git command)
|
||||
controllers.AttachControllers(gui.State.Contexts.Staging,
|
||||
stagingController,
|
||||
@@ -256,11 +307,28 @@ func (gui *Gui) resetControllers() {
|
||||
remoteBranchesController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.Status,
|
||||
statusController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.CommandLog,
|
||||
commandLogController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.Confirmation,
|
||||
confirmationController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.Suggestions,
|
||||
suggestionsController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.Global,
|
||||
syncController,
|
||||
undoController,
|
||||
globalController,
|
||||
contextLinesController,
|
||||
jumpToSideWindowController,
|
||||
)
|
||||
|
||||
controllers.AttachControllers(gui.State.Contexts.Snake,
|
||||
@@ -268,8 +336,19 @@ func (gui *Gui) resetControllers() {
|
||||
)
|
||||
|
||||
// this must come last so that we've got our click handlers defined against the context
|
||||
listControllerFactory := controllers.NewListControllerFactory(gui.c)
|
||||
for _, context := range gui.getListContexts() {
|
||||
listControllerFactory := controllers.NewListControllerFactory(common)
|
||||
for _, context := range gui.c.Context().AllList() {
|
||||
controllers.AttachControllers(context, listControllerFactory.Create(context))
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) getCommitMessageSetTextareaTextFn(getView func() *gocui.View) func(string) {
|
||||
return func(text string) {
|
||||
// using a getView function so that we don't need to worry about when the view is created
|
||||
view := getView()
|
||||
view.ClearTextArea()
|
||||
view.TextArea.TypeString(text)
|
||||
gui.helpers.Confirmation.ResizeCommitMessagePanels()
|
||||
view.RenderTextArea()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,5 +7,8 @@ func AttachControllers(context types.Context, controllers ...types.IController)
|
||||
context.AddKeybindingsFn(controller.GetKeybindings)
|
||||
context.AddMouseKeybindingsFn(controller.GetMouseKeybindings)
|
||||
context.AddOnClickFn(controller.GetOnClick())
|
||||
context.AddOnRenderToMainFn(controller.GetOnRenderToMain())
|
||||
context.AddOnFocusFn(controller.GetOnFocus())
|
||||
context.AddOnFocusLostFn(controller.GetOnFocusLost())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,3 +18,15 @@ func (self *baseController) GetMouseKeybindings(opts types.KeybindingsOpts) []*g
|
||||
func (self *baseController) GetOnClick() func() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *baseController) GetOnRenderToMain() func() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *baseController) GetOnFocus() func(types.OnFocusOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *baseController) GetOnFocusLost() func(types.OnFocusLostOpts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -20,15 +20,15 @@ type ContainsCommits interface {
|
||||
|
||||
type BasicCommitsController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
c *ControllerCommon
|
||||
context ContainsCommits
|
||||
}
|
||||
|
||||
func NewBasicCommitsController(controllerCommon *controllerCommon, context ContainsCommits) *BasicCommitsController {
|
||||
func NewBasicCommitsController(controllerCommon *ControllerCommon, context ContainsCommits) *BasicCommitsController {
|
||||
return &BasicCommitsController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: controllerCommon,
|
||||
context: context,
|
||||
baseController: baseController{},
|
||||
c: controllerCommon,
|
||||
context: context,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (self *BasicCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Commits.ResetCherryPick),
|
||||
Handler: self.helpers.CherryPick.Reset,
|
||||
Handler: self.c.Helpers().CherryPick.Reset,
|
||||
Description: self.c.Tr.LcResetCherryPick,
|
||||
},
|
||||
}
|
||||
@@ -141,7 +141,7 @@ func (self *BasicCommitsController) copyCommitAttribute(commit *models.Commit) e
|
||||
|
||||
func (self *BasicCommitsController) copyCommitSHAToClipboard(commit *models.Commit) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.CopyCommitSHAToClipboard)
|
||||
if err := self.os.CopyToClipboard(commit.Sha); err != nil {
|
||||
if err := self.c.OS().CopyToClipboard(commit.Sha); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -150,13 +150,13 @@ func (self *BasicCommitsController) copyCommitSHAToClipboard(commit *models.Comm
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) copyCommitURLToClipboard(commit *models.Commit) error {
|
||||
url, err := self.helpers.Host.GetCommitURL(commit.Sha)
|
||||
url, err := self.c.Helpers().Host.GetCommitURL(commit.Sha)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CopyCommitURLToClipboard)
|
||||
if err := self.os.CopyToClipboard(url); err != nil {
|
||||
if err := self.c.OS().CopyToClipboard(url); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -165,13 +165,13 @@ func (self *BasicCommitsController) copyCommitURLToClipboard(commit *models.Comm
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) copyCommitDiffToClipboard(commit *models.Commit) error {
|
||||
diff, err := self.git.Commit.GetCommitDiff(commit.Sha)
|
||||
diff, err := self.c.Git().Commit.GetCommitDiff(commit.Sha)
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CopyCommitDiffToClipboard)
|
||||
if err := self.os.CopyToClipboard(diff); err != nil {
|
||||
if err := self.c.OS().CopyToClipboard(diff); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -180,7 +180,7 @@ func (self *BasicCommitsController) copyCommitDiffToClipboard(commit *models.Com
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) copyAuthorToClipboard(commit *models.Commit) error {
|
||||
author, err := self.git.Commit.GetCommitAuthor(commit.Sha)
|
||||
author, err := self.c.Git().Commit.GetCommitAuthor(commit.Sha)
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
@@ -188,7 +188,7 @@ func (self *BasicCommitsController) copyAuthorToClipboard(commit *models.Commit)
|
||||
formattedAuthor := fmt.Sprintf("%s <%s>", author.Name, author.Email)
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CopyCommitAuthorToClipboard)
|
||||
if err := self.os.CopyToClipboard(formattedAuthor); err != nil {
|
||||
if err := self.c.OS().CopyToClipboard(formattedAuthor); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -197,13 +197,13 @@ func (self *BasicCommitsController) copyAuthorToClipboard(commit *models.Commit)
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) copyCommitMessageToClipboard(commit *models.Commit) error {
|
||||
message, err := self.git.Commit.GetCommitMessage(commit.Sha)
|
||||
message, err := self.c.Git().Commit.GetCommitMessage(commit.Sha)
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CopyCommitMessageToClipboard)
|
||||
if err := self.os.CopyToClipboard(message); err != nil {
|
||||
if err := self.c.OS().CopyToClipboard(message); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -212,13 +212,13 @@ func (self *BasicCommitsController) copyCommitMessageToClipboard(commit *models.
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) openInBrowser(commit *models.Commit) error {
|
||||
url, err := self.helpers.Host.GetCommitURL(commit.Sha)
|
||||
url, err := self.c.Helpers().Host.GetCommitURL(commit.Sha)
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.OpenCommitInBrowser)
|
||||
if err := self.os.OpenLink(url); err != nil {
|
||||
if err := self.c.OS().OpenLink(url); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -226,11 +226,11 @@ func (self *BasicCommitsController) openInBrowser(commit *models.Commit) error {
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) newBranch(commit *models.Commit) error {
|
||||
return self.helpers.Refs.NewBranch(commit.RefName(), commit.Description(), "")
|
||||
return self.c.Helpers().Refs.NewBranch(commit.RefName(), commit.Description(), "")
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) createResetMenu(commit *models.Commit) error {
|
||||
return self.helpers.Refs.CreateGitResetMenu(commit.Sha)
|
||||
return self.c.Helpers().Refs.CreateGitResetMenu(commit.Sha)
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) checkout(commit *models.Commit) error {
|
||||
@@ -239,15 +239,15 @@ func (self *BasicCommitsController) checkout(commit *models.Commit) error {
|
||||
Prompt: self.c.Tr.SureCheckoutThisCommit,
|
||||
HandleConfirm: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.CheckoutCommit)
|
||||
return self.helpers.Refs.CheckoutRef(commit.Sha, types.CheckoutRefOptions{})
|
||||
return self.c.Helpers().Refs.CheckoutRef(commit.Sha, types.CheckoutRefOptions{})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) copy(commit *models.Commit) error {
|
||||
return self.helpers.CherryPick.Copy(commit, self.context.GetCommits(), self.context)
|
||||
return self.c.Helpers().CherryPick.Copy(commit, self.context.GetCommits(), self.context)
|
||||
}
|
||||
|
||||
func (self *BasicCommitsController) copyRange(*models.Commit) error {
|
||||
return self.helpers.CherryPick.CopyRange(self.context.GetSelectedLineIdx(), self.context.GetCommits(), self.context)
|
||||
return self.c.Helpers().CherryPick.CopyRange(self.context.GetSelectedLineIdx(), self.context.GetCommits(), self.context)
|
||||
}
|
||||
|
||||
@@ -12,17 +12,17 @@ import (
|
||||
|
||||
type BisectController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &BisectController{}
|
||||
|
||||
func NewBisectController(
|
||||
common *controllerCommon,
|
||||
common *ControllerCommon,
|
||||
) *BisectController {
|
||||
return &BisectController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: common,
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func (self *BisectController) GetKeybindings(opts types.KeybindingsOpts) []*type
|
||||
func (self *BisectController) openMenu(commit *models.Commit) error {
|
||||
// no shame in getting this directly rather than using the cached value
|
||||
// given how cheap it is to obtain
|
||||
info := self.git.Bisect.GetInfo()
|
||||
info := self.c.Git().Bisect.GetInfo()
|
||||
if info.Started() {
|
||||
return self.openMidBisectMenu(info, commit)
|
||||
} else {
|
||||
@@ -63,14 +63,14 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
||||
selectCurrentAfter := info.GetCurrentSha() == "" || info.GetCurrentSha() == commit.Sha
|
||||
// we need to wait to reselect if our bisect commits aren't ancestors of our 'start'
|
||||
// ref, because we'll be reloading our commits in that case.
|
||||
waitToReselect := selectCurrentAfter && !self.git.Bisect.ReachableFromStart(info)
|
||||
waitToReselect := selectCurrentAfter && !self.c.Git().Bisect.ReachableFromStart(info)
|
||||
|
||||
menuItems := []*types.MenuItem{
|
||||
{
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, commit.ShortSha(), info.NewTerm()),
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.BisectMark)
|
||||
if err := self.git.Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
||||
if err := self.c.Git().Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.Mark, commit.ShortSha(), info.OldTerm()),
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.BisectMark)
|
||||
if err := self.git.Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
||||
if err := self.c.Git().Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.Skip, commit.ShortSha()),
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.BisectSkip)
|
||||
if err := self.git.Bisect.Skip(commit.Sha); err != nil {
|
||||
if err := self.c.Git().Bisect.Skip(commit.Sha); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ func (self *BisectController) openMidBisectMenu(info *git_commands.BisectInfo, c
|
||||
{
|
||||
Label: self.c.Tr.Bisect.ResetOption,
|
||||
OnPress: func() error {
|
||||
return self.helpers.Bisect.Reset()
|
||||
return self.c.Helpers().Bisect.Reset()
|
||||
},
|
||||
Key: 'r',
|
||||
},
|
||||
@@ -125,15 +125,15 @@ func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo,
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortSha(), info.NewTerm()),
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.StartBisect)
|
||||
if err := self.git.Bisect.Start(); err != nil {
|
||||
if err := self.c.Git().Bisect.Start(); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
if err := self.git.Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
||||
if err := self.c.Git().Bisect.Mark(commit.Sha, info.NewTerm()); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.helpers.Bisect.PostBisectCommandRefresh()
|
||||
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
||||
},
|
||||
Key: 'b',
|
||||
},
|
||||
@@ -141,15 +141,15 @@ func (self *BisectController) openStartBisectMenu(info *git_commands.BisectInfo,
|
||||
Label: fmt.Sprintf(self.c.Tr.Bisect.MarkStart, commit.ShortSha(), info.OldTerm()),
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.StartBisect)
|
||||
if err := self.git.Bisect.Start(); err != nil {
|
||||
if err := self.c.Git().Bisect.Start(); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
if err := self.git.Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
||||
if err := self.c.Git().Bisect.Mark(commit.Sha, info.OldTerm()); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.helpers.Bisect.PostBisectCommandRefresh()
|
||||
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
||||
},
|
||||
Key: 'g',
|
||||
},
|
||||
@@ -163,7 +163,7 @@ func (self *BisectController) showBisectCompleteMessage(candidateShas []string)
|
||||
prompt = self.c.Tr.Bisect.CompletePromptIndeterminate
|
||||
}
|
||||
|
||||
formattedCommits, err := self.git.Commit.GetCommitsOneline(candidateShas)
|
||||
formattedCommits, err := self.c.Git().Commit.GetCommitsOneline(candidateShas)
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
@@ -173,17 +173,17 @@ func (self *BisectController) showBisectCompleteMessage(candidateShas []string)
|
||||
Prompt: fmt.Sprintf(prompt, strings.TrimSpace(formattedCommits)),
|
||||
HandleConfirm: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.ResetBisect)
|
||||
if err := self.git.Bisect.Reset(); err != nil {
|
||||
if err := self.c.Git().Bisect.Reset(); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
return self.helpers.Bisect.PostBisectCommandRefresh()
|
||||
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *BisectController) afterMark(selectCurrent bool, waitToReselect bool) error {
|
||||
done, candidateShas, err := self.git.Bisect.IsDone()
|
||||
done, candidateShas, err := self.c.Git().Bisect.IsDone()
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
@@ -211,15 +211,15 @@ func (self *BisectController) afterBisectMarkRefresh(selectCurrent bool, waitToR
|
||||
} else {
|
||||
selectFn()
|
||||
|
||||
return self.helpers.Bisect.PostBisectCommandRefresh()
|
||||
return self.c.Helpers().Bisect.PostBisectCommandRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BisectController) selectCurrentBisectCommit() {
|
||||
info := self.git.Bisect.GetInfo()
|
||||
info := self.c.Git().Bisect.GetInfo()
|
||||
if info.GetCurrentSha() != "" {
|
||||
// find index of commit with that sha, move cursor to that.
|
||||
for i, commit := range self.model.Commits {
|
||||
for i, commit := range self.c.Model().Commits {
|
||||
if commit.Sha == info.GetCurrentSha() {
|
||||
self.context().SetSelectedLineIdx(i)
|
||||
_ = self.context().HandleFocus(types.OnFocusOpts{})
|
||||
@@ -245,5 +245,5 @@ func (self *BisectController) Context() types.Context {
|
||||
}
|
||||
|
||||
func (self *BisectController) context() *context.LocalCommitsContext {
|
||||
return self.contexts.LocalCommits
|
||||
return self.c.Contexts().LocalCommits
|
||||
}
|
||||
|
||||
@@ -14,17 +14,17 @@ import (
|
||||
|
||||
type BranchesController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &BranchesController{}
|
||||
|
||||
func NewBranchesController(
|
||||
common *controllerCommon,
|
||||
common *ControllerCommon,
|
||||
) *BranchesController {
|
||||
return &BranchesController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: common,
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,30 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BranchesController) GetOnRenderToMain() func() error {
|
||||
return func() error {
|
||||
return self.c.Helpers().Diff.WithDiffModeCheck(func() error {
|
||||
var task types.UpdateTask
|
||||
branch := self.context().GetSelected()
|
||||
if branch == nil {
|
||||
task = types.NewRenderStringTask(self.c.Tr.NoBranchesThisRepo)
|
||||
} else {
|
||||
cmdObj := self.c.Git().Branch.GetGraphCmdObj(branch.FullRefName())
|
||||
|
||||
task = types.NewRunPtyTask(cmdObj.GetCmd())
|
||||
}
|
||||
|
||||
return self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||
Pair: self.c.MainViewPairs().Normal,
|
||||
Main: &types.ViewUpdateOpts{
|
||||
Title: self.c.Tr.LogTitle,
|
||||
Task: task,
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (self *BranchesController) setUpstream(selectedBranch *models.Branch) error {
|
||||
return self.c.Menu(types.CreateMenuOptions{
|
||||
Title: self.c.Tr.Actions.SetUnsetUpstream,
|
||||
@@ -118,7 +142,7 @@ func (self *BranchesController) setUpstream(selectedBranch *models.Branch) error
|
||||
{
|
||||
LabelColumns: []string{self.c.Tr.LcUnsetUpstream},
|
||||
OnPress: func() error {
|
||||
if err := self.git.Branch.UnsetUpstream(selectedBranch.Name); err != nil {
|
||||
if err := self.c.Git().Branch.UnsetUpstream(selectedBranch.Name); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
if err := self.c.Refresh(types.RefreshOptions{
|
||||
@@ -137,13 +161,13 @@ func (self *BranchesController) setUpstream(selectedBranch *models.Branch) error
|
||||
{
|
||||
LabelColumns: []string{self.c.Tr.LcSetUpstream},
|
||||
OnPress: func() error {
|
||||
return self.helpers.Upstream.PromptForUpstreamWithoutInitialContent(selectedBranch, func(upstream string) error {
|
||||
upstreamRemote, upstreamBranch, err := self.helpers.Upstream.ParseUpstream(upstream)
|
||||
return self.c.Helpers().Upstream.PromptForUpstreamWithoutInitialContent(selectedBranch, func(upstream string) error {
|
||||
upstreamRemote, upstreamBranch, err := self.c.Helpers().Upstream.ParseUpstream(upstream)
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
if err := self.git.Branch.SetUpstream(upstreamRemote, upstreamBranch, selectedBranch.Name); err != nil {
|
||||
if err := self.c.Git().Branch.SetUpstream(upstreamRemote, upstreamBranch, selectedBranch.Name); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
if err := self.c.Refresh(types.RefreshOptions{
|
||||
@@ -169,16 +193,16 @@ func (self *BranchesController) Context() types.Context {
|
||||
}
|
||||
|
||||
func (self *BranchesController) context() *context.BranchesContext {
|
||||
return self.contexts.Branches
|
||||
return self.c.Contexts().Branches
|
||||
}
|
||||
|
||||
func (self *BranchesController) press(selectedBranch *models.Branch) error {
|
||||
if selectedBranch == self.helpers.Refs.GetCheckedOutRef() {
|
||||
if selectedBranch == self.c.Helpers().Refs.GetCheckedOutRef() {
|
||||
return self.c.ErrorMsg(self.c.Tr.AlreadyCheckedOutBranch)
|
||||
}
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CheckoutBranch)
|
||||
return self.helpers.Refs.CheckoutRef(selectedBranch.Name, types.CheckoutRefOptions{})
|
||||
return self.c.Helpers().Refs.CheckoutRef(selectedBranch.Name, types.CheckoutRefOptions{})
|
||||
}
|
||||
|
||||
func (self *BranchesController) handleCreatePullRequest(selectedBranch *models.Branch) error {
|
||||
@@ -186,7 +210,7 @@ func (self *BranchesController) handleCreatePullRequest(selectedBranch *models.B
|
||||
}
|
||||
|
||||
func (self *BranchesController) handleCreatePullRequestMenu(selectedBranch *models.Branch) error {
|
||||
checkedOutBranch := self.helpers.Refs.GetCheckedOutRef()
|
||||
checkedOutBranch := self.c.Helpers().Refs.GetCheckedOutRef()
|
||||
|
||||
return self.createPullRequestMenu(selectedBranch, checkedOutBranch)
|
||||
}
|
||||
@@ -194,18 +218,18 @@ func (self *BranchesController) handleCreatePullRequestMenu(selectedBranch *mode
|
||||
func (self *BranchesController) copyPullRequestURL() error {
|
||||
branch := self.context().GetSelected()
|
||||
|
||||
branchExistsOnRemote := self.git.Remote.CheckRemoteBranchExists(branch.Name)
|
||||
branchExistsOnRemote := self.c.Git().Remote.CheckRemoteBranchExists(branch.Name)
|
||||
|
||||
if !branchExistsOnRemote {
|
||||
return self.c.Error(errors.New(self.c.Tr.NoBranchOnRemote))
|
||||
}
|
||||
|
||||
url, err := self.helpers.Host.GetPullRequestURL(branch.Name, "")
|
||||
url, err := self.c.Helpers().Host.GetPullRequestURL(branch.Name, "")
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
self.c.LogAction(self.c.Tr.Actions.CopyPullRequestURL)
|
||||
if err := self.os.CopyToClipboard(url); err != nil {
|
||||
if err := self.c.OS().CopyToClipboard(url); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -224,7 +248,7 @@ func (self *BranchesController) forceCheckout() error {
|
||||
Prompt: message,
|
||||
HandleConfirm: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.ForceCheckoutBranch)
|
||||
if err := self.git.Branch.Checkout(branch.Name, git_commands.CheckoutOptions{Force: true}); err != nil {
|
||||
if err := self.c.Git().Branch.Checkout(branch.Name, git_commands.CheckoutOptions{Force: true}); err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
@@ -235,10 +259,10 @@ func (self *BranchesController) forceCheckout() error {
|
||||
func (self *BranchesController) checkoutByName() error {
|
||||
return self.c.Prompt(types.PromptOpts{
|
||||
Title: self.c.Tr.BranchName + ":",
|
||||
FindSuggestionsFunc: self.helpers.Suggestions.GetRefsSuggestionsFunc(),
|
||||
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetRefsSuggestionsFunc(),
|
||||
HandleConfirm: func(response string) error {
|
||||
self.c.LogAction("Checkout branch")
|
||||
return self.helpers.Refs.CheckoutRef(response, types.CheckoutRefOptions{
|
||||
return self.c.Helpers().Refs.CheckoutRef(response, types.CheckoutRefOptions{
|
||||
OnRefNotFound: func(ref string) error {
|
||||
return self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.BranchNotFoundTitle,
|
||||
@@ -260,7 +284,7 @@ func (self *BranchesController) createNewBranchWithName(newBranchName string) er
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := self.git.Branch.New(newBranchName, branch.FullRefName()); err != nil {
|
||||
if err := self.c.Git().Branch.New(newBranchName, branch.FullRefName()); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -269,7 +293,7 @@ func (self *BranchesController) createNewBranchWithName(newBranchName string) er
|
||||
}
|
||||
|
||||
func (self *BranchesController) delete(branch *models.Branch) error {
|
||||
checkedOutBranch := self.helpers.Refs.GetCheckedOutRef()
|
||||
checkedOutBranch := self.c.Helpers().Refs.GetCheckedOutRef()
|
||||
if checkedOutBranch.Name == branch.Name {
|
||||
return self.c.ErrorMsg(self.c.Tr.CantDeleteCheckOutBranch)
|
||||
}
|
||||
@@ -296,7 +320,7 @@ func (self *BranchesController) deleteWithForce(selectedBranch *models.Branch, f
|
||||
Prompt: message,
|
||||
HandleConfirm: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.DeleteBranch)
|
||||
if err := self.git.Branch.Delete(selectedBranch.Name, force); err != nil {
|
||||
if err := self.c.Git().Branch.Delete(selectedBranch.Name, force); err != nil {
|
||||
errMessage := err.Error()
|
||||
if !force && strings.Contains(errMessage, "git branch -D ") {
|
||||
return self.deleteWithForce(selectedBranch, true)
|
||||
@@ -310,12 +334,12 @@ func (self *BranchesController) deleteWithForce(selectedBranch *models.Branch, f
|
||||
|
||||
func (self *BranchesController) merge() error {
|
||||
selectedBranchName := self.context().GetSelected().Name
|
||||
return self.helpers.MergeAndRebase.MergeRefIntoCheckedOutBranch(selectedBranchName)
|
||||
return self.c.Helpers().MergeAndRebase.MergeRefIntoCheckedOutBranch(selectedBranchName)
|
||||
}
|
||||
|
||||
func (self *BranchesController) rebase() error {
|
||||
selectedBranchName := self.context().GetSelected().Name
|
||||
return self.helpers.MergeAndRebase.RebaseOntoRef(selectedBranchName)
|
||||
return self.c.Helpers().MergeAndRebase.RebaseOntoRef(selectedBranchName)
|
||||
}
|
||||
|
||||
func (self *BranchesController) fastForward(branch *models.Branch) error {
|
||||
@@ -340,10 +364,10 @@ func (self *BranchesController) fastForward(branch *models.Branch) error {
|
||||
)
|
||||
|
||||
return self.c.WithLoaderPanel(message, func() error {
|
||||
if branch == self.helpers.Refs.GetCheckedOutRef() {
|
||||
if branch == self.c.Helpers().Refs.GetCheckedOutRef() {
|
||||
self.c.LogAction(action)
|
||||
|
||||
err := self.git.Sync.Pull(
|
||||
err := self.c.Git().Sync.Pull(
|
||||
git_commands.PullOptions{
|
||||
RemoteName: branch.UpstreamRemote,
|
||||
BranchName: branch.UpstreamBranch,
|
||||
@@ -357,7 +381,7 @@ func (self *BranchesController) fastForward(branch *models.Branch) error {
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
} else {
|
||||
self.c.LogAction(action)
|
||||
err := self.git.Sync.FastForward(branch.Name, branch.UpstreamRemote, branch.UpstreamBranch)
|
||||
err := self.c.Git().Sync.FastForward(branch.Name, branch.UpstreamRemote, branch.UpstreamBranch)
|
||||
if err != nil {
|
||||
_ = self.c.Error(err)
|
||||
}
|
||||
@@ -369,11 +393,11 @@ func (self *BranchesController) fastForward(branch *models.Branch) error {
|
||||
}
|
||||
|
||||
func (self *BranchesController) createTag(branch *models.Branch) error {
|
||||
return self.helpers.Tags.CreateTagMenu(branch.FullRefName(), func() {})
|
||||
return self.c.Helpers().Tags.CreateTagMenu(branch.FullRefName(), func() {})
|
||||
}
|
||||
|
||||
func (self *BranchesController) createResetMenu(selectedBranch *models.Branch) error {
|
||||
return self.helpers.Refs.CreateGitResetMenu(selectedBranch.Name)
|
||||
return self.c.Helpers().Refs.CreateGitResetMenu(selectedBranch.Name)
|
||||
}
|
||||
|
||||
func (self *BranchesController) rename(branch *models.Branch) error {
|
||||
@@ -383,7 +407,7 @@ func (self *BranchesController) rename(branch *models.Branch) error {
|
||||
InitialContent: branch.Name,
|
||||
HandleConfirm: func(newBranchName string) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.RenameBranch)
|
||||
if err := self.git.Branch.Rename(branch.Name, newBranchName); err != nil {
|
||||
if err := self.c.Git().Branch.Rename(branch.Name, newBranchName); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -391,7 +415,7 @@ func (self *BranchesController) rename(branch *models.Branch) error {
|
||||
_ = self.c.Refresh(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.BRANCHES}})
|
||||
|
||||
// now that we've got our stuff again we need to find that branch and reselect it.
|
||||
for i, newBranch := range self.model.Branches {
|
||||
for i, newBranch := range self.c.Model().Branches {
|
||||
if newBranch.Name == newBranchName {
|
||||
self.context().SetSelectedLineIdx(i)
|
||||
if err := self.context().HandleRender(); err != nil {
|
||||
@@ -420,7 +444,7 @@ func (self *BranchesController) rename(branch *models.Branch) error {
|
||||
}
|
||||
|
||||
func (self *BranchesController) newBranch(selectedBranch *models.Branch) error {
|
||||
return self.helpers.Refs.NewBranch(selectedBranch.FullRefName(), selectedBranch.RefName(), "")
|
||||
return self.c.Helpers().Refs.NewBranch(selectedBranch.FullRefName(), selectedBranch.RefName(), "")
|
||||
}
|
||||
|
||||
func (self *BranchesController) createPullRequestMenu(selectedBranch *models.Branch, checkedOutBranch *models.Branch) error {
|
||||
@@ -443,7 +467,7 @@ func (self *BranchesController) createPullRequestMenu(selectedBranch *models.Bra
|
||||
OnPress: func() error {
|
||||
return self.c.Prompt(types.PromptOpts{
|
||||
Title: branch.Name + " →",
|
||||
FindSuggestionsFunc: self.helpers.Suggestions.GetBranchNameSuggestionsFunc(),
|
||||
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetBranchNameSuggestionsFunc(),
|
||||
HandleConfirm: func(targetBranchName string) error {
|
||||
return self.createPullRequest(branch.Name, targetBranchName)
|
||||
},
|
||||
@@ -471,14 +495,14 @@ func (self *BranchesController) createPullRequestMenu(selectedBranch *models.Bra
|
||||
}
|
||||
|
||||
func (self *BranchesController) createPullRequest(from string, to string) error {
|
||||
url, err := self.helpers.Host.GetPullRequestURL(from, to)
|
||||
url, err := self.c.Helpers().Host.GetPullRequestURL(from, to)
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.OpenPullRequest)
|
||||
|
||||
if err := self.os.OpenLink(url); err != nil {
|
||||
if err := self.c.OS().OpenLink(url); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type CommandLogController struct {
|
||||
baseController
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &CommandLogController{}
|
||||
|
||||
func NewCommandLogController(
|
||||
common *ControllerCommon,
|
||||
) *CommandLogController {
|
||||
return &CommandLogController{
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CommandLogController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
bindings := []*types.Binding{}
|
||||
|
||||
return bindings
|
||||
}
|
||||
|
||||
func (self *CommandLogController) GetOnFocusLost() func(types.OnFocusLostOpts) error {
|
||||
return func(types.OnFocusLostOpts) error {
|
||||
self.c.Views().Extras.Autoscroll = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CommandLogController) Context() types.Context {
|
||||
return self.context()
|
||||
}
|
||||
|
||||
func (self *CommandLogController) context() types.Context {
|
||||
return self.c.Contexts().CommandLog
|
||||
}
|
||||
@@ -6,17 +6,17 @@ import (
|
||||
|
||||
type CommitDescriptionController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &CommitMessageController{}
|
||||
|
||||
func NewCommitDescriptionController(
|
||||
common *controllerCommon,
|
||||
common *ControllerCommon,
|
||||
) *CommitDescriptionController {
|
||||
return &CommitDescriptionController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: common,
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,17 +44,17 @@ func (self *CommitDescriptionController) Context() types.Context {
|
||||
}
|
||||
|
||||
func (self *CommitDescriptionController) context() types.Context {
|
||||
return self.contexts.CommitMessage
|
||||
return self.c.Contexts().CommitMessage
|
||||
}
|
||||
|
||||
func (self *CommitDescriptionController) switchToCommitMessage() error {
|
||||
return self.c.PushContext(self.contexts.CommitMessage)
|
||||
return self.c.PushContext(self.c.Contexts().CommitMessage)
|
||||
}
|
||||
|
||||
func (self *CommitDescriptionController) close() error {
|
||||
return self.helpers.Commits.CloseCommitMessagePanel()
|
||||
return self.c.Helpers().Commits.CloseCommitMessagePanel()
|
||||
}
|
||||
|
||||
func (self *CommitDescriptionController) confirm() error {
|
||||
return self.helpers.Commits.HandleCommitConfirm()
|
||||
return self.c.Helpers().Commits.HandleCommitConfirm()
|
||||
}
|
||||
|
||||
@@ -8,29 +8,33 @@ import (
|
||||
|
||||
type CommitMessageController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &CommitMessageController{}
|
||||
|
||||
func NewCommitMessageController(
|
||||
common *controllerCommon,
|
||||
common *ControllerCommon,
|
||||
) *CommitMessageController {
|
||||
return &CommitMessageController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: common,
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: merge that commit panel PR because we're not currently showing how to add a newline as it's
|
||||
// handled by the editor func rather than by the controller here.
|
||||
func (self *CommitMessageController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
bindings := []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.SubmitEditorText),
|
||||
Handler: self.confirm,
|
||||
Key: opts.GetKey(opts.Config.Universal.SubmitEditorText),
|
||||
Handler: self.confirm,
|
||||
Description: self.c.Tr.LcConfirm,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||
Handler: self.close,
|
||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||
Handler: self.close,
|
||||
Description: self.c.Tr.LcClose,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevItem),
|
||||
@@ -49,12 +53,19 @@ func (self *CommitMessageController) GetKeybindings(opts types.KeybindingsOpts)
|
||||
return bindings
|
||||
}
|
||||
|
||||
func (self *CommitMessageController) GetOnFocusLost() func(types.OnFocusLostOpts) error {
|
||||
return func(types.OnFocusLostOpts) error {
|
||||
self.context().RenderCommitLength()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CommitMessageController) Context() types.Context {
|
||||
return self.context()
|
||||
}
|
||||
|
||||
func (self *CommitMessageController) context() *context.CommitMessageContext {
|
||||
return self.contexts.CommitMessage
|
||||
return self.c.Contexts().CommitMessage
|
||||
}
|
||||
|
||||
func (self *CommitMessageController) handlePreviousCommit() error {
|
||||
@@ -69,7 +80,7 @@ func (self *CommitMessageController) handleNextCommit() error {
|
||||
}
|
||||
|
||||
func (self *CommitMessageController) switchToCommitDescription() error {
|
||||
if err := self.c.PushContext(self.contexts.CommitDescription); err != nil {
|
||||
if err := self.c.PushContext(self.c.Contexts().CommitDescription); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -80,10 +91,10 @@ func (self *CommitMessageController) handleCommitIndexChange(value int) error {
|
||||
newIndex := currentIndex + value
|
||||
if newIndex == context.NoCommitIndex {
|
||||
self.context().SetSelectedIndex(newIndex)
|
||||
self.helpers.Commits.SetMessageAndDescriptionInView(self.context().GetHistoryMessage())
|
||||
self.c.Helpers().Commits.SetMessageAndDescriptionInView(self.context().GetHistoryMessage())
|
||||
return nil
|
||||
} else if currentIndex == context.NoCommitIndex {
|
||||
self.context().SetHistoryMessage(self.helpers.Commits.JoinCommitMessageAndDescription())
|
||||
self.context().SetHistoryMessage(self.c.Helpers().Commits.JoinCommitMessageAndDescription())
|
||||
}
|
||||
|
||||
validCommit, err := self.setCommitMessageAtIndex(newIndex)
|
||||
@@ -95,21 +106,21 @@ func (self *CommitMessageController) handleCommitIndexChange(value int) error {
|
||||
|
||||
// returns true if the given index is for a valid commit
|
||||
func (self *CommitMessageController) setCommitMessageAtIndex(index int) (bool, error) {
|
||||
commitMessage, err := self.git.Commit.GetCommitMessageFromHistory(index)
|
||||
commitMessage, err := self.c.Git().Commit.GetCommitMessageFromHistory(index)
|
||||
if err != nil {
|
||||
if err == git_commands.ErrInvalidCommitIndex {
|
||||
return false, nil
|
||||
}
|
||||
return false, self.c.ErrorMsg(self.c.Tr.CommitWithoutMessageErr)
|
||||
}
|
||||
self.helpers.Commits.UpdateCommitPanelView(commitMessage)
|
||||
self.c.Helpers().Commits.UpdateCommitPanelView(commitMessage)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *CommitMessageController) confirm() error {
|
||||
return self.helpers.Commits.HandleCommitConfirm()
|
||||
return self.c.Helpers().Commits.HandleCommitConfirm()
|
||||
}
|
||||
|
||||
func (self *CommitMessageController) close() error {
|
||||
return self.helpers.Commits.CloseCommitMessagePanel()
|
||||
return self.c.Helpers().Commits.CloseCommitMessagePanel()
|
||||
}
|
||||
|
||||
@@ -11,17 +11,17 @@ import (
|
||||
|
||||
type CommitFilesController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &CommitFilesController{}
|
||||
|
||||
func NewCommitFilesController(
|
||||
common *controllerCommon,
|
||||
common *ControllerCommon,
|
||||
) *CommitFilesController {
|
||||
return &CommitFilesController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: common,
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,40 @@ func (self *CommitFilesController) Context() types.Context {
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) context() *context.CommitFilesContext {
|
||||
return self.contexts.CommitFiles
|
||||
return self.c.Contexts().CommitFiles
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) GetOnRenderToMain() func() error {
|
||||
return func() error {
|
||||
node := self.context().GetSelected()
|
||||
if node == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
ref := self.context().GetRef()
|
||||
to := ref.RefName()
|
||||
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
||||
|
||||
cmdObj := self.c.Git().WorkingTree.ShowFileDiffCmdObj(
|
||||
from, to, reverse, node.GetPath(), false, self.c.State().GetIgnoreWhitespaceInDiffView(),
|
||||
)
|
||||
task := types.NewRunPtyTask(cmdObj.GetCmd())
|
||||
|
||||
pair := self.c.MainViewPairs().Normal
|
||||
if node.File != nil {
|
||||
pair = self.c.MainViewPairs().PatchBuilding
|
||||
}
|
||||
|
||||
return self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||
Pair: pair,
|
||||
Main: &types.ViewUpdateOpts{
|
||||
Title: self.c.Tr.Patch,
|
||||
SubTitle: self.c.Helpers().Diff.IgnoringWhitespaceSubTitle(),
|
||||
Task: task,
|
||||
},
|
||||
Secondary: secondaryPatchPanelUpdateOpts(self.c),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) onClickMain(opts gocui.ViewMouseBindingOpts) error {
|
||||
@@ -112,7 +145,7 @@ func (self *CommitFilesController) onClickMain(opts gocui.ViewMouseBindingOpts)
|
||||
|
||||
func (self *CommitFilesController) checkout(node *filetree.CommitFileNode) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.CheckoutFile)
|
||||
if err := self.git.WorkingTree.CheckoutFile(self.context().GetRef().RefName(), node.GetPath()); err != nil {
|
||||
if err := self.c.Git().WorkingTree.CheckoutFile(self.context().GetRef().RefName(), node.GetPath()); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -120,7 +153,7 @@ func (self *CommitFilesController) checkout(node *filetree.CommitFileNode) error
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) discard(node *filetree.CommitFileNode) error {
|
||||
if ok, err := self.helpers.PatchBuilding.ValidateNormalWorkingTreeState(); !ok {
|
||||
if ok, err := self.c.Helpers().PatchBuilding.ValidateNormalWorkingTreeState(); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -130,8 +163,8 @@ func (self *CommitFilesController) discard(node *filetree.CommitFileNode) error
|
||||
HandleConfirm: func() error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.DiscardOldFileChange)
|
||||
if err := self.git.Rebase.DiscardOldFileChanges(self.model.Commits, self.contexts.LocalCommits.GetSelectedLineIdx(), node.GetPath()); err != nil {
|
||||
if err := self.helpers.MergeAndRebase.CheckMergeOrRebase(err); err != nil {
|
||||
if err := self.c.Git().Rebase.DiscardOldFileChanges(self.c.Model().Commits, self.c.Contexts().LocalCommits.GetSelectedLineIdx(), node.GetPath()); err != nil {
|
||||
if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -143,7 +176,7 @@ func (self *CommitFilesController) discard(node *filetree.CommitFileNode) error
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) open(node *filetree.CommitFileNode) error {
|
||||
return self.helpers.Files.OpenFile(node.GetPath())
|
||||
return self.c.Helpers().Files.OpenFile(node.GetPath())
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) edit(node *filetree.CommitFileNode) error {
|
||||
@@ -151,13 +184,13 @@ func (self *CommitFilesController) edit(node *filetree.CommitFileNode) error {
|
||||
return self.c.ErrorMsg(self.c.Tr.ErrCannotEditDirectory)
|
||||
}
|
||||
|
||||
return self.helpers.Files.EditFile(node.GetPath())
|
||||
return self.c.Helpers().Files.EditFile(node.GetPath())
|
||||
}
|
||||
|
||||
func (self *CommitFilesController) toggleForPatch(node *filetree.CommitFileNode) error {
|
||||
toggle := func() error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.LcUpdatingPatch, func() error {
|
||||
if !self.git.Patch.PatchBuilder.Active() {
|
||||
if !self.c.Git().Patch.PatchBuilder.Active() {
|
||||
if err := self.startPatchBuilder(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -166,34 +199,34 @@ func (self *CommitFilesController) toggleForPatch(node *filetree.CommitFileNode)
|
||||
// if there is any file that hasn't been fully added we'll fully add everything,
|
||||
// otherwise we'll remove everything
|
||||
adding := node.SomeFile(func(file *models.CommitFile) bool {
|
||||
return self.git.Patch.PatchBuilder.GetFileStatus(file.Name, self.context().GetRef().RefName()) != patch.WHOLE
|
||||
return self.c.Git().Patch.PatchBuilder.GetFileStatus(file.Name, self.context().GetRef().RefName()) != patch.WHOLE
|
||||
})
|
||||
|
||||
err := node.ForEachFile(func(file *models.CommitFile) error {
|
||||
if adding {
|
||||
return self.git.Patch.PatchBuilder.AddFileWhole(file.Name)
|
||||
return self.c.Git().Patch.PatchBuilder.AddFileWhole(file.Name)
|
||||
} else {
|
||||
return self.git.Patch.PatchBuilder.RemoveFile(file.Name)
|
||||
return self.c.Git().Patch.PatchBuilder.RemoveFile(file.Name)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
if self.git.Patch.PatchBuilder.IsEmpty() {
|
||||
self.git.Patch.PatchBuilder.Reset()
|
||||
if self.c.Git().Patch.PatchBuilder.IsEmpty() {
|
||||
self.c.Git().Patch.PatchBuilder.Reset()
|
||||
}
|
||||
|
||||
return self.c.PostRefreshUpdate(self.context())
|
||||
})
|
||||
}
|
||||
|
||||
if self.git.Patch.PatchBuilder.Active() && self.git.Patch.PatchBuilder.To != self.context().GetRef().RefName() {
|
||||
if self.c.Git().Patch.PatchBuilder.Active() && self.c.Git().Patch.PatchBuilder.To != self.context().GetRef().RefName() {
|
||||
return self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.DiscardPatch,
|
||||
Prompt: self.c.Tr.DiscardPatchConfirm,
|
||||
HandleConfirm: func() error {
|
||||
self.git.Patch.PatchBuilder.Reset()
|
||||
self.c.Git().Patch.PatchBuilder.Reset()
|
||||
return toggle()
|
||||
},
|
||||
})
|
||||
@@ -213,9 +246,9 @@ func (self *CommitFilesController) startPatchBuilder() error {
|
||||
canRebase := commitFilesContext.GetCanRebase()
|
||||
ref := commitFilesContext.GetRef()
|
||||
to := ref.RefName()
|
||||
from, reverse := self.modes.Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
||||
from, reverse := self.c.Modes().Diffing.GetFromAndReverseArgsForDiff(ref.ParentRefName())
|
||||
|
||||
self.git.Patch.PatchBuilder.Start(from, to, reverse, canRebase)
|
||||
self.c.Git().Patch.PatchBuilder.Start(from, to, reverse, canRebase)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -229,21 +262,21 @@ func (self *CommitFilesController) enterCommitFile(node *filetree.CommitFileNode
|
||||
}
|
||||
|
||||
enterTheFile := func() error {
|
||||
if !self.git.Patch.PatchBuilder.Active() {
|
||||
if !self.c.Git().Patch.PatchBuilder.Active() {
|
||||
if err := self.startPatchBuilder(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return self.c.PushContext(self.contexts.CustomPatchBuilder, opts)
|
||||
return self.c.PushContext(self.c.Contexts().CustomPatchBuilder, opts)
|
||||
}
|
||||
|
||||
if self.git.Patch.PatchBuilder.Active() && self.git.Patch.PatchBuilder.To != self.context().GetRef().RefName() {
|
||||
if self.c.Git().Patch.PatchBuilder.Active() && self.c.Git().Patch.PatchBuilder.To != self.context().GetRef().RefName() {
|
||||
return self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.DiscardPatch,
|
||||
Prompt: self.c.Tr.DiscardPatchConfirm,
|
||||
HandleConfirm: func() error {
|
||||
self.git.Patch.PatchBuilder.Reset()
|
||||
self.c.Git().Patch.PatchBuilder.Reset()
|
||||
return enterTheFile()
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,42 +1,24 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type controllerCommon struct {
|
||||
c *types.HelperCommon
|
||||
os *oscommands.OSCommand
|
||||
git *commands.GitCommand
|
||||
helpers *helpers.Helpers
|
||||
model *types.Model
|
||||
contexts *context.ContextTree
|
||||
modes *types.Modes
|
||||
mutexes *types.Mutexes
|
||||
type ControllerCommon struct {
|
||||
*helpers.HelperCommon
|
||||
IGetHelpers
|
||||
}
|
||||
|
||||
type IGetHelpers interface {
|
||||
Helpers() *helpers.Helpers
|
||||
}
|
||||
|
||||
func NewControllerCommon(
|
||||
c *types.HelperCommon,
|
||||
os *oscommands.OSCommand,
|
||||
git *commands.GitCommand,
|
||||
helpers *helpers.Helpers,
|
||||
model *types.Model,
|
||||
contexts *context.ContextTree,
|
||||
modes *types.Modes,
|
||||
mutexes *types.Mutexes,
|
||||
) *controllerCommon {
|
||||
return &controllerCommon{
|
||||
c: c,
|
||||
os: os,
|
||||
git: git,
|
||||
helpers: helpers,
|
||||
model: model,
|
||||
contexts: contexts,
|
||||
modes: modes,
|
||||
mutexes: mutexes,
|
||||
c *helpers.HelperCommon,
|
||||
IGetHelpers IGetHelpers,
|
||||
) *ControllerCommon {
|
||||
return &ControllerCommon{
|
||||
HelperCommon: c,
|
||||
IGetHelpers: IGetHelpers,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type ConfirmationController struct {
|
||||
baseController
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &ConfirmationController{}
|
||||
|
||||
func NewConfirmationController(
|
||||
common *ControllerCommon,
|
||||
) *ConfirmationController {
|
||||
return &ConfirmationController{
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ConfirmationController) GetKeybindings(opts types.KeybindingsOpts) []*types.Binding {
|
||||
bindings := []*types.Binding{
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Confirm),
|
||||
Handler: func() error { return self.context().State.OnConfirm() },
|
||||
Description: self.c.Tr.LcConfirm,
|
||||
Display: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||
Handler: func() error { return self.context().State.OnClose() },
|
||||
Description: self.c.Tr.LcCloseCancel,
|
||||
Display: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.TogglePanel),
|
||||
Handler: func() error {
|
||||
if len(self.c.Contexts().Suggestions.State.Suggestions) > 0 {
|
||||
return self.c.ReplaceContext(self.c.Contexts().Suggestions)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return bindings
|
||||
}
|
||||
|
||||
func (self *ConfirmationController) GetOnFocusLost() func(types.OnFocusLostOpts) error {
|
||||
return func(types.OnFocusLostOpts) error {
|
||||
self.c.Helpers().Confirmation.DeactivateConfirmationPrompt()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (self *ConfirmationController) Context() types.Context {
|
||||
return self.context()
|
||||
}
|
||||
|
||||
func (self *ConfirmationController) context() *context.ConfirmationContext {
|
||||
return self.c.Contexts().Confirmation
|
||||
}
|
||||
@@ -24,17 +24,17 @@ var CONTEXT_KEYS_SHOWING_DIFFS = []types.ContextKey{
|
||||
|
||||
type ContextLinesController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &ContextLinesController{}
|
||||
|
||||
func NewContextLinesController(
|
||||
common *controllerCommon,
|
||||
common *ControllerCommon,
|
||||
) *ContextLinesController {
|
||||
return &ContextLinesController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: common,
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ func (self *ContextLinesController) applyChange() error {
|
||||
}
|
||||
|
||||
func (self *ContextLinesController) checkCanChangeContext() error {
|
||||
if self.git.Patch.PatchBuilder.Active() {
|
||||
if self.c.Git().Patch.PatchBuilder.Active() {
|
||||
return errors.New(self.c.Tr.CantChangeContextSizeError)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type CustomCommandAction struct {
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
func (self *CustomCommandAction) Call() error {
|
||||
return self.c.Prompt(types.PromptOpts{
|
||||
Title: self.c.Tr.CustomCommand,
|
||||
FindSuggestionsFunc: self.GetCustomCommandsHistorySuggestionsFunc(),
|
||||
HandleConfirm: func(command string) error {
|
||||
if self.shouldSaveCommand(command) {
|
||||
self.c.GetAppState().CustomCommandsHistory = utils.Limit(
|
||||
lo.Uniq(append(self.c.GetAppState().CustomCommandsHistory, command)),
|
||||
1000,
|
||||
)
|
||||
}
|
||||
|
||||
err := self.c.SaveAppState()
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CustomCommand)
|
||||
return self.c.RunSubprocessAndRefresh(
|
||||
self.c.OS().Cmd.NewShell(command),
|
||||
)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (self *CustomCommandAction) GetCustomCommandsHistorySuggestionsFunc() func(string) []*types.Suggestion {
|
||||
// reversing so that we display the latest command first
|
||||
history := slices.Reverse(self.c.GetAppState().CustomCommandsHistory)
|
||||
|
||||
return helpers.FuzzySearchFunc(history)
|
||||
}
|
||||
|
||||
// this mimics the shell functionality `ignorespace`
|
||||
// which doesn't save a command to history if it starts with a space
|
||||
func (self *CustomCommandAction) shouldSaveCommand(command string) bool {
|
||||
return !strings.HasPrefix(command, " ")
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type CustomPatchOptionsMenuAction struct {
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) Call() error {
|
||||
if !self.c.Git().Patch.PatchBuilder.Active() {
|
||||
return self.c.ErrorMsg(self.c.Tr.NoPatchError)
|
||||
}
|
||||
|
||||
menuItems := []*types.MenuItem{
|
||||
{
|
||||
Label: "reset patch",
|
||||
OnPress: self.c.Helpers().PatchBuilding.Reset,
|
||||
Key: 'c',
|
||||
},
|
||||
{
|
||||
Label: "apply patch",
|
||||
OnPress: func() error { return self.handleApplyPatch(false) },
|
||||
Key: 'a',
|
||||
},
|
||||
{
|
||||
Label: "apply patch in reverse",
|
||||
OnPress: func() error { return self.handleApplyPatch(true) },
|
||||
Key: 'r',
|
||||
},
|
||||
}
|
||||
|
||||
if self.c.Git().Patch.PatchBuilder.CanRebase && self.c.Git().Status.WorkingTreeState() == enums.REBASE_MODE_NONE {
|
||||
menuItems = append(menuItems, []*types.MenuItem{
|
||||
{
|
||||
Label: fmt.Sprintf("remove patch from original commit (%s)", self.c.Git().Patch.PatchBuilder.To),
|
||||
OnPress: self.handleDeletePatchFromCommit,
|
||||
Key: 'd',
|
||||
},
|
||||
{
|
||||
Label: "move patch out into index",
|
||||
OnPress: self.handleMovePatchIntoWorkingTree,
|
||||
Key: 'i',
|
||||
},
|
||||
{
|
||||
Label: "move patch into new commit",
|
||||
OnPress: self.handlePullPatchIntoNewCommit,
|
||||
Key: 'n',
|
||||
},
|
||||
}...)
|
||||
|
||||
if self.c.CurrentContext().GetKey() == self.c.Contexts().LocalCommits.GetKey() {
|
||||
selectedCommit := self.c.Contexts().LocalCommits.GetSelected()
|
||||
if selectedCommit != nil && self.c.Git().Patch.PatchBuilder.To != selectedCommit.Sha {
|
||||
// adding this option to index 1
|
||||
menuItems = append(
|
||||
menuItems[:1],
|
||||
append(
|
||||
[]*types.MenuItem{
|
||||
{
|
||||
Label: fmt.Sprintf("move patch to selected commit (%s)", selectedCommit.Sha),
|
||||
OnPress: self.handleMovePatchToSelectedCommit,
|
||||
Key: 'm',
|
||||
},
|
||||
}, menuItems[1:]...,
|
||||
)...,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
menuItems = append(menuItems, []*types.MenuItem{
|
||||
{
|
||||
Label: "copy patch to clipboard",
|
||||
OnPress: func() error { return self.copyPatchToClipboard() },
|
||||
Key: 'y',
|
||||
},
|
||||
}...)
|
||||
|
||||
return self.c.Menu(types.CreateMenuOptions{Title: self.c.Tr.PatchOptionsTitle, Items: menuItems})
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) getPatchCommitIndex() int {
|
||||
for index, commit := range self.c.Model().Commits {
|
||||
if commit.Sha == self.c.Git().Patch.PatchBuilder.To {
|
||||
return index
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) validateNormalWorkingTreeState() (bool, error) {
|
||||
if self.c.Git().Status.WorkingTreeState() != enums.REBASE_MODE_NONE {
|
||||
return false, self.c.ErrorMsg(self.c.Tr.CantPatchWhileRebasingError)
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) returnFocusFromPatchExplorerIfNecessary() error {
|
||||
if self.c.CurrentContext().GetKey() == self.c.Contexts().CustomPatchBuilder.GetKey() {
|
||||
return self.c.Helpers().PatchBuilding.Escape()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) handleDeletePatchFromCommit() error {
|
||||
if ok, err := self.validateNormalWorkingTreeState(); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
|
||||
commitIndex := self.getPatchCommitIndex()
|
||||
self.c.LogAction(self.c.Tr.Actions.RemovePatchFromCommit)
|
||||
err := self.c.Git().Patch.DeletePatchesFromCommit(self.c.Model().Commits, commitIndex)
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||
})
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) handleMovePatchToSelectedCommit() error {
|
||||
if ok, err := self.validateNormalWorkingTreeState(); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
|
||||
commitIndex := self.getPatchCommitIndex()
|
||||
self.c.LogAction(self.c.Tr.Actions.MovePatchToSelectedCommit)
|
||||
err := self.c.Git().Patch.MovePatchToSelectedCommit(self.c.Model().Commits, commitIndex, self.c.Contexts().LocalCommits.GetSelectedLineIdx())
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||
})
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) handleMovePatchIntoWorkingTree() error {
|
||||
if ok, err := self.validateNormalWorkingTreeState(); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pull := func(stash bool) error {
|
||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
|
||||
commitIndex := self.getPatchCommitIndex()
|
||||
self.c.LogAction(self.c.Tr.Actions.MovePatchIntoIndex)
|
||||
err := self.c.Git().Patch.MovePatchIntoIndex(self.c.Model().Commits, commitIndex, stash)
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||
})
|
||||
}
|
||||
|
||||
if self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
|
||||
return self.c.Confirm(types.ConfirmOpts{
|
||||
Title: self.c.Tr.MustStashTitle,
|
||||
Prompt: self.c.Tr.MustStashWarning,
|
||||
HandleConfirm: func() error {
|
||||
return pull(true)
|
||||
},
|
||||
})
|
||||
} else {
|
||||
return pull(false)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) handlePullPatchIntoNewCommit() error {
|
||||
if ok, err := self.validateNormalWorkingTreeState(); !ok {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func() error {
|
||||
commitIndex := self.getPatchCommitIndex()
|
||||
self.c.LogAction(self.c.Tr.Actions.MovePatchIntoNewCommit)
|
||||
err := self.c.Git().Patch.PullPatchIntoNewCommit(self.c.Model().Commits, commitIndex)
|
||||
return self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err)
|
||||
})
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) handleApplyPatch(reverse bool) error {
|
||||
if err := self.returnFocusFromPatchExplorerIfNecessary(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
action := self.c.Tr.Actions.ApplyPatch
|
||||
if reverse {
|
||||
action = "Apply patch in reverse"
|
||||
}
|
||||
self.c.LogAction(action)
|
||||
if err := self.c.Git().Patch.ApplyCustomPatch(reverse); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
}
|
||||
|
||||
func (self *CustomPatchOptionsMenuAction) copyPatchToClipboard() error {
|
||||
patch := self.c.Git().Patch.PatchBuilder.RenderAggregatedPatch(true)
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CopyPatchToClipboard)
|
||||
if err := self.c.OS().CopyToClipboard(patch); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
self.c.Toast(self.c.Tr.PatchCopiedToClipboard)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/modes/diffing"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type DiffingMenuAction struct {
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
func (self *DiffingMenuAction) Call() error {
|
||||
names := self.c.Helpers().Diff.CurrentDiffTerminals()
|
||||
|
||||
menuItems := []*types.MenuItem{}
|
||||
for _, name := range names {
|
||||
name := name
|
||||
menuItems = append(menuItems, []*types.MenuItem{
|
||||
{
|
||||
Label: fmt.Sprintf("%s %s", self.c.Tr.LcDiff, name),
|
||||
OnPress: func() error {
|
||||
self.c.Modes().Diffing.Ref = name
|
||||
// can scope this down based on current view but too lazy right now
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
},
|
||||
},
|
||||
}...)
|
||||
}
|
||||
|
||||
menuItems = append(menuItems, []*types.MenuItem{
|
||||
{
|
||||
Label: self.c.Tr.LcEnterRefToDiff,
|
||||
OnPress: func() error {
|
||||
return self.c.Prompt(types.PromptOpts{
|
||||
Title: self.c.Tr.LcEnteRefName,
|
||||
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetRefsSuggestionsFunc(),
|
||||
HandleConfirm: func(response string) error {
|
||||
self.c.Modes().Diffing.Ref = strings.TrimSpace(response)
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
}...)
|
||||
|
||||
if self.c.Modes().Diffing.Active() {
|
||||
menuItems = append(menuItems, []*types.MenuItem{
|
||||
{
|
||||
Label: self.c.Tr.LcSwapDiff,
|
||||
OnPress: func() error {
|
||||
self.c.Modes().Diffing.Reverse = !self.c.Modes().Diffing.Reverse
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
},
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.LcExitDiffMode,
|
||||
OnPress: func() error {
|
||||
self.c.Modes().Diffing = diffing.New()
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
},
|
||||
},
|
||||
}...)
|
||||
}
|
||||
|
||||
return self.c.Menu(types.CreateMenuOptions{Title: self.c.Tr.DiffingMenuTitle, Items: menuItems})
|
||||
}
|
||||
@@ -9,27 +9,21 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/context"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
type FilesController struct {
|
||||
baseController // nolint: unused
|
||||
*controllerCommon
|
||||
|
||||
enterSubmodule func(submodule *models.SubmoduleConfig) error
|
||||
setCommitMessage func(message string)
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &FilesController{}
|
||||
|
||||
func NewFilesController(
|
||||
common *controllerCommon,
|
||||
enterSubmodule func(submodule *models.SubmoduleConfig) error,
|
||||
setCommitMessage func(message string),
|
||||
common *ControllerCommon,
|
||||
) *FilesController {
|
||||
return &FilesController{
|
||||
controllerCommon: common,
|
||||
enterSubmodule: enterSubmodule,
|
||||
setCommitMessage: setCommitMessage,
|
||||
c: common,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,12 +41,12 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.CommitChanges),
|
||||
Handler: self.helpers.WorkingTree.HandleCommitPress,
|
||||
Handler: self.c.Helpers().WorkingTree.HandleCommitPress,
|
||||
Description: self.c.Tr.CommitChanges,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.CommitChangesWithoutHook),
|
||||
Handler: self.helpers.WorkingTree.HandleWIPCommitPress,
|
||||
Handler: self.c.Helpers().WorkingTree.HandleWIPCommitPress,
|
||||
Description: self.c.Tr.LcCommitChangesWithoutHook,
|
||||
},
|
||||
{
|
||||
@@ -62,7 +56,7 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.CommitChangesWithEditor),
|
||||
Handler: self.helpers.WorkingTree.HandleCommitEditorPress,
|
||||
Handler: self.c.Helpers().WorkingTree.HandleCommitEditorPress,
|
||||
Description: self.c.Tr.CommitChangesWithEditor,
|
||||
},
|
||||
{
|
||||
@@ -126,7 +120,7 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Files.OpenMergeTool),
|
||||
Handler: self.helpers.WorkingTree.OpenMergeTool,
|
||||
Handler: self.c.Helpers().WorkingTree.OpenMergeTool,
|
||||
Description: self.c.Tr.LcOpenMergeTool,
|
||||
},
|
||||
{
|
||||
@@ -172,6 +166,87 @@ func (self *FilesController) GetMouseKeybindings(opts types.KeybindingsOpts) []*
|
||||
}
|
||||
}
|
||||
|
||||
func (self *FilesController) GetOnRenderToMain() func() error {
|
||||
return func() error {
|
||||
return self.c.Helpers().Diff.WithDiffModeCheck(func() error {
|
||||
node := self.context().GetSelected()
|
||||
|
||||
if node == nil {
|
||||
return self.c.RenderToMainViews(types.RefreshMainOpts{
|
||||
Pair: self.c.MainViewPairs().Normal,
|
||||
Main: &types.ViewUpdateOpts{
|
||||
Title: self.c.Tr.DiffTitle,
|
||||
SubTitle: self.c.Helpers().Diff.IgnoringWhitespaceSubTitle(),
|
||||
Task: types.NewRenderStringTask(self.c.Tr.NoChangedFiles),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if node.File != nil && node.File.HasInlineMergeConflicts {
|
||||
hasConflicts, err := self.c.Helpers().MergeConflicts.SetMergeState(node.GetPath())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if hasConflicts {
|
||||
return self.c.Helpers().MergeConflicts.Render(false)
|
||||
}
|
||||
}
|
||||
|
||||
if self.c.UserConfig.Gui.ExperimentalMarkCommitsWhichChangedFile {
|
||||
go utils.Safe(func() {
|
||||
recentCommitsWhichChangedFile := self.c.Git().Commit.GetRecentCommitsWhichChangedFile(node.GetPath())
|
||||
self.c.OnUIThread(func() error {
|
||||
self.c.State().GetRepoState().SetRecentCommitsWhichChangedFile(recentCommitsWhichChangedFile)
|
||||
return self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
self.c.Helpers().MergeConflicts.ResetMergeState()
|
||||
|
||||
pair := self.c.MainViewPairs().Normal
|
||||
if node.File != nil {
|
||||
pair = self.c.MainViewPairs().Staging
|
||||
}
|
||||
|
||||
split := self.c.UserConfig.Gui.SplitDiff == "always" || (node.GetHasUnstagedChanges() && node.GetHasStagedChanges())
|
||||
mainShowsStaged := !split && node.GetHasStagedChanges()
|
||||
|
||||
cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, mainShowsStaged, self.c.State().GetIgnoreWhitespaceInDiffView())
|
||||
title := self.c.Tr.UnstagedChanges
|
||||
if mainShowsStaged {
|
||||
title = self.c.Tr.StagedChanges
|
||||
}
|
||||
refreshOpts := types.RefreshMainOpts{
|
||||
Pair: pair,
|
||||
Main: &types.ViewUpdateOpts{
|
||||
Task: types.NewRunPtyTask(cmdObj.GetCmd()),
|
||||
SubTitle: self.c.Helpers().Diff.IgnoringWhitespaceSubTitle(),
|
||||
Title: title,
|
||||
},
|
||||
}
|
||||
|
||||
if split {
|
||||
cmdObj := self.c.Git().WorkingTree.WorktreeFileDiffCmdObj(node, false, true, self.c.State().GetIgnoreWhitespaceInDiffView())
|
||||
|
||||
title := self.c.Tr.StagedChanges
|
||||
if mainShowsStaged {
|
||||
title = self.c.Tr.UnstagedChanges
|
||||
}
|
||||
|
||||
refreshOpts.Secondary = &types.ViewUpdateOpts{
|
||||
Title: title,
|
||||
SubTitle: self.c.Helpers().Diff.IgnoringWhitespaceSubTitle(),
|
||||
Task: types.NewRunPtyTask(cmdObj.GetCmd()),
|
||||
}
|
||||
}
|
||||
|
||||
return self.c.RenderToMainViews(refreshOpts)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (self *FilesController) GetOnClick() func() error {
|
||||
return self.checkSelectedFileNode(self.press)
|
||||
}
|
||||
@@ -227,7 +302,7 @@ func (self *FilesController) optimisticChange(node *filetree.FileNode, optimisti
|
||||
rerender := false
|
||||
err := node.ForEachFile(func(f *models.File) error {
|
||||
// can't act on the file itself: we need to update the original model file
|
||||
for _, modelFile := range self.model.Files {
|
||||
for _, modelFile := range self.c.Model().Files {
|
||||
if modelFile.Name == f.Name {
|
||||
if optimisticChangeFn(modelFile) {
|
||||
rerender = true
|
||||
@@ -242,7 +317,7 @@ func (self *FilesController) optimisticChange(node *filetree.FileNode, optimisti
|
||||
return err
|
||||
}
|
||||
if rerender {
|
||||
if err := self.c.PostRefreshUpdate(self.contexts.Files); err != nil {
|
||||
if err := self.c.PostRefreshUpdate(self.c.Contexts().Files); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -253,8 +328,8 @@ func (self *FilesController) optimisticChange(node *filetree.FileNode, optimisti
|
||||
func (self *FilesController) pressWithLock(node *filetree.FileNode) error {
|
||||
// Obtaining this lock because optimistic rendering requires us to mutate
|
||||
// the files in our model.
|
||||
self.mutexes.RefreshingFilesMutex.Lock()
|
||||
defer self.mutexes.RefreshingFilesMutex.Unlock()
|
||||
self.c.Mutexes().RefreshingFilesMutex.Lock()
|
||||
defer self.c.Mutexes().RefreshingFilesMutex.Unlock()
|
||||
|
||||
if node.IsFile() {
|
||||
file := node.File
|
||||
@@ -266,7 +341,7 @@ func (self *FilesController) pressWithLock(node *filetree.FileNode) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.git.WorkingTree.StageFile(file.Name); err != nil {
|
||||
if err := self.c.Git().WorkingTree.StageFile(file.Name); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
} else {
|
||||
@@ -276,7 +351,7 @@ func (self *FilesController) pressWithLock(node *filetree.FileNode) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||
if err := self.c.Git().WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
}
|
||||
@@ -294,7 +369,7 @@ func (self *FilesController) pressWithLock(node *filetree.FileNode) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.git.WorkingTree.StageFile(node.Path); err != nil {
|
||||
if err := self.c.Git().WorkingTree.StageFile(node.Path); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
} else {
|
||||
@@ -305,7 +380,7 @@ func (self *FilesController) pressWithLock(node *filetree.FileNode) error {
|
||||
}
|
||||
|
||||
// pretty sure it doesn't matter that we're always passing true here
|
||||
if err := self.git.WorkingTree.UnStageFile([]string{node.Path}, true); err != nil {
|
||||
if err := self.c.Git().WorkingTree.UnStageFile([]string{node.Path}, true); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
}
|
||||
@@ -346,7 +421,7 @@ func (self *FilesController) Context() types.Context {
|
||||
}
|
||||
|
||||
func (self *FilesController) context() *context.WorkingTreeContext {
|
||||
return self.contexts.Files
|
||||
return self.c.Contexts().Files
|
||||
}
|
||||
|
||||
func (self *FilesController) getSelectedFile() *models.File {
|
||||
@@ -373,10 +448,10 @@ func (self *FilesController) EnterFile(opts types.OnFocusOpts) error {
|
||||
|
||||
file := node.File
|
||||
|
||||
submoduleConfigs := self.model.Submodules
|
||||
submoduleConfigs := self.c.Model().Submodules
|
||||
if file.IsSubmodule(submoduleConfigs) {
|
||||
submoduleConfig := file.SubmoduleConfig(submoduleConfigs)
|
||||
return self.enterSubmodule(submoduleConfig)
|
||||
return self.c.Helpers().Repos.EnterSubmodule(submoduleConfig)
|
||||
}
|
||||
|
||||
if file.HasInlineMergeConflicts {
|
||||
@@ -386,7 +461,7 @@ func (self *FilesController) EnterFile(opts types.OnFocusOpts) error {
|
||||
return self.c.ErrorMsg(self.c.Tr.FileStagingRequirements)
|
||||
}
|
||||
|
||||
return self.c.PushContext(self.contexts.Staging, opts)
|
||||
return self.c.PushContext(self.c.Contexts().Staging, opts)
|
||||
}
|
||||
|
||||
func (self *FilesController) toggleStagedAll() error {
|
||||
@@ -402,8 +477,8 @@ func (self *FilesController) toggleStagedAll() error {
|
||||
}
|
||||
|
||||
func (self *FilesController) toggleStagedAllWithLock() error {
|
||||
self.mutexes.RefreshingFilesMutex.Lock()
|
||||
defer self.mutexes.RefreshingFilesMutex.Unlock()
|
||||
self.c.Mutexes().RefreshingFilesMutex.Lock()
|
||||
defer self.c.Mutexes().RefreshingFilesMutex.Unlock()
|
||||
|
||||
root := self.context().FileTreeViewModel.GetRoot()
|
||||
|
||||
@@ -420,7 +495,7 @@ func (self *FilesController) toggleStagedAllWithLock() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.git.WorkingTree.StageAll(); err != nil {
|
||||
if err := self.c.Git().WorkingTree.StageAll(); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
} else {
|
||||
@@ -430,7 +505,7 @@ func (self *FilesController) toggleStagedAllWithLock() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.git.WorkingTree.UnstageAll(); err != nil {
|
||||
if err := self.c.Git().WorkingTree.UnstageAll(); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
}
|
||||
@@ -441,7 +516,7 @@ func (self *FilesController) toggleStagedAllWithLock() error {
|
||||
func (self *FilesController) unstageFiles(node *filetree.FileNode) error {
|
||||
return node.ForEachFile(func(file *models.File) error {
|
||||
if file.HasStagedChanges {
|
||||
if err := self.git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||
if err := self.c.Git().WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -457,7 +532,7 @@ func (self *FilesController) ignoreOrExcludeTracked(node *filetree.FileNode, trA
|
||||
return err
|
||||
}
|
||||
|
||||
if err := self.git.WorkingTree.RemoveTrackedFiles(node.GetPath()); err != nil {
|
||||
if err := self.c.Git().WorkingTree.RemoveTrackedFiles(node.GetPath()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -495,7 +570,7 @@ func (self *FilesController) ignore(node *filetree.FileNode) error {
|
||||
if node.GetPath() == ".gitignore" {
|
||||
return self.c.ErrorMsg(self.c.Tr.Actions.IgnoreFileErr)
|
||||
}
|
||||
err := self.ignoreOrExcludeFile(node, self.c.Tr.IgnoreTracked, self.c.Tr.IgnoreTrackedPrompt, self.c.Tr.Actions.LcIgnoreExcludeFile, self.git.WorkingTree.Ignore)
|
||||
err := self.ignoreOrExcludeFile(node, self.c.Tr.IgnoreTracked, self.c.Tr.IgnoreTrackedPrompt, self.c.Tr.Actions.LcIgnoreExcludeFile, self.c.Git().WorkingTree.Ignore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -512,7 +587,7 @@ func (self *FilesController) exclude(node *filetree.FileNode) error {
|
||||
return self.c.ErrorMsg(self.c.Tr.Actions.ExcludeGitIgnoreErr)
|
||||
}
|
||||
|
||||
err := self.ignoreOrExcludeFile(node, self.c.Tr.ExcludeTracked, self.c.Tr.ExcludeTrackedPrompt, self.c.Tr.Actions.ExcludeFile, self.git.WorkingTree.Exclude)
|
||||
err := self.ignoreOrExcludeFile(node, self.c.Tr.ExcludeTracked, self.c.Tr.ExcludeTrackedPrompt, self.c.Tr.Actions.ExcludeFile, self.c.Git().WorkingTree.Exclude)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -552,19 +627,19 @@ func (self *FilesController) refresh() error {
|
||||
}
|
||||
|
||||
func (self *FilesController) handleAmendCommitPress() error {
|
||||
if len(self.model.Files) == 0 {
|
||||
if len(self.c.Model().Files) == 0 {
|
||||
return self.c.ErrorMsg(self.c.Tr.NoFilesStagedTitle)
|
||||
}
|
||||
|
||||
if !self.helpers.WorkingTree.AnyStagedFiles() {
|
||||
return self.helpers.WorkingTree.PromptToStageAllAndRetry(self.handleAmendCommitPress)
|
||||
if !self.c.Helpers().WorkingTree.AnyStagedFiles() {
|
||||
return self.c.Helpers().WorkingTree.PromptToStageAllAndRetry(self.handleAmendCommitPress)
|
||||
}
|
||||
|
||||
if len(self.model.Commits) == 0 {
|
||||
if len(self.c.Model().Commits) == 0 {
|
||||
return self.c.ErrorMsg(self.c.Tr.NoCommitToAmend)
|
||||
}
|
||||
|
||||
return self.helpers.AmendHelper.AmendHead()
|
||||
return self.c.Helpers().AmendHelper.AmendHead()
|
||||
}
|
||||
|
||||
func (self *FilesController) handleStatusFilterPressed() error {
|
||||
@@ -603,7 +678,7 @@ func (self *FilesController) edit(node *filetree.FileNode) error {
|
||||
return self.c.ErrorMsg(self.c.Tr.ErrCannotEditDirectory)
|
||||
}
|
||||
|
||||
return self.helpers.Files.EditFile(node.GetPath())
|
||||
return self.c.Helpers().Files.EditFile(node.GetPath())
|
||||
}
|
||||
|
||||
func (self *FilesController) Open() error {
|
||||
@@ -612,7 +687,7 @@ func (self *FilesController) Open() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return self.helpers.Files.OpenFile(node.GetPath())
|
||||
return self.c.Helpers().Files.OpenFile(node.GetPath())
|
||||
}
|
||||
|
||||
func (self *FilesController) switchToMerge() error {
|
||||
@@ -621,7 +696,7 @@ func (self *FilesController) switchToMerge() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return self.helpers.MergeConflicts.SwitchToMerge(file.Name)
|
||||
return self.c.Helpers().MergeConflicts.SwitchToMerge(file.Name)
|
||||
}
|
||||
|
||||
func (self *FilesController) createStashMenu() error {
|
||||
@@ -631,28 +706,28 @@ func (self *FilesController) createStashMenu() error {
|
||||
{
|
||||
Label: self.c.Tr.LcStashAllChanges,
|
||||
OnPress: func() error {
|
||||
if !self.helpers.WorkingTree.IsWorkingTreeDirty() {
|
||||
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
|
||||
return self.c.ErrorMsg(self.c.Tr.NoFilesToStash)
|
||||
}
|
||||
return self.handleStashSave(self.git.Stash.Save, self.c.Tr.Actions.StashAllChanges)
|
||||
return self.handleStashSave(self.c.Git().Stash.Save, self.c.Tr.Actions.StashAllChanges)
|
||||
},
|
||||
Key: 'a',
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.LcStashAllChangesKeepIndex,
|
||||
OnPress: func() error {
|
||||
if !self.helpers.WorkingTree.IsWorkingTreeDirty() {
|
||||
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
|
||||
return self.c.ErrorMsg(self.c.Tr.NoFilesToStash)
|
||||
}
|
||||
// if there are no staged files it behaves the same as Stash.Save
|
||||
return self.handleStashSave(self.git.Stash.StashAndKeepIndex, self.c.Tr.Actions.StashAllChangesKeepIndex)
|
||||
return self.handleStashSave(self.c.Git().Stash.StashAndKeepIndex, self.c.Tr.Actions.StashAllChangesKeepIndex)
|
||||
},
|
||||
Key: 'i',
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.LcStashIncludeUntrackedChanges,
|
||||
OnPress: func() error {
|
||||
return self.handleStashSave(self.git.Stash.StashIncludeUntrackedChanges, self.c.Tr.Actions.StashIncludeUntrackedChanges)
|
||||
return self.handleStashSave(self.c.Git().Stash.StashIncludeUntrackedChanges, self.c.Tr.Actions.StashIncludeUntrackedChanges)
|
||||
},
|
||||
Key: 'U',
|
||||
},
|
||||
@@ -660,24 +735,24 @@ func (self *FilesController) createStashMenu() error {
|
||||
Label: self.c.Tr.LcStashStagedChanges,
|
||||
OnPress: func() error {
|
||||
// there must be something in staging otherwise the current implementation mucks the stash up
|
||||
if !self.helpers.WorkingTree.AnyStagedFiles() {
|
||||
if !self.c.Helpers().WorkingTree.AnyStagedFiles() {
|
||||
return self.c.ErrorMsg(self.c.Tr.NoTrackedStagedFilesStash)
|
||||
}
|
||||
return self.handleStashSave(self.git.Stash.SaveStagedChanges, self.c.Tr.Actions.StashStagedChanges)
|
||||
return self.handleStashSave(self.c.Git().Stash.SaveStagedChanges, self.c.Tr.Actions.StashStagedChanges)
|
||||
},
|
||||
Key: 's',
|
||||
},
|
||||
{
|
||||
Label: self.c.Tr.LcStashUnstagedChanges,
|
||||
OnPress: func() error {
|
||||
if !self.helpers.WorkingTree.IsWorkingTreeDirty() {
|
||||
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
|
||||
return self.c.ErrorMsg(self.c.Tr.NoFilesToStash)
|
||||
}
|
||||
if self.helpers.WorkingTree.AnyStagedFiles() {
|
||||
return self.handleStashSave(self.git.Stash.StashUnstagedChanges, self.c.Tr.Actions.StashUnstagedChanges)
|
||||
if self.c.Helpers().WorkingTree.AnyStagedFiles() {
|
||||
return self.handleStashSave(self.c.Git().Stash.StashUnstagedChanges, self.c.Tr.Actions.StashUnstagedChanges)
|
||||
}
|
||||
// ordinary stash
|
||||
return self.handleStashSave(self.git.Stash.Save, self.c.Tr.Actions.StashUnstagedChanges)
|
||||
return self.handleStashSave(self.c.Git().Stash.Save, self.c.Tr.Actions.StashUnstagedChanges)
|
||||
},
|
||||
Key: 'u',
|
||||
},
|
||||
@@ -686,11 +761,11 @@ func (self *FilesController) createStashMenu() error {
|
||||
}
|
||||
|
||||
func (self *FilesController) stash() error {
|
||||
return self.handleStashSave(self.git.Stash.Save, self.c.Tr.Actions.StashAllChanges)
|
||||
return self.handleStashSave(self.c.Git().Stash.Save, self.c.Tr.Actions.StashAllChanges)
|
||||
}
|
||||
|
||||
func (self *FilesController) createResetToUpstreamMenu() error {
|
||||
return self.helpers.Refs.CreateGitResetMenu("@{upstream}")
|
||||
return self.c.Helpers().Refs.CreateGitResetMenu("@{upstream}")
|
||||
}
|
||||
|
||||
func (self *FilesController) handleToggleDirCollapsed() error {
|
||||
@@ -701,7 +776,7 @@ func (self *FilesController) handleToggleDirCollapsed() error {
|
||||
|
||||
self.context().FileTreeViewModel.ToggleCollapsed(node.GetPath())
|
||||
|
||||
if err := self.c.PostRefreshUpdate(self.contexts.Files); err != nil {
|
||||
if err := self.c.PostRefreshUpdate(self.c.Contexts().Files); err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
|
||||
@@ -747,7 +822,7 @@ func (self *FilesController) fetch() error {
|
||||
|
||||
func (self *FilesController) fetchAux() (err error) {
|
||||
self.c.LogAction("Fetch")
|
||||
err = self.git.Sync.Fetch(git_commands.FetchOptions{})
|
||||
err = self.c.Git().Sync.Fetch(git_commands.FetchOptions{})
|
||||
|
||||
if err != nil && strings.Contains(err.Error(), "exit status 128") {
|
||||
_ = self.c.ErrorMsg(self.c.Tr.PassUnameWrong)
|
||||
|
||||
@@ -12,17 +12,17 @@ import (
|
||||
|
||||
type FilesRemoveController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &FilesRemoveController{}
|
||||
|
||||
func NewFilesRemoveController(
|
||||
common *controllerCommon,
|
||||
common *ControllerCommon,
|
||||
) *FilesRemoveController {
|
||||
return &FilesRemoveController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: common,
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
|
||||
Label: self.c.Tr.LcDiscardAllChanges,
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.DiscardAllChangesInDirectory)
|
||||
if err := self.git.WorkingTree.DiscardAllDirChanges(node); err != nil {
|
||||
if err := self.c.Git().WorkingTree.DiscardAllDirChanges(node); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
@@ -67,7 +67,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
|
||||
Label: self.c.Tr.LcDiscardUnstagedChanges,
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.DiscardUnstagedChangesInDirectory)
|
||||
if err := self.git.WorkingTree.DiscardUnstagedDirChanges(node); err != nil {
|
||||
if err := self.c.Git().WorkingTree.DiscardUnstagedDirChanges(node); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
|
||||
} else {
|
||||
file := node.File
|
||||
|
||||
submodules := self.model.Submodules
|
||||
submodules := self.c.Model().Submodules
|
||||
if file.IsSubmodule(submodules) {
|
||||
submodule := file.SubmoduleConfig(submodules)
|
||||
|
||||
@@ -103,7 +103,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
|
||||
Label: self.c.Tr.LcDiscardAllChanges,
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.DiscardAllChangesInFile)
|
||||
if err := self.git.WorkingTree.DiscardAllFileChanges(file); err != nil {
|
||||
if err := self.c.Git().WorkingTree.DiscardAllFileChanges(file); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}})
|
||||
@@ -123,7 +123,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
|
||||
Label: self.c.Tr.LcDiscardUnstagedChanges,
|
||||
OnPress: func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.DiscardAllUnstagedChangesInFile)
|
||||
if err := self.git.WorkingTree.DiscardUnstagedFileChanges(file); err != nil {
|
||||
if err := self.c.Git().WorkingTree.DiscardUnstagedFileChanges(file); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -148,17 +148,17 @@ func (self *FilesRemoveController) ResetSubmodule(submodule *models.SubmoduleCon
|
||||
return self.c.WithWaitingStatus(self.c.Tr.LcResettingSubmoduleStatus, func() error {
|
||||
self.c.LogAction(self.c.Tr.Actions.ResetSubmodule)
|
||||
|
||||
file := self.helpers.WorkingTree.FileForSubmodule(submodule)
|
||||
file := self.c.Helpers().WorkingTree.FileForSubmodule(submodule)
|
||||
if file != nil {
|
||||
if err := self.git.WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||
if err := self.c.Git().WorkingTree.UnStageFile(file.Names(), file.Tracked); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := self.git.Submodule.Stash(submodule); err != nil {
|
||||
if err := self.c.Git().Submodule.Stash(submodule); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
if err := self.git.Submodule.Reset(submodule); err != nil {
|
||||
if err := self.c.Git().Submodule.Reset(submodule); err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
|
||||
@@ -182,5 +182,5 @@ func (self *FilesRemoveController) Context() types.Context {
|
||||
}
|
||||
|
||||
func (self *FilesRemoveController) context() *context.WorkingTreeContext {
|
||||
return self.contexts.Files
|
||||
return self.c.Contexts().Files
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
)
|
||||
|
||||
type FilteringMenuAction struct {
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
func (self *FilteringMenuAction) Call() error {
|
||||
fileName := ""
|
||||
switch self.c.CurrentSideContext() {
|
||||
case self.c.Contexts().Files:
|
||||
node := self.c.Contexts().Files.GetSelected()
|
||||
if node != nil {
|
||||
fileName = node.GetPath()
|
||||
}
|
||||
case self.c.Contexts().CommitFiles:
|
||||
node := self.c.Contexts().CommitFiles.GetSelected()
|
||||
if node != nil {
|
||||
fileName = node.GetPath()
|
||||
}
|
||||
}
|
||||
|
||||
menuItems := []*types.MenuItem{}
|
||||
|
||||
if fileName != "" {
|
||||
menuItems = append(menuItems, &types.MenuItem{
|
||||
Label: fmt.Sprintf("%s '%s'", self.c.Tr.LcFilterBy, fileName),
|
||||
OnPress: func() error {
|
||||
return self.setFiltering(fileName)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
menuItems = append(menuItems, &types.MenuItem{
|
||||
Label: self.c.Tr.LcFilterPathOption,
|
||||
OnPress: func() error {
|
||||
return self.c.Prompt(types.PromptOpts{
|
||||
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetFilePathSuggestionsFunc(),
|
||||
Title: self.c.Tr.EnterFileName,
|
||||
HandleConfirm: func(response string) error {
|
||||
return self.setFiltering(strings.TrimSpace(response))
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
|
||||
if self.c.Modes().Filtering.Active() {
|
||||
menuItems = append(menuItems, &types.MenuItem{
|
||||
Label: self.c.Tr.LcExitFilterMode,
|
||||
OnPress: self.c.Helpers().Mode.ClearFiltering,
|
||||
})
|
||||
}
|
||||
|
||||
return self.c.Menu(types.CreateMenuOptions{Title: self.c.Tr.FilteringMenuTitle, Items: menuItems})
|
||||
}
|
||||
|
||||
func (self *FilteringMenuAction) setFiltering(path string) error {
|
||||
self.c.Modes().Filtering.SetPath(path)
|
||||
|
||||
repoState := self.c.State().GetRepoState()
|
||||
if repoState.GetScreenMode() == types.SCREEN_NORMAL {
|
||||
repoState.SetScreenMode(types.SCREEN_HALF)
|
||||
}
|
||||
|
||||
if err := self.c.PushContext(self.c.Contexts().LocalCommits); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.COMMITS}, Then: func() {
|
||||
self.c.Contexts().LocalCommits.SetSelectedLineIdx(0)
|
||||
self.c.Contexts().LocalCommits.FocusLine()
|
||||
}})
|
||||
}
|
||||
@@ -11,17 +11,17 @@ import (
|
||||
|
||||
type GitFlowController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
var _ types.IController = &GitFlowController{}
|
||||
|
||||
func NewGitFlowController(
|
||||
common *controllerCommon,
|
||||
common *ControllerCommon,
|
||||
) *GitFlowController {
|
||||
return &GitFlowController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: common,
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func (self *GitFlowController) GetKeybindings(opts types.KeybindingsOpts) []*typ
|
||||
}
|
||||
|
||||
func (self *GitFlowController) handleCreateGitFlowMenu(branch *models.Branch) error {
|
||||
if !self.git.Flow.GitFlowEnabled() {
|
||||
if !self.c.Git().Flow.GitFlowEnabled() {
|
||||
return self.c.ErrorMsg("You need to install git-flow and enable it in this repo to use git-flow features")
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ func (self *GitFlowController) handleCreateGitFlowMenu(branch *models.Branch) er
|
||||
HandleConfirm: func(name string) error {
|
||||
self.c.LogAction(self.c.Tr.Actions.GitFlowStart)
|
||||
return self.c.RunSubprocessAndRefresh(
|
||||
self.git.Flow.StartCmdObj(branchType, name),
|
||||
self.c.Git().Flow.StartCmdObj(branchType, name),
|
||||
)
|
||||
},
|
||||
})
|
||||
@@ -94,7 +94,7 @@ func (self *GitFlowController) handleCreateGitFlowMenu(branch *models.Branch) er
|
||||
}
|
||||
|
||||
func (self *GitFlowController) gitFlowFinishBranch(branchName string) error {
|
||||
cmdObj, err := self.git.Flow.FinishCmdObj(branchName)
|
||||
cmdObj, err := self.c.Git().Flow.FinishCmdObj(branchName)
|
||||
if err != nil {
|
||||
return self.c.Error(err)
|
||||
}
|
||||
@@ -119,5 +119,5 @@ func (self *GitFlowController) Context() types.Context {
|
||||
}
|
||||
|
||||
func (self *GitFlowController) context() *context.BranchesContext {
|
||||
return self.contexts.Branches
|
||||
return self.c.Contexts().Branches
|
||||
}
|
||||
|
||||
@@ -1,26 +1,21 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/controllers/helpers"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
type GlobalController struct {
|
||||
baseController
|
||||
*controllerCommon
|
||||
c *ControllerCommon
|
||||
}
|
||||
|
||||
func NewGlobalController(
|
||||
common *controllerCommon,
|
||||
common *ControllerCommon,
|
||||
) *GlobalController {
|
||||
return &GlobalController{
|
||||
baseController: baseController{},
|
||||
controllerCommon: common,
|
||||
baseController: baseController{},
|
||||
c: common,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,47 +26,143 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type
|
||||
Handler: self.customCommand,
|
||||
Description: self.c.Tr.LcExecuteCustomCommand,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (self *GlobalController) customCommand() error {
|
||||
return self.c.Prompt(types.PromptOpts{
|
||||
Title: self.c.Tr.CustomCommand,
|
||||
FindSuggestionsFunc: self.GetCustomCommandsHistorySuggestionsFunc(),
|
||||
HandleConfirm: func(command string) error {
|
||||
if self.shouldSaveCommand(command) {
|
||||
self.c.GetAppState().CustomCommandsHistory = utils.Limit(
|
||||
lo.Uniq(append(self.c.GetAppState().CustomCommandsHistory, command)),
|
||||
1000,
|
||||
)
|
||||
}
|
||||
|
||||
err := self.c.SaveAppState()
|
||||
if err != nil {
|
||||
self.c.Log.Error(err)
|
||||
}
|
||||
|
||||
self.c.LogAction(self.c.Tr.Actions.CustomCommand)
|
||||
return self.c.RunSubprocessAndRefresh(
|
||||
self.os.Cmd.NewShell(command),
|
||||
)
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.CreatePatchOptionsMenu),
|
||||
Handler: self.createCustomPatchOptionsMenu,
|
||||
Description: self.c.Tr.ViewPatchOptions,
|
||||
OpensMenu: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// this mimics the shell functionality `ignorespace`
|
||||
// which doesn't save a command to history if it starts with a space
|
||||
func (self *GlobalController) shouldSaveCommand(command string) bool {
|
||||
return !strings.HasPrefix(command, " ")
|
||||
}
|
||||
|
||||
func (self *GlobalController) GetCustomCommandsHistorySuggestionsFunc() func(string) []*types.Suggestion {
|
||||
// reversing so that we display the latest command first
|
||||
history := slices.Reverse(self.c.GetAppState().CustomCommandsHistory)
|
||||
|
||||
return helpers.FuzzySearchFunc(history)
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.CreateRebaseOptionsMenu),
|
||||
Handler: self.c.Helpers().MergeAndRebase.CreateRebaseOptionsMenu,
|
||||
Description: self.c.Tr.ViewMergeRebaseOptions,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Refresh),
|
||||
Handler: self.refresh,
|
||||
Description: self.c.Tr.LcRefresh,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.NextScreenMode),
|
||||
Handler: self.nextScreenMode,
|
||||
Description: self.c.Tr.LcNextScreenMode,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.PrevScreenMode),
|
||||
Handler: self.prevScreenMode,
|
||||
Description: self.c.Tr.LcPrevScreenMode,
|
||||
},
|
||||
{
|
||||
ViewName: "",
|
||||
Key: opts.GetKey(opts.Config.Universal.OptionMenu),
|
||||
Handler: self.createOptionsMenu,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
ViewName: "",
|
||||
Key: opts.GetKey(opts.Config.Universal.OptionMenuAlt1),
|
||||
Modifier: gocui.ModNone,
|
||||
// we have the description on the alt key and not the main key for legacy reasons
|
||||
// (the original main key was 'x' but we've reassigned that to other purposes)
|
||||
Description: self.c.Tr.LcOpenMenu,
|
||||
Handler: self.createOptionsMenu,
|
||||
},
|
||||
{
|
||||
ViewName: "",
|
||||
Key: opts.GetKey(opts.Config.Universal.FilteringMenu),
|
||||
Handler: self.createFilteringMenu,
|
||||
Description: self.c.Tr.LcOpenFilteringMenu,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.DiffingMenu),
|
||||
Handler: self.createDiffingMenu,
|
||||
Description: self.c.Tr.LcOpenDiffingMenu,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.DiffingMenuAlt),
|
||||
Handler: self.createDiffingMenu,
|
||||
Description: self.c.Tr.LcOpenDiffingMenu,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Quit),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.quit,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.QuitAlt1),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.quit,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.QuitWithoutChangingDirectory),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.quitWithoutChangingDirectory,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.Return),
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: self.escape,
|
||||
},
|
||||
{
|
||||
Key: opts.GetKey(opts.Config.Universal.ToggleWhitespaceInDiffView),
|
||||
Handler: self.toggleWhitespace,
|
||||
Description: self.c.Tr.ToggleWhitespaceInDiffView,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (self *GlobalController) Context() types.Context {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *GlobalController) customCommand() error {
|
||||
return (&CustomCommandAction{c: self.c}).Call()
|
||||
}
|
||||
|
||||
func (self *GlobalController) createCustomPatchOptionsMenu() error {
|
||||
return (&CustomPatchOptionsMenuAction{c: self.c}).Call()
|
||||
}
|
||||
|
||||
func (self *GlobalController) refresh() error {
|
||||
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
|
||||
}
|
||||
|
||||
func (self *GlobalController) nextScreenMode() error {
|
||||
return (&ScreenModeActions{c: self.c}).Next()
|
||||
}
|
||||
|
||||
func (self *GlobalController) prevScreenMode() error {
|
||||
return (&ScreenModeActions{c: self.c}).Prev()
|
||||
}
|
||||
|
||||
func (self *GlobalController) createOptionsMenu() error {
|
||||
return (&OptionsMenuAction{c: self.c}).Call()
|
||||
}
|
||||
|
||||
func (self *GlobalController) createFilteringMenu() error {
|
||||
return (&FilteringMenuAction{c: self.c}).Call()
|
||||
}
|
||||
|
||||
func (self *GlobalController) createDiffingMenu() error {
|
||||
return (&DiffingMenuAction{c: self.c}).Call()
|
||||
}
|
||||
|
||||
func (self *GlobalController) quit() error {
|
||||
return (&QuitActions{c: self.c}).Quit()
|
||||
}
|
||||
|
||||
func (self *GlobalController) quitWithoutChangingDirectory() error {
|
||||
return (&QuitActions{c: self.c}).QuitWithoutChangingDirectory()
|
||||
}
|
||||
|
||||
func (self *GlobalController) escape() error {
|
||||
return (&QuitActions{c: self.c}).Escape()
|
||||
}
|
||||
|
||||
func (self *GlobalController) toggleWhitespace() error {
|
||||
return (&ToggleWhitespaceAction{c: self.c}).Call()
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user