Compare commits

...
Author SHA1 Message Date
Jesse Duffield b146c05578 WIP 2023-05-27 13:11:25 +10:00
Jesse Duffield 6c7f893f7a WIP 2023-05-27 12:08:27 +10:00
Jesse Duffield 8b15f22379 WIP 2023-05-26 19:05:22 +10:00
Jesse Duffield 9c7db3a37b Utilise file instead of stderr for integration tests
Windows for some reason is not properly making use of stderr so instead
we're going to write our result to a file explicitly
2023-05-26 19:05:22 +10:00
Jesse Duffield c961a5833b WIP 2023-05-26 19:05:22 +10:00
Jesse Duffield 6509875009 WIP 2023-05-26 19:05:22 +10:00
Jesse Duffield 3bf1be65b3 Run integration tests on windows 2023-05-26 19:04:29 +10:00
Jesse Duffield 8e6967c702 Merge pull request #2676 from jesseduffield/better-time-format 2023-05-26 17:34:06 +10:00
Jesse Duffield 0e0458f355 More compact and flexible date format
You can now configure both a time format and a short time format, where the short format kicks in
when the time is within the last day
2023-05-26 17:31:39 +10:00
Jesse Duffield 05bfa96936 Merge pull request #2672 from jesseduffield/sentence-case 2023-05-26 17:21:33 +10:00
Jesse Duffield be6acf2fbe Merge pull request #2670 from jesseduffield/better-list-context-trait 2023-05-26 15:28:32 +10:00
README-bot bf092f76d6 Updated README.md 2023-05-25 23:19:47 +00:00
Jesse Duffield 9c384c5267 Clean up helix editor preset 2023-05-26 09:19:12 +10:00
Jesse Duffield d772c9f1d4 Use sentence case everywhere
We have not been good at consistent casing so far. Now we use 'Sentence case' everywhere. EVERYWHERE.

Also Removing 'Lc' prefix from i18n field names: the 'Lc' stood for lowercase but now that everything
is in 'Sentence case' there's no need for the distinction.

I've got a couple lower case things I've kept: namely, things that show up in parentheses.
2023-05-25 23:52:19 +10:00
Jesse Duffield e5534d9781 Merge pull request #2668 from dvic/add-helix-support 2023-05-25 19:59:14 +10:00
Jesse Duffield 34755285a1 Merge pull request #2671 from jesseduffield/global-logging-for-development 2023-05-25 18:41:27 +10:00
Jesse Duffield e0ecc9e835 Allow global logging when developing
I'll be honest, for all I know logging should be global in general: it is
a pain to pass a logger to any struct that needs it. But smart people on the
internet tell me otherwise, and I do like the idea of not having any global
variables lying around.

Nonetheless, I often need to log things when locally debugging and that's a
different kind of logging than the kind you would include in the actual
released binary. For example if I want to log something from gocui, I would
rather not have gocui depend on lazygit's logging setup.
2023-05-25 18:31:32 +10:00
Jesse Duffield add1de4138 Use boolean field to control whether viewport is refreshed on line focus
Go really doesn't like us doing anything inheritance-y: it does not support open recursion meaning
it's really hard to re-use code. As such, here we're falling back to conditional logic.

This fixes an issue where our ListContextTrait was calling FocusLine which was intended to be
overridden by ViewportListContextTrait, but the subclassed function wasn't being called. I'm
not actually sure how this went wrong given that it was working fine in the past, but at any rate,
the new code is easy to follow.
2023-05-25 17:09:18 +10:00
dvic ed496deeca Add helix editor preset 2023-05-24 23:08:26 +02:00
Jesse Duffield 1f8e838052 Merge pull request #2656 from mazharz/gitlab-merge-request-url
Update gitlab merge request URL to match new routing
2023-05-23 22:54:57 +10:00
Jesse Duffield f212e19efd Merge pull request #2662 from jesseduffield/patches-for-update-args-vector 2023-05-23 20:30:15 +10:00
Jesse Duffield fb0931e1a1 Fix discard logic
Missed a spot a couple PR's ago. We had an integration test which caught this but which was skipped due
to index.lock file issues. The test was also broken for other reasons due to it not having been running
for a while, so I've fixed that up too.
2023-05-23 20:23:08 +10:00
Jesse Duffield ee1597415d Merge pull request #2655 from jesseduffield/use-command-args-vector 2023-05-23 19:55:09 +10:00
Jesse Duffield 63dc07fded Construct arg vector manually rather than parse string
By constructing an arg vector manually, we no longer need to quote arguments

Mandate that args must be passed when building a command

Now you need to provide an args array when building a command.
There are a handful of places where we need to deal with a string,
such as with user-defined custom commands, and for those we now require
that at the callsite they use str.ToArgv to do that. I don't want
to provide a method out of the box for it because I want to discourage its
use.

For some reason we were invoking a command through a shell when amending a
commit, and I don't believe we needed to do that as there was nothing user-
supplied about the command. So I've switched to using a regular command out-
side the shell there
2023-05-23 19:49:19 +10:00
README-bot 70e473b25d Updated README.md 2023-05-23 09:20:04 +00:00
Jesse Duffield 1a4cf84b58 Merge pull request #2661 from jesseduffield/cache-binary-paths
Cache binary paths
2023-05-23 19:19:49 +10:00
Jesse Duffield ad72a1f5a3 Cache binary paths
Turns out that with our secureexec package (which we only use on windows due to a windows security thing),
2023-05-23 19:15:33 +10:00
Mazhar Zandsalimi 59379b45da Update gitlab commit URL to match new routing 2023-05-22 07:39:47 +03:30
Mazhar Zandsalimi 2d4ca2b54f Update gitlab merge request URL to match new routing 2023-05-21 13:21:34 +03:30
Stefan Haller ec5075104a Merge pull request #2644 from stefanhaller/remove-empty=keep-option-when-rebasing
Don't keep commits that become empty during a rebase
2023-05-21 07:45:21 +02:00
Stefan Haller 3cddd7cfa5 Don't keep commits that become empty during a rebase
The only exception is when moving a custom patch for an entire commit to an
earlier commit; in this case the source commit becomes empty, but we want to
keep it, mainly for consistency with moving the patch to a later commit, which
behaves the same.

In all other cases where we rebase, it's confusing when empty commits are kept;
the most common example is rebasing a branch onto master, where master already
contains some of the commits of our branch. In this case we simply want to drop
these.
2023-05-20 21:10:03 +02:00
373 changed files with 5732 additions and 4196 deletions
+20 -7
View File
@@ -43,12 +43,23 @@ jobs:
restore-keys: |
${{runner.os}}-go-
- name: Test code
# we're passing -short so that we skip the integration tests, which will be run in parallel below
# we're passing -short so that we skip the integration tests, which will be run below
run: |
go test ./... -short
integration-tests:
runs-on: ubuntu-latest
name: "Integration Tests"
strategy:
fail-fast: false
matrix:
os:
- ubuntu-latest
- windows-latest
include:
- os: ubuntu-latest
cache_path: ~/.cache/go-build
- os: windows-latest
cache_path: ~\AppData\Local\go-build
runs-on: ${{matrix.os}}
name: "Integration Tests - ${{matrix.os}}"
env:
GOFLAGS: -mod=vendor
steps:
@@ -59,18 +70,20 @@ jobs:
with:
go-version: 1.18.x
- name: Cache build
uses: actions/cache@v1
uses: actions/cache@v3
with:
path: |
~/.cache/go-build
${{matrix.cache_path}}
~/go/pkg/mod
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
restore-keys: |
${{runner.os}}-go-
- name: Test code
# LONG_WAIT_BEFORE_FAIL means that for a given test assertion, we'll wait longer before failing
env:
# LONG_WAIT_BEFORE_FAIL means that for a given test assertion, we'll wait longer before failing
LONG_WAIT_BEFORE_FAIL: true
run: |
LONG_WAIT_BEFORE_FAIL=true go test pkg/integration/clients/*.go
go test ./pkg/integration/clients
build:
runs-on: ubuntu-latest
env:
+3 -3
View File
@@ -24,7 +24,7 @@
{
"label": "Run current file integration test",
"type": "shell",
"command": "go generate pkg/integration/tests/tests.go && go run cmd/integration_test/main.go cli ${relativeFile}",
"command": "go run cmd/integration_test/main.go cli ${relativeFile}",
"problemMatcher": [],
"group": {
"kind": "test",
@@ -37,7 +37,7 @@
{
"label": "Run current file integration test (slow)",
"type": "shell",
"command": "go generate pkg/integration/tests/tests.go && go run cmd/integration_test/main.go cli --slow ${relativeFile}",
"command": "go run cmd/integration_test/main.go cli --slow ${relativeFile}",
"problemMatcher": [],
"group": {
"kind": "test",
@@ -49,7 +49,7 @@
{
"label": "Run current file integration test (sandbox)",
"type": "shell",
"command": "go generate pkg/integration/tests/tests.go && go run cmd/integration_test/main.go cli --sandbox ${relativeFile}",
"command": "go run cmd/integration_test/main.go cli --sandbox ${relativeFile}",
"problemMatcher": [],
"group": {
"kind": "test",
+5 -22
View File
@@ -99,36 +99,19 @@ go install mvdan.cc/gofumpt@latest && gofumpt -l -w .
## Internationalisation
Boy that's a hard word to spell. Anyway, lazygit is translated into several languages within the pkg/i18n package. If you need to render text to the user, you should add a new field to the TranslationSet struct in `pkg/i18n/english.go` and add the actual content within the `EnglishTranslationSet()` method in the same file. Then you can access via `gui.Tr.YourNewText` (or `app.Tr.YourNewText`, etc). Although it is appreciated if you translate the text into other languages, it's not expected of you (google translate will likely do a bad job anyway!).
Boy that's a hard word to spell. Anyway, lazygit is translated into several languages within the pkg/i18n package. If you need to render text to the user, you should add a new field to the TranslationSet struct in `pkg/i18n/english.go` and add the actual content within the `EnglishTranslationSet()` method in the same file. Then you can access via `gui.Tr.YourNewText` (or `self.c.Tr.YourNewText`, etc). Although it is appreciated if you translate the text into other languages, it's not expected of you (google translate will likely do a bad job anyway!).
Note, we use 'Sentence case' for everything (so no 'Title Case' or 'whatever-it's-called-when-there's-no-capital-letters-case')
## Debugging
The easiest way to debug lazygit is to have two terminal tabs open at once: one for running lazygit (via `go run main.go -debug` in the project root) and one for viewing lazygit's logs (which can be done via `go run main.go --logs` or just `lazygit --logs`).
From most places in the codebase you have access to a logger e.g. `gui.Log.Warn("blah")`.
From most places in the codebase you have access to a logger e.g. `gui.Log.Warn("blah")` or `self.c.Log.Warn("blah")`.
If you find that the existing logs are too noisy, you can set the log level with e.g. `LOG_LEVEL=warn go run main.go -debug` and then only use `Warn` logs yourself.
If you need to log from code in the vendor directory (e.g. the `gocui` package), you won't have access to the logger, but you can easily add logging support by adding the following:
```go
func newLogger() *logrus.Entry {
// REPLACE THE BELOW PATH WITH YOUR ACTUAL LOG PATH (YOU'LL SEE THIS PRINTED WHEN YOU RUN `lazygit --logs`
logPath := "/Users/jesseduffield/Library/Application Support/jesseduffield/lazygit/development.log"
file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
panic("unable to log to file")
}
logger := logrus.New()
logger.SetLevel(logrus.WarnLevel)
logger.SetOutput(file)
return logger.WithFields(logrus.Fields{})
}
var Log = newLogger()
...
Log.Warn("blah")
```
If you need to log from code in the vendor directory (e.g. the `gocui` package), you won't have access to the logger, but you can easily add logging support by setting the `LAZYGIT_LOG_PATH` environment variable and using `logs.Global.Warn("blah")`. This is a global logger that's only intended for development purposes.
If you keep having to do some setup steps to reproduce an issue, read the Testing section below to see how to create an integration test by recording a lazygit session. It's pretty easy!
+1 -1
View File
File diff suppressed because one or more lines are too long
+7
View File
@@ -6,6 +6,8 @@ import (
"os"
"github.com/jesseduffield/lazygit/pkg/integration/clients"
"github.com/jesseduffield/lazygit/pkg/integration/components"
"github.com/samber/lo"
)
var usage = `
@@ -49,6 +51,11 @@ func main() {
}
}
// allowing full test paths to be passed for convenience
testNames = lo.Map(testNames, func(name string, _ int) string {
return components.TestNameFromFilePath(name)
})
clients.RunCLI(testNames, slow, sandbox)
case "tui":
clients.RunTUI()
+3 -2
View File
@@ -28,7 +28,8 @@ gui:
expandFocusedSidePanel: false
mainPanelSplitMode: 'flexible' # one of 'horizontal' | 'flexible' | 'vertical'
language: 'auto' # one of 'auto' | 'en' | 'zh' | 'pl' | 'nl' | 'ja' | 'ko'
timeFormat: '02 Jan 06 15:04 MST' # https://pkg.go.dev/time#Time.Format
timeFormat: '02 Jan 06' # https://pkg.go.dev/time#Time.Format
shortTimeFormat: '3:04PM'
theme:
activeBorderColor:
- green
@@ -289,7 +290,7 @@ os:
```
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
`kakoune`, `helix`, 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
+216 -216
View File
@@ -4,317 +4,317 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
## Global Keybindings
## Global keybindings
<pre>
<kbd>&lt;c-r&gt;</kbd>: switch to a recent repo
<kbd>&lt;pgup&gt;</kbd>: scroll up main panel (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: scroll down main panel (fn+down/shift+j)
<kbd>@</kbd>: open command log menu
<kbd>&lt;c-r&gt;</kbd>: Switch to a recent repo
<kbd>&lt;pgup&gt;</kbd>: Scroll up main panel (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: Scroll down main panel (fn+down/shift+j)
<kbd>@</kbd>: Open command log menu
<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>&lt;c-p&gt;</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>&lt;c-s&gt;</kbd>: view filter-by-path options
<kbd>W</kbd>: open diff menu
<kbd>&lt;c-e&gt;</kbd>: open diff menu
<kbd>:</kbd>: Execute custom command
<kbd>&lt;c-p&gt;</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>&lt;c-s&gt;</kbd>: View filter-by-path options
<kbd>W</kbd>: Open diff menu
<kbd>&lt;c-e&gt;</kbd>: Open diff menu
<kbd>&lt;c-w&gt;</kbd>: Toggle whether or not whitespace changes are shown in the diff view
<kbd>z</kbd>: undo (via reflog) (experimental)
<kbd>&lt;c-z&gt;</kbd>: redo (via reflog) (experimental)
<kbd>P</kbd>: push
<kbd>p</kbd>: pull
<kbd>z</kbd>: Undo
<kbd>&lt;c-z&gt;</kbd>: Redo
<kbd>P</kbd>: Push
<kbd>p</kbd>: Pull
</pre>
## List Panel Navigation
## List panel navigation
<pre>
<kbd>,</kbd>: previous page
<kbd>.</kbd>: next page
<kbd>&lt;</kbd>: scroll to top
<kbd>/</kbd>: start search
<kbd>&gt;</kbd>: scroll to bottom
<kbd>H</kbd>: scroll left
<kbd>L</kbd>: scroll right
<kbd>]</kbd>: next tab
<kbd>[</kbd>: previous tab
<kbd>,</kbd>: Previous page
<kbd>.</kbd>: Next page
<kbd>&lt;</kbd>: Scroll to top
<kbd>/</kbd>: Start search
<kbd>&gt;</kbd>: Scroll to bottom
<kbd>H</kbd>: Scroll left
<kbd>L</kbd>: Scroll right
<kbd>]</kbd>: Next tab
<kbd>[</kbd>: Previous tab
</pre>
## Commit Files
## Commit files
<pre>
<kbd>&lt;c-o&gt;</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>&lt;space&gt;</kbd>: toggle file included in patch
<kbd>a</kbd>: toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>`</kbd>: toggle file tree view
<kbd>&lt;c-o&gt;</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>&lt;space&gt;</kbd>: Toggle file included in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>`</kbd>: Toggle file tree view
</pre>
## Commit Summary
## Commit summary
<pre>
<kbd>&lt;enter&gt;</kbd>: confirm
<kbd>&lt;esc&gt;</kbd>: close
<kbd>&lt;enter&gt;</kbd>: Confirm
<kbd>&lt;esc&gt;</kbd>: Close
</pre>
## Commits
<pre>
<kbd>&lt;c-o&gt;</kbd>: copy commit SHA to clipboard
<kbd>&lt;c-r&gt;</kbd>: reset cherry-picked (copied) commits selection
<kbd>b</kbd>: view bisect options
<kbd>s</kbd>: squash down
<kbd>f</kbd>: fixup commit
<kbd>r</kbd>: reword commit
<kbd>R</kbd>: reword commit with editor
<kbd>d</kbd>: delete commit
<kbd>e</kbd>: edit commit
<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>&lt;c-j&gt;</kbd>: move commit down one
<kbd>&lt;c-k&gt;</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>&lt;c-l&gt;</kbd>: open log menu
<kbd>&lt;space&gt;</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>&lt;enter&gt;</kbd>: view selected item's files
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>b</kbd>: View bisect options
<kbd>s</kbd>: Squash down
<kbd>f</kbd>: Fixup commit
<kbd>r</kbd>: Reword commit
<kbd>R</kbd>: Reword commit with editor
<kbd>d</kbd>: Delete commit
<kbd>e</kbd>: Edit commit
<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>&lt;c-j&gt;</kbd>: Move commit down one
<kbd>&lt;c-k&gt;</kbd>: Move commit up one
<kbd>v</kbd>: Paste commits (cherry-pick)
<kbd>A</kbd>: Amend commit with staged changes
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: Revert commit
<kbd>T</kbd>: Tag commit
<kbd>&lt;c-l&gt;</kbd>: Open log menu
<kbd>&lt;space&gt;</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>&lt;enter&gt;</kbd>: View selected item's files
</pre>
## Confirmation Panel
## Confirmation panel
<pre>
<kbd>&lt;enter&gt;</kbd>: confirm
<kbd>&lt;esc&gt;</kbd>: close/cancel
<kbd>&lt;enter&gt;</kbd>: Confirm
<kbd>&lt;esc&gt;</kbd>: Close/Cancel
</pre>
## Files
<pre>
<kbd>&lt;c-o&gt;</kbd>: copy the file name to the clipboard
<kbd>d</kbd>: view 'discard changes' options
<kbd>&lt;space&gt;</kbd>: toggle staged
<kbd>&lt;c-o&gt;</kbd>: Copy the file name to the clipboard
<kbd>d</kbd>: View 'discard changes' options
<kbd>&lt;space&gt;</kbd>: Toggle staged
<kbd>&lt;c-b&gt;</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
<kbd>C</kbd>: commit changes using git editor
<kbd>e</kbd>: edit file
<kbd>o</kbd>: open file
<kbd>i</kbd>: ignore or exclude file
<kbd>r</kbd>: refresh files
<kbd>s</kbd>: stash all changes
<kbd>S</kbd>: view stash options
<kbd>a</kbd>: stage/unstage all
<kbd>&lt;enter&gt;</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
<kbd>M</kbd>: open external merge tool (git mergetool)
<kbd>f</kbd>: fetch
<kbd>c</kbd>: Commit changes
<kbd>w</kbd>: Commit changes without pre-commit hook
<kbd>A</kbd>: Amend last commit
<kbd>C</kbd>: Commit changes using git editor
<kbd>e</kbd>: Edit file
<kbd>o</kbd>: Open file
<kbd>i</kbd>: Ignore or exclude file
<kbd>r</kbd>: Refresh files
<kbd>s</kbd>: Stash all changes
<kbd>S</kbd>: View stash options
<kbd>a</kbd>: Stage/unstage all
<kbd>&lt;enter&gt;</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
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>f</kbd>: Fetch
</pre>
## Local Branches
## Local branches
<pre>
<kbd>&lt;c-o&gt;</kbd>: copy branch name to clipboard
<kbd>i</kbd>: show git-flow options
<kbd>&lt;space&gt;</kbd>: checkout
<kbd>n</kbd>: new branch
<kbd>o</kbd>: create pull request
<kbd>O</kbd>: create pull request options
<kbd>&lt;c-y&gt;</kbd>: copy pull request URL to clipboard
<kbd>c</kbd>: checkout by name
<kbd>F</kbd>: force checkout
<kbd>d</kbd>: delete branch
<kbd>r</kbd>: rebase checked-out branch onto this branch
<kbd>M</kbd>: merge into currently checked out branch
<kbd>f</kbd>: fast-forward this branch from its upstream
<kbd>T</kbd>: create tag
<kbd>g</kbd>: view reset options
<kbd>R</kbd>: rename branch
<kbd>u</kbd>: set/unset upstream
<kbd>&lt;enter&gt;</kbd>: view commits
<kbd>&lt;c-o&gt;</kbd>: Copy branch name to clipboard
<kbd>i</kbd>: Show git-flow options
<kbd>&lt;space&gt;</kbd>: Checkout
<kbd>n</kbd>: New branch
<kbd>o</kbd>: Create pull request
<kbd>O</kbd>: Create pull request options
<kbd>&lt;c-y&gt;</kbd>: Copy pull request URL to clipboard
<kbd>c</kbd>: Checkout by name
<kbd>F</kbd>: Force checkout
<kbd>d</kbd>: Delete branch
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>M</kbd>: Merge into currently checked out branch
<kbd>f</kbd>: Fast-forward this branch from its upstream
<kbd>T</kbd>: Create tag
<kbd>g</kbd>: View reset options
<kbd>R</kbd>: Rename branch
<kbd>u</kbd>: Set/Unset upstream
<kbd>&lt;enter&gt;</kbd>: View commits
</pre>
## Main Panel (Merging)
## Main panel (merging)
<pre>
<kbd>e</kbd>: edit file
<kbd>o</kbd>: open file
<kbd>&lt;left&gt;</kbd>: select previous conflict
<kbd>&lt;right&gt;</kbd>: select next conflict
<kbd>&lt;up&gt;</kbd>: select previous hunk
<kbd>&lt;down&gt;</kbd>: select next hunk
<kbd>z</kbd>: undo
<kbd>M</kbd>: open external merge tool (git mergetool)
<kbd>&lt;space&gt;</kbd>: pick hunk
<kbd>b</kbd>: pick all hunks
<kbd>&lt;esc&gt;</kbd>: return to files panel
<kbd>e</kbd>: Edit file
<kbd>o</kbd>: Open file
<kbd>&lt;left&gt;</kbd>: Select previous conflict
<kbd>&lt;right&gt;</kbd>: Select next conflict
<kbd>&lt;up&gt;</kbd>: Select previous hunk
<kbd>&lt;down&gt;</kbd>: Select next hunk
<kbd>z</kbd>: Undo
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>&lt;space&gt;</kbd>: Pick hunk
<kbd>b</kbd>: Pick all hunks
<kbd>&lt;esc&gt;</kbd>: Return to files panel
</pre>
## Main Panel (Normal)
## Main panel (normal)
<pre>
<kbd>mouse wheel down</kbd>: scroll down (fn+up)
<kbd>mouse wheel up</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)
## Main panel (patch building)
<pre>
<kbd>&lt;left&gt;</kbd>: select previous hunk
<kbd>&lt;right&gt;</kbd>: select next hunk
<kbd>v</kbd>: toggle drag select
<kbd>V</kbd>: toggle drag select
<kbd>a</kbd>: toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: copy the selected text to the clipboard
<kbd>o</kbd>: open file
<kbd>e</kbd>: edit file
<kbd>&lt;space&gt;</kbd>: add/remove line(s) to patch
<kbd>&lt;esc&gt;</kbd>: exit custom patch builder
<kbd>&lt;left&gt;</kbd>: Select previous hunk
<kbd>&lt;right&gt;</kbd>: Select next hunk
<kbd>v</kbd>: Toggle drag select
<kbd>V</kbd>: Toggle drag select
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Open file
<kbd>e</kbd>: Edit file
<kbd>&lt;space&gt;</kbd>: Add/Remove line(s) to patch
<kbd>&lt;esc&gt;</kbd>: Exit custom patch builder
</pre>
## Main Panel (Staging)
## Main panel (staging)
<pre>
<kbd>&lt;left&gt;</kbd>: select previous hunk
<kbd>&lt;right&gt;</kbd>: select next hunk
<kbd>v</kbd>: toggle drag select
<kbd>V</kbd>: toggle drag select
<kbd>a</kbd>: toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: copy the selected text to the clipboard
<kbd>o</kbd>: open file
<kbd>e</kbd>: edit file
<kbd>&lt;esc&gt;</kbd>: return to files panel
<kbd>&lt;tab&gt;</kbd>: switch to other panel (staged/unstaged changes)
<kbd>&lt;space&gt;</kbd>: toggle line staged / unstaged
<kbd>d</kbd>: delete change (git reset)
<kbd>E</kbd>: edit hunk
<kbd>c</kbd>: commit changes
<kbd>w</kbd>: commit changes without pre-commit hook
<kbd>C</kbd>: commit changes using git editor
<kbd>&lt;left&gt;</kbd>: Select previous hunk
<kbd>&lt;right&gt;</kbd>: Select next hunk
<kbd>v</kbd>: Toggle drag select
<kbd>V</kbd>: Toggle drag select
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Open file
<kbd>e</kbd>: Edit file
<kbd>&lt;esc&gt;</kbd>: Return to files panel
<kbd>&lt;tab&gt;</kbd>: Switch to other panel (staged/unstaged changes)
<kbd>&lt;space&gt;</kbd>: Toggle line staged / unstaged
<kbd>d</kbd>: Delete change (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: Commit changes
<kbd>w</kbd>: Commit changes without pre-commit hook
<kbd>C</kbd>: Commit changes using git editor
</pre>
## Menu
<pre>
<kbd>&lt;enter&gt;</kbd>: execute
<kbd>&lt;esc&gt;</kbd>: close
<kbd>&lt;enter&gt;</kbd>: Execute
<kbd>&lt;esc&gt;</kbd>: Close
</pre>
## Reflog
<pre>
<kbd>&lt;c-o&gt;</kbd>: copy commit SHA to clipboard
<kbd>&lt;space&gt;</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>&lt;c-r&gt;</kbd>: reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: view commits
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>&lt;space&gt;</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>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View commits
</pre>
## Remote Branches
## Remote branches
<pre>
<kbd>&lt;c-o&gt;</kbd>: copy branch name to clipboard
<kbd>&lt;space&gt;</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>&lt;c-o&gt;</kbd>: Copy branch name to clipboard
<kbd>&lt;space&gt;</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>&lt;esc&gt;</kbd>: Return to remotes list
<kbd>g</kbd>: view reset options
<kbd>&lt;enter&gt;</kbd>: view commits
<kbd>g</kbd>: View reset options
<kbd>&lt;enter&gt;</kbd>: View commits
</pre>
## Remotes
<pre>
<kbd>f</kbd>: fetch remote
<kbd>n</kbd>: add new remote
<kbd>d</kbd>: remove remote
<kbd>e</kbd>: edit remote
<kbd>f</kbd>: Fetch remote
<kbd>n</kbd>: Add new remote
<kbd>d</kbd>: Remove remote
<kbd>e</kbd>: Edit remote
</pre>
## Stash
<pre>
<kbd>&lt;space&gt;</kbd>: apply
<kbd>g</kbd>: pop
<kbd>d</kbd>: drop
<kbd>n</kbd>: new branch
<kbd>r</kbd>: rename stash
<kbd>&lt;enter&gt;</kbd>: view selected item's files
<kbd>&lt;space&gt;</kbd>: Apply
<kbd>g</kbd>: Pop
<kbd>d</kbd>: Drop
<kbd>n</kbd>: New branch
<kbd>r</kbd>: Rename stash
<kbd>&lt;enter&gt;</kbd>: View selected item's files
</pre>
## Status
<pre>
<kbd>o</kbd>: open config file
<kbd>e</kbd>: edit config file
<kbd>u</kbd>: check for update
<kbd>&lt;enter&gt;</kbd>: switch to a recent repo
<kbd>a</kbd>: show all branch logs
<kbd>o</kbd>: Open config file
<kbd>e</kbd>: Edit config file
<kbd>u</kbd>: Check for update
<kbd>&lt;enter&gt;</kbd>: Switch to a recent repo
<kbd>a</kbd>: Show all branch logs
</pre>
## Sub-commits
<pre>
<kbd>&lt;c-o&gt;</kbd>: copy commit SHA to clipboard
<kbd>&lt;space&gt;</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>&lt;c-r&gt;</kbd>: reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: view selected item's files
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>&lt;space&gt;</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>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View selected item's files
</pre>
## Submodules
<pre>
<kbd>&lt;c-o&gt;</kbd>: copy submodule name to clipboard
<kbd>&lt;enter&gt;</kbd>: enter submodule
<kbd>d</kbd>: remove submodule
<kbd>u</kbd>: update submodule
<kbd>n</kbd>: add new submodule
<kbd>e</kbd>: update submodule URL
<kbd>i</kbd>: initialize submodule
<kbd>b</kbd>: view bulk submodule options
<kbd>&lt;c-o&gt;</kbd>: Copy submodule name to clipboard
<kbd>&lt;enter&gt;</kbd>: Enter submodule
<kbd>d</kbd>: Remove submodule
<kbd>u</kbd>: Update submodule
<kbd>n</kbd>: Add new submodule
<kbd>e</kbd>: Update submodule URL
<kbd>i</kbd>: Initialize submodule
<kbd>b</kbd>: View bulk submodule options
</pre>
## Tags
<pre>
<kbd>&lt;space&gt;</kbd>: checkout
<kbd>d</kbd>: delete tag
<kbd>P</kbd>: push tag
<kbd>n</kbd>: create tag
<kbd>g</kbd>: view reset options
<kbd>&lt;enter&gt;</kbd>: view commits
<kbd>&lt;space&gt;</kbd>: Checkout
<kbd>d</kbd>: Delete tag
<kbd>P</kbd>: Push tag
<kbd>n</kbd>: Create tag
<kbd>g</kbd>: View reset options
<kbd>&lt;enter&gt;</kbd>: View commits
</pre>
+54 -54
View File
@@ -14,20 +14,20 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<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>&lt;c-p&gt;</kbd>: view custom patch options
<kbd>m</kbd>: view merge/rebase options
<kbd>&lt;c-p&gt;</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>&lt;c-s&gt;</kbd>: view filter-by-path options
<kbd>&lt;c-s&gt;</kbd>: View filter-by-path options
<kbd>W</kbd>: 差分メニューを開く
<kbd>&lt;c-e&gt;</kbd>: 差分メニューを開く
<kbd>&lt;c-w&gt;</kbd>: 空白文字の差分の表示有無を切り替え
<kbd>z</kbd>: アンドゥ (via reflog) (experimental)
<kbd>&lt;c-z&gt;</kbd>: リドゥ (via reflog) (experimental)
<kbd>P</kbd>: push
<kbd>p</kbd>: pull
<kbd>P</kbd>: Push
<kbd>p</kbd>: Pull
</pre>
## 一覧パネルの操作
@@ -48,11 +48,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<pre>
<kbd>&lt;space&gt;</kbd>: 適用
<kbd>g</kbd>: pop
<kbd>d</kbd>: drop
<kbd>g</kbd>: Pop
<kbd>d</kbd>: Drop
<kbd>n</kbd>: 新しいブランチを作成
<kbd>r</kbd>: Stashを変更
<kbd>&lt;enter&gt;</kbd>: view selected item's files
<kbd>&lt;enter&gt;</kbd>: View selected item's files
</pre>
## Sub-commits
@@ -63,33 +63,33 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>y</kbd>: コミットの情報をコピー
<kbd>o</kbd>: ブラウザでコミットを開く
<kbd>n</kbd>: コミットにブランチを作成
<kbd>g</kbd>: view reset options
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: コミットをコピー (cherry-pick)
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: view selected item's files
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View selected item's files
</pre>
## コミット
<pre>
<kbd>&lt;c-o&gt;</kbd>: コミットのSHAをクリップボードにコピー
<kbd>&lt;c-r&gt;</kbd>: reset cherry-picked (copied) commits selection
<kbd>b</kbd>: view bisect options
<kbd>s</kbd>: squash down
<kbd>f</kbd>: fixup commit
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>b</kbd>: View bisect options
<kbd>s</kbd>: Squash down
<kbd>f</kbd>: Fixup commit
<kbd>r</kbd>: コミットメッセージを変更
<kbd>R</kbd>: エディタでコミットメッセージを編集
<kbd>d</kbd>: コミットを削除
<kbd>e</kbd>: コミットを編集
<kbd>p</kbd>: pick commit (when mid-rebase)
<kbd>p</kbd>: Pick commit (when mid-rebase)
<kbd>F</kbd>: このコミットに対するfixupコミットを作成
<kbd>S</kbd>: squash all 'fixup!' commits above selected commit (autosquash)
<kbd>S</kbd>: Squash all 'fixup!' commits above selected commit (autosquash)
<kbd>&lt;c-j&gt;</kbd>: コミットを1つ下に移動
<kbd>&lt;c-k&gt;</kbd>: コミットを1つ上に移動
<kbd>v</kbd>: コミットを貼り付け (cherry-pick)
<kbd>A</kbd>: ステージされた変更でamendコミット
<kbd>a</kbd>: reset commit author
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: コミットをrevert
<kbd>T</kbd>: タグを作成
<kbd>&lt;c-l&gt;</kbd>: ログメニューを開く
@@ -97,23 +97,23 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>y</kbd>: コミットの情報をコピー
<kbd>o</kbd>: ブラウザでコミットを開く
<kbd>n</kbd>: コミットにブランチを作成
<kbd>g</kbd>: view reset options
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: コミットをコピー (cherry-pick)
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
<kbd>&lt;enter&gt;</kbd>: view selected item's files
<kbd>&lt;enter&gt;</kbd>: View selected item's files
</pre>
## コミットファイル
<pre>
<kbd>&lt;c-o&gt;</kbd>: コミットされたファイル名をクリップボードにコピー
<kbd>c</kbd>: checkout file
<kbd>d</kbd>: discard this commit's changes to this file
<kbd>c</kbd>: Checkout file
<kbd>d</kbd>: Discard this commit's changes to this file
<kbd>o</kbd>: ファイルを開く
<kbd>e</kbd>: ファイルを編集
<kbd>&lt;space&gt;</kbd>: toggle file included in patch
<kbd>a</kbd>: toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>&lt;space&gt;</kbd>: Toggle file included in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>`</kbd>: ファイルツリーの表示を切り替え
</pre>
@@ -134,7 +134,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>n</kbd>: サブモジュールを新規追加
<kbd>e</kbd>: サブモジュールのURLを更新
<kbd>i</kbd>: サブモジュールを初期化
<kbd>b</kbd>: view bulk submodule options
<kbd>b</kbd>: View bulk submodule options
</pre>
## ステータス
@@ -154,7 +154,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>d</kbd>: タグを削除
<kbd>P</kbd>: タグをpush
<kbd>n</kbd>: タグを作成
<kbd>g</kbd>: view reset options
<kbd>g</kbd>: View reset options
<kbd>&lt;enter&gt;</kbd>: コミットを閲覧
</pre>
@@ -162,7 +162,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<pre>
<kbd>&lt;c-o&gt;</kbd>: ファイル名をクリップボードにコピー
<kbd>d</kbd>: view 'discard changes' options
<kbd>d</kbd>: View 'discard changes' options
<kbd>&lt;space&gt;</kbd>: ステージ/アンステージ
<kbd>&lt;c-b&gt;</kbd>: ファイルをフィルタ (ステージ/アンステージ)
<kbd>c</kbd>: 変更をコミット
@@ -174,36 +174,36 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>i</kbd>: ファイルをignore
<kbd>r</kbd>: ファイルをリフレッシュ
<kbd>s</kbd>: 変更をstash
<kbd>S</kbd>: view stash options
<kbd>S</kbd>: View stash options
<kbd>a</kbd>: すべての変更をステージ/アンステージ
<kbd>&lt;enter&gt;</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>&lt;enter&gt;</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
<kbd>M</kbd>: Git mergetoolを開く
<kbd>f</kbd>: Fetch
</pre>
## ブランチ
<pre>
<kbd>&lt;c-o&gt;</kbd>: ブランチ名をクリップボードにコピー
<kbd>i</kbd>: show git-flow options
<kbd>i</kbd>: Show git-flow options
<kbd>&lt;space&gt;</kbd>: チェックアウト
<kbd>n</kbd>: 新しいブランチを作成
<kbd>o</kbd>: Pull Requestを作成
<kbd>O</kbd>: create pull request options
<kbd>O</kbd>: Create pull request options
<kbd>&lt;c-y&gt;</kbd>: Pull RequestのURLをクリップボードにコピー
<kbd>c</kbd>: checkout by name
<kbd>F</kbd>: force checkout
<kbd>c</kbd>: Checkout by name
<kbd>F</kbd>: Force checkout
<kbd>d</kbd>: ブランチを削除
<kbd>r</kbd>: rebase checked-out branch onto this branch
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>M</kbd>: 現在のブランチにマージ
<kbd>f</kbd>: fast-forward this branch from its upstream
<kbd>f</kbd>: Fast-forward this branch from its upstream
<kbd>T</kbd>: タグを作成
<kbd>g</kbd>: view reset options
<kbd>g</kbd>: View reset options
<kbd>R</kbd>: ブランチ名を変更
<kbd>u</kbd>: set/unset upstream
<kbd>u</kbd>: Set/Unset upstream
<kbd>&lt;enter&gt;</kbd>: コミットを閲覧
</pre>
@@ -217,9 +217,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;up&gt;</kbd>: 前のhunkを選択
<kbd>&lt;down&gt;</kbd>: 次のhunkを選択
<kbd>z</kbd>: アンドゥ
<kbd>M</kbd>: git mergetoolを開く
<kbd>&lt;space&gt;</kbd>: pick hunk
<kbd>b</kbd>: pick all hunks
<kbd>M</kbd>: Git mergetoolを開く
<kbd>&lt;space&gt;</kbd>: Pick hunk
<kbd>b</kbd>: Pick all hunks
<kbd>&lt;esc&gt;</kbd>: ファイル一覧に戻る
</pre>
@@ -237,12 +237,12 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;right&gt;</kbd>: 次のhunkを選択
<kbd>v</kbd>: 範囲選択を切り替え
<kbd>V</kbd>: 範囲選択を切り替え
<kbd>a</kbd>: hunk選択を切り替え
<kbd>a</kbd>: Hunk選択を切り替え
<kbd>&lt;c-o&gt;</kbd>: 選択されたテキストをクリップボードにコピー
<kbd>o</kbd>: ファイルを開く
<kbd>e</kbd>: ファイルを編集
<kbd>&lt;space&gt;</kbd>: 行をパッチに追加/削除
<kbd>&lt;esc&gt;</kbd>: exit custom patch builder
<kbd>&lt;esc&gt;</kbd>: Exit custom patch builder
</pre>
## メインパネル (Staging)
@@ -252,7 +252,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;right&gt;</kbd>: 次のhunkを選択
<kbd>v</kbd>: 範囲選択を切り替え
<kbd>V</kbd>: 範囲選択を切り替え
<kbd>a</kbd>: hunk選択を切り替え
<kbd>a</kbd>: Hunk選択を切り替え
<kbd>&lt;c-o&gt;</kbd>: 選択されたテキストをクリップボードにコピー
<kbd>o</kbd>: ファイルを開く
<kbd>e</kbd>: ファイルを編集
@@ -260,7 +260,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;tab&gt;</kbd>: パネルを切り替え
<kbd>&lt;space&gt;</kbd>: 選択行をステージ/アンステージ
<kbd>d</kbd>: 変更を削除 (git reset)
<kbd>E</kbd>: edit hunk
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: 変更をコミット
<kbd>w</kbd>: pre-commitフックを実行せずに変更をコミット
<kbd>C</kbd>: gitエディタを使用して変更をコミット
@@ -289,11 +289,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;space&gt;</kbd>: チェックアウト
<kbd>n</kbd>: 新しいブランチを作成
<kbd>M</kbd>: 現在のブランチにマージ
<kbd>r</kbd>: rebase checked-out branch onto this branch
<kbd>r</kbd>: Rebase checked-out branch onto this branch
<kbd>d</kbd>: ブランチを削除
<kbd>u</kbd>: set as upstream of checked-out branch
<kbd>u</kbd>: Set as upstream of checked-out branch
<kbd>&lt;esc&gt;</kbd>: リモート一覧に戻る
<kbd>g</kbd>: view reset options
<kbd>g</kbd>: View reset options
<kbd>&lt;enter&gt;</kbd>: コミットを閲覧
</pre>
@@ -305,10 +305,10 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>y</kbd>: コミットの情報をコピー
<kbd>o</kbd>: ブラウザでコミットを開く
<kbd>n</kbd>: コミットにブランチを作成
<kbd>g</kbd>: view reset options
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: コミットをコピー (cherry-pick)
<kbd>C</kbd>: コミットを範囲コピー (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: reset cherry-picked (copied) commits selection
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: コミットを閲覧
</pre>
+57 -57
View File
@@ -11,16 +11,16 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;pgup&gt;</kbd>: 메인 패널을 위로 스크롤 (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: 메인 패널을 아래로로 스크롤 (fn+down/shift+j)
<kbd>@</kbd>: 명령어 로그 메뉴 열기
<kbd>}</kbd>: diff 보기의 변경 사항 주위에 표시되는 컨텍스트의 크기를 늘리기
<kbd>{</kbd>: diff 보기의 변경 사항 주위에 표시되는 컨텍스트 크기 줄이기
<kbd>:</kbd>: execute custom command
<kbd>}</kbd>: Diff 보기의 변경 사항 주위에 표시되는 컨텍스트의 크기를 늘리기
<kbd>{</kbd>: Diff 보기의 변경 사항 주위에 표시되는 컨텍스트 크기 줄이기
<kbd>:</kbd>: Execute custom command
<kbd>&lt;c-p&gt;</kbd>: 커스텀 Patch 옵션 보기
<kbd>m</kbd>: view merge/rebase options
<kbd>m</kbd>: View merge/rebase options
<kbd>R</kbd>: 새로고침
<kbd>+</kbd>: 다음 스크린 모드 (normal/half/fullscreen)
<kbd>_</kbd>: 이전 스크린 모드
<kbd>?</kbd>: 매뉴 열기
<kbd>&lt;c-s&gt;</kbd>: view filter-by-path options
<kbd>&lt;c-s&gt;</kbd>: View filter-by-path options
<kbd>W</kbd>: Diff 메뉴 열기
<kbd>&lt;c-e&gt;</kbd>: Diff 메뉴 열기
<kbd>&lt;c-w&gt;</kbd>: 공백문자를 Diff 뷰에서 표시 여부 전환
@@ -30,7 +30,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>p</kbd>: 업데이트
</pre>
## List Panel Navigation
## List panel navigation
<pre>
<kbd>,</kbd>: 이전 페이지
@@ -52,10 +52,10 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>y</kbd>: 커밋 attribute 복사
<kbd>o</kbd>: 브라우저에서 커밋 열기
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
<kbd>g</kbd>: view reset options
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: reset cherry-picked (copied) commits selection
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: 커밋 보기
</pre>
@@ -63,11 +63,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<pre>
<kbd>&lt;space&gt;</kbd>: 적용
<kbd>g</kbd>: pop
<kbd>d</kbd>: drop
<kbd>g</kbd>: Pop
<kbd>d</kbd>: Drop
<kbd>n</kbd>: 새 브랜치 생성
<kbd>r</kbd>: rename stash
<kbd>&lt;enter&gt;</kbd>: view selected item's files
<kbd>r</kbd>: Rename stash
<kbd>&lt;enter&gt;</kbd>: View selected item's files
</pre>
## Sub-commits
@@ -78,11 +78,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>y</kbd>: 커밋 attribute 복사
<kbd>o</kbd>: 브라우저에서 커밋 열기
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
<kbd>g</kbd>: view reset options
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
<kbd>&lt;c-r&gt;</kbd>: reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: view selected item's files
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View selected item's files
</pre>
## 메뉴
@@ -102,9 +102,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;up&gt;</kbd>: 이전 hunk를 선택
<kbd>&lt;down&gt;</kbd>: 다음 hunk를 선택
<kbd>z</kbd>: 되돌리기
<kbd>M</kbd>: git mergetool를 열기
<kbd>&lt;space&gt;</kbd>: pick hunk
<kbd>b</kbd>: pick all hunks
<kbd>M</kbd>: Git mergetool를 열기
<kbd>&lt;space&gt;</kbd>: Pick hunk
<kbd>b</kbd>: Pick all hunks
<kbd>&lt;esc&gt;</kbd>: 파일 목록으로 돌아가기
</pre>
@@ -122,12 +122,12 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;right&gt;</kbd>: 다음 hunk를 선택
<kbd>v</kbd>: 드래그 선택 전환
<kbd>V</kbd>: 드래그 선택 전환
<kbd>a</kbd>: toggle select hunk
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: 선택한 텍스트를 클립보드에 복사
<kbd>o</kbd>: 파일 닫기
<kbd>e</kbd>: 파일 편집
<kbd>&lt;space&gt;</kbd>: line(s)을 패치에 추가/삭제
<kbd>&lt;esc&gt;</kbd>: exit custom patch builder
<kbd>&lt;space&gt;</kbd>: Line(s)을 패치에 추가/삭제
<kbd>&lt;esc&gt;</kbd>: Exit custom patch builder
</pre>
## 메인 패널 (Staging)
@@ -137,7 +137,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;right&gt;</kbd>: 다음 hunk를 선택
<kbd>v</kbd>: 드래그 선택 전환
<kbd>V</kbd>: 드래그 선택 전환
<kbd>a</kbd>: toggle select hunk
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: 선택한 텍스트를 클립보드에 복사
<kbd>o</kbd>: 파일 닫기
<kbd>e</kbd>: 파일 편집
@@ -145,9 +145,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;tab&gt;</kbd>: 패널 전환
<kbd>&lt;space&gt;</kbd>: 선택한 행을 staged / unstaged
<kbd>d</kbd>: 변경을 삭제 (git reset)
<kbd>E</kbd>: edit hunk
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: 커밋 변경내용
<kbd>w</kbd>: commit changes without pre-commit hook
<kbd>w</kbd>: Commit changes without pre-commit hook
<kbd>C</kbd>: Git 편집기를 사용하여 변경 내용을 커밋합니다.
</pre>
@@ -155,7 +155,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<pre>
<kbd>&lt;c-o&gt;</kbd>: 브랜치명을 클립보드에 복사
<kbd>i</kbd>: git-flow 옵션 보기
<kbd>i</kbd>: Git-flow 옵션 보기
<kbd>&lt;space&gt;</kbd>: 체크아웃
<kbd>n</kbd>: 새 브랜치 생성
<kbd>o</kbd>: 풀 리퀘스트 생성
@@ -166,11 +166,11 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>d</kbd>: 브랜치 삭제
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
<kbd>M</kbd>: 현재 브랜치에 병합
<kbd>f</kbd>: fast-forward this branch from its upstream
<kbd>f</kbd>: Fast-forward this branch from its upstream
<kbd>T</kbd>: 태그를 생성
<kbd>g</kbd>: view reset options
<kbd>g</kbd>: View reset options
<kbd>R</kbd>: 브랜치 이름 변경
<kbd>u</kbd>: set/unset upstream
<kbd>u</kbd>: Set/Unset upstream
<kbd>&lt;enter&gt;</kbd>: 커밋 보기
</pre>
@@ -194,7 +194,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>n</kbd>: 새로운 서브모듈 추가
<kbd>e</kbd>: 서브모듈의 URL을 수정
<kbd>i</kbd>: 서브모듈 초기화
<kbd>b</kbd>: view bulk submodule options
<kbd>b</kbd>: View bulk submodule options
</pre>
## 원격
@@ -215,9 +215,9 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>M</kbd>: 현재 브랜치에 병합
<kbd>r</kbd>: 체크아웃된 브랜치를 이 브랜치에 리베이스
<kbd>d</kbd>: 브랜치 삭제
<kbd>u</kbd>: set as upstream of checked-out branch
<kbd>u</kbd>: Set as upstream of checked-out branch
<kbd>&lt;esc&gt;</kbd>: 원격목록으로 돌아가기
<kbd>g</kbd>: view reset options
<kbd>g</kbd>: View reset options
<kbd>&lt;enter&gt;</kbd>: 커밋 보기
</pre>
@@ -225,46 +225,46 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<pre>
<kbd>&lt;c-o&gt;</kbd>: 커밋 SHA를 클립보드에 복사
<kbd>&lt;c-r&gt;</kbd>: reset cherry-picked (copied) commits selection
<kbd>b</kbd>: bisect 옵션 보기
<kbd>s</kbd>: squash down
<kbd>f</kbd>: fixup commit
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>b</kbd>: Bisect 옵션 보기
<kbd>s</kbd>: Squash down
<kbd>f</kbd>: Fixup commit
<kbd>r</kbd>: 커밋메시지 변경
<kbd>R</kbd>: 에디터에서 커밋메시지 수정
<kbd>d</kbd>: 커밋 삭제
<kbd>e</kbd>: 커밋을 편집
<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>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>&lt;c-j&gt;</kbd>: 커밋을 1개 아래로 이동
<kbd>&lt;c-k&gt;</kbd>: 커밋을 1개 위로 이동
<kbd>v</kbd>: 커밋을 붙여넣기 (cherry-pick)
<kbd>A</kbd>: amend commit with staged changes
<kbd>a</kbd>: reset commit author
<kbd>A</kbd>: Amend commit with staged changes
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: 커밋 되돌리기
<kbd>T</kbd>: tag commit
<kbd>T</kbd>: Tag commit
<kbd>&lt;c-l&gt;</kbd>: 로그 메뉴 열기
<kbd>&lt;space&gt;</kbd>: 커밋을 체크아웃
<kbd>y</kbd>: 커밋 attribute 복사
<kbd>o</kbd>: 브라우저에서 커밋 열기
<kbd>n</kbd>: 커밋에서 새 브랜치를 만듭니다.
<kbd>g</kbd>: view reset options
<kbd>g</kbd>: View reset options
<kbd>c</kbd>: 커밋을 복사 (cherry-pick)
<kbd>C</kbd>: 커밋을 범위로 복사 (cherry-pick)
<kbd>&lt;enter&gt;</kbd>: view selected item's files
<kbd>&lt;enter&gt;</kbd>: View selected item's files
</pre>
## 커밋 파일
<pre>
<kbd>&lt;c-o&gt;</kbd>: 커밋한 파일명을 클립보드에 복사
<kbd>c</kbd>: checkout file
<kbd>d</kbd>: discard this commit's changes to this file
<kbd>c</kbd>: Checkout file
<kbd>d</kbd>: Discard this commit's changes to this file
<kbd>o</kbd>: 파일 닫기
<kbd>e</kbd>: 파일 편집
<kbd>&lt;space&gt;</kbd>: toggle file included in patch
<kbd>a</kbd>: toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>&lt;space&gt;</kbd>: Toggle file included in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>`</kbd>: 파일 트리뷰로 전환
</pre>
@@ -282,7 +282,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>d</kbd>: 태그 삭제
<kbd>P</kbd>: 태그를 push
<kbd>n</kbd>: 태그를 생성
<kbd>g</kbd>: view reset options
<kbd>g</kbd>: View reset options
<kbd>&lt;enter&gt;</kbd>: 커밋 보기
</pre>
@@ -290,26 +290,26 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<pre>
<kbd>&lt;c-o&gt;</kbd>: 파일명을 클립보드에 복사
<kbd>d</kbd>: view 'discard changes' options
<kbd>d</kbd>: View 'discard changes' options
<kbd>&lt;space&gt;</kbd>: Staged 전환
<kbd>&lt;c-b&gt;</kbd>: 파일을 필터하기 (Staged/unstaged)
<kbd>c</kbd>: 커밋 변경내용
<kbd>w</kbd>: commit changes without pre-commit hook
<kbd>w</kbd>: Commit changes without pre-commit hook
<kbd>A</kbd>: 마지맛 커밋 수정
<kbd>C</kbd>: Git 편집기를 사용하여 변경 내용을 커밋합니다.
<kbd>e</kbd>: 파일 편집
<kbd>o</kbd>: 파일 닫기
<kbd>i</kbd>: ignore file
<kbd>i</kbd>: Ignore file
<kbd>r</kbd>: 파일 새로고침
<kbd>s</kbd>: 변경사항을 Stash
<kbd>S</kbd>: Stash 옵션 보기
<kbd>a</kbd>: 모든 변경을 Staged/unstaged으로 전환
<kbd>&lt;enter&gt;</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>&lt;enter&gt;</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
<kbd>M</kbd>: Git mergetool를 열기
<kbd>f</kbd>: Fetch
</pre>
## 확인 패널
+210 -210
View File
@@ -4,317 +4,317 @@ _This file is auto-generated. To update, make the changes in the pkg/i18n direct
_Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
## Globale Sneltoetsen
## Globale sneltoetsen
<pre>
<kbd>&lt;c-r&gt;</kbd>: wissel naar een recente repo
<kbd>&lt;pgup&gt;</kbd>: scroll naar beneden vanaf hoofdpaneel (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: scroll naar beneden vanaf hoofdpaneel (fn+down/shift+j)
<kbd>@</kbd>: open command log menu
<kbd>&lt;c-r&gt;</kbd>: Wissel naar een recente repo
<kbd>&lt;pgup&gt;</kbd>: Scroll naar beneden vanaf hoofdpaneel (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: Scroll naar beneden vanaf hoofdpaneel (fn+down/shift+j)
<kbd>@</kbd>: Open command log menu
<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>&lt;c-p&gt;</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>&lt;c-s&gt;</kbd>: bekijk scoping opties
<kbd>W</kbd>: open diff menu
<kbd>&lt;c-e&gt;</kbd>: open diff menu
<kbd>:</kbd>: Voer aangepaste commando uit
<kbd>&lt;c-p&gt;</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>&lt;c-s&gt;</kbd>: Bekijk scoping opties
<kbd>W</kbd>: Open diff menu
<kbd>&lt;c-e&gt;</kbd>: Open diff menu
<kbd>&lt;c-w&gt;</kbd>: Toggle whether or not whitespace changes are shown in the diff view
<kbd>z</kbd>: ongedaan maken (via reflog) (experimenteel)
<kbd>&lt;c-z&gt;</kbd>: redo (via reflog) (experimenteel)
<kbd>P</kbd>: push
<kbd>p</kbd>: pull
<kbd>z</kbd>: Ongedaan maken (via reflog) (experimenteel)
<kbd>&lt;c-z&gt;</kbd>: Redo (via reflog) (experimenteel)
<kbd>P</kbd>: Push
<kbd>p</kbd>: Pull
</pre>
## Lijstpaneel Navigatie
## Lijstpaneel navigatie
<pre>
<kbd>,</kbd>: vorige pagina
<kbd>.</kbd>: volgende pagina
<kbd>&lt;</kbd>: scroll naar boven
<kbd>/</kbd>: start met zoeken
<kbd>&gt;</kbd>: scroll naar beneden
<kbd>H</kbd>: scroll left
<kbd>L</kbd>: scroll right
<kbd>]</kbd>: volgende tabblad
<kbd>[</kbd>: vorige tabblad
<kbd>,</kbd>: Vorige pagina
<kbd>.</kbd>: Volgende pagina
<kbd>&lt;</kbd>: Scroll naar boven
<kbd>/</kbd>: Start met zoeken
<kbd>&gt;</kbd>: Scroll naar beneden
<kbd>H</kbd>: Scroll left
<kbd>L</kbd>: Scroll right
<kbd>]</kbd>: Volgende tabblad
<kbd>[</kbd>: Vorige tabblad
</pre>
## Bestanden
<pre>
<kbd>&lt;c-o&gt;</kbd>: kopieer de bestandsnaam naar het klembord
<kbd>d</kbd>: bekijk 'veranderingen ongedaan maken' opties
<kbd>&lt;space&gt;</kbd>: toggle staged
<kbd>&lt;c-o&gt;</kbd>: Kopieer de bestandsnaam naar het klembord
<kbd>d</kbd>: Bekijk 'veranderingen ongedaan maken' opties
<kbd>&lt;space&gt;</kbd>: Toggle staged
<kbd>&lt;c-b&gt;</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
<kbd>C</kbd>: commit veranderingen met de git editor
<kbd>e</kbd>: verander bestand
<kbd>o</kbd>: open bestand
<kbd>i</kbd>: ignore or exclude file
<kbd>r</kbd>: refresh bestanden
<kbd>s</kbd>: stash-bestanden
<kbd>S</kbd>: bekijk stash opties
<kbd>a</kbd>: toggle staged alle
<kbd>&lt;enter&gt;</kbd>: stage individuele hunks/lijnen
<kbd>g</kbd>: bekijk upstream reset opties
<kbd>D</kbd>: bekijk reset opties
<kbd>`</kbd>: toggle bestandsboom weergave
<kbd>M</kbd>: open external merge tool (git mergetool)
<kbd>f</kbd>: fetch
<kbd>c</kbd>: Commit veranderingen
<kbd>w</kbd>: Commit veranderingen zonder pre-commit hook
<kbd>A</kbd>: Wijzig laatste commit
<kbd>C</kbd>: Commit veranderingen met de git editor
<kbd>e</kbd>: Verander bestand
<kbd>o</kbd>: Open bestand
<kbd>i</kbd>: Ignore or exclude file
<kbd>r</kbd>: Refresh bestanden
<kbd>s</kbd>: Stash-bestanden
<kbd>S</kbd>: Bekijk stash opties
<kbd>a</kbd>: Toggle staged alle
<kbd>&lt;enter&gt;</kbd>: Stage individuele hunks/lijnen
<kbd>g</kbd>: Bekijk upstream reset opties
<kbd>D</kbd>: Bekijk reset opties
<kbd>`</kbd>: Toggle bestandsboom weergave
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>f</kbd>: Fetch
</pre>
## Bevestigingspaneel
<pre>
<kbd>&lt;enter&gt;</kbd>: bevestig
<kbd>&lt;esc&gt;</kbd>: sluiten
<kbd>&lt;enter&gt;</kbd>: Bevestig
<kbd>&lt;esc&gt;</kbd>: Sluiten
</pre>
## Branches
<pre>
<kbd>&lt;c-o&gt;</kbd>: kopieer branch name naar klembord
<kbd>i</kbd>: laat git-flow opties zien
<kbd>&lt;space&gt;</kbd>: uitchecken
<kbd>n</kbd>: nieuwe branch
<kbd>o</kbd>: maak een pull-request
<kbd>O</kbd>: bekijk opties voor pull-aanvraag
<kbd>&lt;c-y&gt;</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
<kbd>r</kbd>: rebase branch
<kbd>M</kbd>: merge in met huidige checked out branch
<kbd>f</kbd>: fast-forward deze branch vanaf zijn upstream
<kbd>T</kbd>: creëer tag
<kbd>g</kbd>: bekijk reset opties
<kbd>R</kbd>: hernoem branch
<kbd>u</kbd>: set/unset upstream
<kbd>&lt;enter&gt;</kbd>: bekijk commits
<kbd>&lt;c-o&gt;</kbd>: Kopieer branch name naar klembord
<kbd>i</kbd>: Laat git-flow opties zien
<kbd>&lt;space&gt;</kbd>: Uitchecken
<kbd>n</kbd>: Nieuwe branch
<kbd>o</kbd>: Maak een pull-request
<kbd>O</kbd>: Bekijk opties voor pull-aanvraag
<kbd>&lt;c-y&gt;</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
<kbd>r</kbd>: Rebase branch
<kbd>M</kbd>: Merge in met huidige checked out branch
<kbd>f</kbd>: Fast-forward deze branch vanaf zijn upstream
<kbd>T</kbd>: Creëer tag
<kbd>g</kbd>: Bekijk reset opties
<kbd>R</kbd>: Hernoem branch
<kbd>u</kbd>: Set/Unset upstream
<kbd>&lt;enter&gt;</kbd>: Bekijk commits
</pre>
## Commit Bericht
## Commit bericht
<pre>
<kbd>&lt;enter&gt;</kbd>: bevestig
<kbd>&lt;esc&gt;</kbd>: sluiten
<kbd>&lt;enter&gt;</kbd>: Bevestig
<kbd>&lt;esc&gt;</kbd>: Sluiten
</pre>
## Commit bestanden
<pre>
<kbd>&lt;c-o&gt;</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>&lt;space&gt;</kbd>: toggle bestand inbegrepen in patch
<kbd>a</kbd>: toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: enter bestand om geselecteerde regels toe te voegen aan de patch
<kbd>`</kbd>: toggle bestandsboom weergave
<kbd>&lt;c-o&gt;</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>&lt;space&gt;</kbd>: Toggle bestand inbegrepen in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter bestand om geselecteerde regels toe te voegen aan de patch
<kbd>`</kbd>: Toggle bestandsboom weergave
</pre>
## Commits
<pre>
<kbd>&lt;c-o&gt;</kbd>: kopieer commit SHA naar klembord
<kbd>&lt;c-r&gt;</kbd>: reset cherry-picked (gekopieerde) commits selectie
<kbd>b</kbd>: view bisect options
<kbd>s</kbd>: squash beneden
<kbd>&lt;c-o&gt;</kbd>: Kopieer commit SHA naar klembord
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (gekopieerde) commits selectie
<kbd>b</kbd>: View bisect options
<kbd>s</kbd>: Squash beneden
<kbd>f</kbd>: Fixup commit
<kbd>r</kbd>: hernoem commit
<kbd>R</kbd>: hernoem commit met editor
<kbd>d</kbd>: verwijder commit
<kbd>e</kbd>: wijzig commit
<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>&lt;c-j&gt;</kbd>: verplaats commit 1 naar beneden
<kbd>&lt;c-k&gt;</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>&lt;c-l&gt;</kbd>: open log menu
<kbd>&lt;space&gt;</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>&lt;enter&gt;</kbd>: bekijk gecommite bestanden
<kbd>r</kbd>: Hernoem commit
<kbd>R</kbd>: Hernoem commit met editor
<kbd>d</kbd>: Verwijder commit
<kbd>e</kbd>: Wijzig commit
<kbd>p</kbd>: Kies commit (wanneer midden in rebase)
<kbd>F</kbd>: Creëer fixup commit
<kbd>S</kbd>: Squash bovenstaande commits
<kbd>&lt;c-j&gt;</kbd>: Verplaats commit 1 naar beneden
<kbd>&lt;c-k&gt;</kbd>: Verplaats commit 1 naar boven
<kbd>v</kbd>: Plak commits (cherry-pick)
<kbd>A</kbd>: Wijzig commit met staged veranderingen
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: Commit ongedaan maken
<kbd>T</kbd>: Tag commit
<kbd>&lt;c-l&gt;</kbd>: Open log menu
<kbd>&lt;space&gt;</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>&lt;enter&gt;</kbd>: Bekijk gecommite bestanden
</pre>
## Menu
<pre>
<kbd>&lt;enter&gt;</kbd>: uitvoeren
<kbd>&lt;esc&gt;</kbd>: sluiten
<kbd>&lt;enter&gt;</kbd>: Uitvoeren
<kbd>&lt;esc&gt;</kbd>: Sluiten
</pre>
## Mergen
<pre>
<kbd>e</kbd>: verander bestand
<kbd>o</kbd>: open bestand
<kbd>&lt;left&gt;</kbd>: selecteer voorgaand conflict
<kbd>&lt;right&gt;</kbd>: selecteer volgende conflict
<kbd>&lt;up&gt;</kbd>: selecteer bovenste hunk
<kbd>&lt;down&gt;</kbd>: selecteer onderste hunk
<kbd>z</kbd>: ongedaan maken
<kbd>M</kbd>: open external merge tool (git mergetool)
<kbd>&lt;space&gt;</kbd>: kies hunk
<kbd>b</kbd>: kies bijde hunks
<kbd>&lt;esc&gt;</kbd>: ga terug naar het bestanden paneel
<kbd>e</kbd>: Verander bestand
<kbd>o</kbd>: Open bestand
<kbd>&lt;left&gt;</kbd>: Selecteer voorgaand conflict
<kbd>&lt;right&gt;</kbd>: Selecteer volgende conflict
<kbd>&lt;up&gt;</kbd>: Selecteer bovenste hunk
<kbd>&lt;down&gt;</kbd>: Selecteer onderste hunk
<kbd>z</kbd>: Ongedaan maken
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>&lt;space&gt;</kbd>: Kies stuk
<kbd>b</kbd>: Kies beide stukken
<kbd>&lt;esc&gt;</kbd>: Ga terug naar het bestanden paneel
</pre>
## Normaal
<pre>
<kbd>mouse wheel down</kbd>: scroll omlaag (fn+up)
<kbd>mouse wheel up</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
## Patch bouwen
<pre>
<kbd>&lt;left&gt;</kbd>: selecteer de vorige hunk
<kbd>&lt;right&gt;</kbd>: selecteer de volgende hunk
<kbd>v</kbd>: toggle drag selecteer
<kbd>V</kbd>: toggle drag selecteer
<kbd>a</kbd>: toggle selecteer hunk
<kbd>&lt;c-o&gt;</kbd>: copy the selected text to the clipboard
<kbd>o</kbd>: open bestand
<kbd>e</kbd>: verander bestand
<kbd>&lt;space&gt;</kbd>: voeg toe/verwijder lijn(en) in patch
<kbd>&lt;esc&gt;</kbd>: sluit lijn-bij-lijn modus
<kbd>&lt;left&gt;</kbd>: Selecteer de vorige hunk
<kbd>&lt;right&gt;</kbd>: Selecteer de volgende hunk
<kbd>v</kbd>: Toggle drag selecteer
<kbd>V</kbd>: Toggle drag selecteer
<kbd>a</kbd>: Toggle selecteer hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Open bestand
<kbd>e</kbd>: Verander bestand
<kbd>&lt;space&gt;</kbd>: Voeg toe/verwijder lijn(en) in patch
<kbd>&lt;esc&gt;</kbd>: Sluit lijn-bij-lijn modus
</pre>
## Reflog
<pre>
<kbd>&lt;c-o&gt;</kbd>: kopieer commit SHA naar klembord
<kbd>&lt;space&gt;</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>&lt;c-r&gt;</kbd>: reset cherry-picked (gekopieerde) commits selectie
<kbd>&lt;enter&gt;</kbd>: bekijk commits
<kbd>&lt;c-o&gt;</kbd>: Kopieer commit SHA naar klembord
<kbd>&lt;space&gt;</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>&lt;c-r&gt;</kbd>: Reset cherry-picked (gekopieerde) commits selectie
<kbd>&lt;enter&gt;</kbd>: Bekijk commits
</pre>
## Remote Branches
## Remote branches
<pre>
<kbd>&lt;c-o&gt;</kbd>: kopieer branch name naar klembord
<kbd>&lt;space&gt;</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>&lt;esc&gt;</kbd>: ga terug naar remotes lijst
<kbd>g</kbd>: bekijk reset opties
<kbd>&lt;enter&gt;</kbd>: bekijk commits
<kbd>&lt;c-o&gt;</kbd>: Kopieer branch name naar klembord
<kbd>&lt;space&gt;</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>&lt;esc&gt;</kbd>: Ga terug naar remotes lijst
<kbd>g</kbd>: Bekijk reset opties
<kbd>&lt;enter&gt;</kbd>: Bekijk commits
</pre>
## Remotes
<pre>
<kbd>f</kbd>: fetch remote
<kbd>n</kbd>: voeg een nieuwe remote toe
<kbd>d</kbd>: verwijder remote
<kbd>e</kbd>: wijzig remote
<kbd>f</kbd>: Fetch remote
<kbd>n</kbd>: Voeg een nieuwe remote toe
<kbd>d</kbd>: Verwijder remote
<kbd>e</kbd>: Wijzig remote
</pre>
## Staging
<pre>
<kbd>&lt;left&gt;</kbd>: selecteer de vorige hunk
<kbd>&lt;right&gt;</kbd>: selecteer de volgende hunk
<kbd>v</kbd>: toggle drag selecteer
<kbd>V</kbd>: toggle drag selecteer
<kbd>a</kbd>: toggle selecteer hunk
<kbd>&lt;c-o&gt;</kbd>: copy the selected text to the clipboard
<kbd>o</kbd>: open bestand
<kbd>e</kbd>: verander bestand
<kbd>&lt;esc&gt;</kbd>: ga terug naar het bestanden paneel
<kbd>&lt;tab&gt;</kbd>: ga naar een ander paneel
<kbd>&lt;space&gt;</kbd>: toggle lijnen staged / unstaged
<kbd>d</kbd>: verwijdert change (git reset)
<kbd>E</kbd>: edit hunk
<kbd>c</kbd>: commit veranderingen
<kbd>w</kbd>: commit veranderingen zonder pre-commit hook
<kbd>C</kbd>: commit veranderingen met de git editor
<kbd>&lt;left&gt;</kbd>: Selecteer de vorige hunk
<kbd>&lt;right&gt;</kbd>: Selecteer de volgende hunk
<kbd>v</kbd>: Toggle drag selecteer
<kbd>V</kbd>: Toggle drag selecteer
<kbd>a</kbd>: Toggle selecteer hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Open bestand
<kbd>e</kbd>: Verander bestand
<kbd>&lt;esc&gt;</kbd>: Ga terug naar het bestanden paneel
<kbd>&lt;tab&gt;</kbd>: Ga naar een ander paneel
<kbd>&lt;space&gt;</kbd>: Toggle lijnen staged / unstaged
<kbd>d</kbd>: Verwijdert change (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: Commit veranderingen
<kbd>w</kbd>: Commit veranderingen zonder pre-commit hook
<kbd>C</kbd>: Commit veranderingen met de git editor
</pre>
## Stash
<pre>
<kbd>&lt;space&gt;</kbd>: toepassen
<kbd>g</kbd>: pop
<kbd>d</kbd>: laten vallen
<kbd>n</kbd>: nieuwe branch
<kbd>r</kbd>: rename stash
<kbd>&lt;enter&gt;</kbd>: bekijk gecommite bestanden
<kbd>&lt;space&gt;</kbd>: Toepassen
<kbd>g</kbd>: Pop
<kbd>d</kbd>: Laten vallen
<kbd>n</kbd>: Nieuwe branch
<kbd>r</kbd>: Rename stash
<kbd>&lt;enter&gt;</kbd>: Bekijk gecommite bestanden
</pre>
## Status
<pre>
<kbd>o</kbd>: open config bestand
<kbd>e</kbd>: verander config bestand
<kbd>u</kbd>: check voor updates
<kbd>&lt;enter&gt;</kbd>: wissel naar een recente repo
<kbd>a</kbd>: alle logs van de branch laten zien
<kbd>o</kbd>: Open config bestand
<kbd>e</kbd>: Verander config bestand
<kbd>u</kbd>: Check voor updates
<kbd>&lt;enter&gt;</kbd>: Wissel naar een recente repo
<kbd>a</kbd>: Alle logs van de branch laten zien
</pre>
## Sub-commits
<pre>
<kbd>&lt;c-o&gt;</kbd>: kopieer commit SHA naar klembord
<kbd>&lt;space&gt;</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>&lt;c-r&gt;</kbd>: reset cherry-picked (gekopieerde) commits selectie
<kbd>&lt;enter&gt;</kbd>: bekijk gecommite bestanden
<kbd>&lt;c-o&gt;</kbd>: Kopieer commit SHA naar klembord
<kbd>&lt;space&gt;</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>&lt;c-r&gt;</kbd>: Reset cherry-picked (gekopieerde) commits selectie
<kbd>&lt;enter&gt;</kbd>: Bekijk gecommite bestanden
</pre>
## Submodules
<pre>
<kbd>&lt;c-o&gt;</kbd>: kopieer submodule naam naar klembord
<kbd>&lt;enter&gt;</kbd>: enter submodule
<kbd>d</kbd>: remove submodule
<kbd>u</kbd>: update submodule
<kbd>n</kbd>: voeg nieuwe submodule toe
<kbd>e</kbd>: update submodule URL
<kbd>i</kbd>: initialiseer submodule
<kbd>b</kbd>: bekijk bulk submodule opties
<kbd>&lt;c-o&gt;</kbd>: Kopieer submodule naam naar klembord
<kbd>&lt;enter&gt;</kbd>: Enter submodule
<kbd>d</kbd>: Remove submodule
<kbd>u</kbd>: Update submodule
<kbd>n</kbd>: Voeg nieuwe submodule toe
<kbd>e</kbd>: Update submodule URL
<kbd>i</kbd>: Initialiseer submodule
<kbd>b</kbd>: Bekijk bulk submodule opties
</pre>
## Tags
<pre>
<kbd>&lt;space&gt;</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>&lt;enter&gt;</kbd>: bekijk commits
<kbd>&lt;space&gt;</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>&lt;enter&gt;</kbd>: Bekijk commits
</pre>
+207 -207
View File
@@ -7,314 +7,314 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
## Globalne
<pre>
<kbd>&lt;c-r&gt;</kbd>: switch to a recent repo
<kbd>&lt;pgup&gt;</kbd>: scroll up main panel (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: scroll down main panel (fn+down/shift+j)
<kbd>@</kbd>: open command log menu
<kbd>&lt;c-r&gt;</kbd>: Switch to a recent repo
<kbd>&lt;pgup&gt;</kbd>: Scroll up main panel (fn+up/shift+k)
<kbd>&lt;pgdown&gt;</kbd>: Scroll down main panel (fn+down/shift+j)
<kbd>@</kbd>: Open command log menu
<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>&lt;c-p&gt;</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>&lt;c-s&gt;</kbd>: view filter-by-path options
<kbd>W</kbd>: open diff menu
<kbd>&lt;c-e&gt;</kbd>: open diff menu
<kbd>:</kbd>: Wykonaj własną komendę
<kbd>&lt;c-p&gt;</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>&lt;c-s&gt;</kbd>: View filter-by-path options
<kbd>W</kbd>: Open diff menu
<kbd>&lt;c-e&gt;</kbd>: Open diff menu
<kbd>&lt;c-w&gt;</kbd>: Toggle whether or not whitespace changes are shown in the diff view
<kbd>z</kbd>: undo (via reflog) (experimental)
<kbd>&lt;c-z&gt;</kbd>: redo (via reflog) (experimental)
<kbd>P</kbd>: push
<kbd>p</kbd>: pull
<kbd>z</kbd>: Undo
<kbd>&lt;c-z&gt;</kbd>: Redo
<kbd>P</kbd>: Push
<kbd>p</kbd>: Pull
</pre>
## List Panel Navigation
## List panel navigation
<pre>
<kbd>,</kbd>: previous page
<kbd>.</kbd>: next page
<kbd>&lt;</kbd>: scroll to top
<kbd>/</kbd>: start search
<kbd>&gt;</kbd>: scroll to bottom
<kbd>H</kbd>: scroll left
<kbd>L</kbd>: scroll right
<kbd>]</kbd>: next tab
<kbd>[</kbd>: previous tab
<kbd>,</kbd>: Previous page
<kbd>.</kbd>: Next page
<kbd>&lt;</kbd>: Scroll to top
<kbd>/</kbd>: Start search
<kbd>&gt;</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
## Commit summary
<pre>
<kbd>&lt;enter&gt;</kbd>: potwierdź
<kbd>&lt;esc&gt;</kbd>: zamknij
<kbd>&lt;enter&gt;</kbd>: Potwierdź
<kbd>&lt;esc&gt;</kbd>: Zamknij
</pre>
## Commity
<pre>
<kbd>&lt;c-o&gt;</kbd>: copy commit SHA to clipboard
<kbd>&lt;c-r&gt;</kbd>: reset cherry-picked (copied) commits selection
<kbd>b</kbd>: view bisect options
<kbd>s</kbd>: ściśnij
<kbd>f</kbd>: napraw commit
<kbd>r</kbd>: zmień nazwę commita
<kbd>R</kbd>: zmień nazwę commita w edytorze
<kbd>d</kbd>: usuń commit
<kbd>e</kbd>: edytuj commit
<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>&lt;c-j&gt;</kbd>: przenieś commit 1 w dół
<kbd>&lt;c-k&gt;</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>&lt;c-l&gt;</kbd>: open log menu
<kbd>&lt;space&gt;</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>&lt;enter&gt;</kbd>: przeglądaj pliki commita
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>b</kbd>: View bisect options
<kbd>s</kbd>: Ściśnij
<kbd>f</kbd>: Napraw commit
<kbd>r</kbd>: Zmień nazwę commita
<kbd>R</kbd>: Zmień nazwę commita w edytorze
<kbd>d</kbd>: Usuń commit
<kbd>e</kbd>: Edytuj commit
<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>&lt;c-j&gt;</kbd>: Przenieś commit 1 w dół
<kbd>&lt;c-k&gt;</kbd>: Przenieś commit 1 w górę
<kbd>v</kbd>: Wklej commity (przebieranie)
<kbd>A</kbd>: Popraw commit zmianami z poczekalni
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: Odwróć commit
<kbd>T</kbd>: Tag commit
<kbd>&lt;c-l&gt;</kbd>: Open log menu
<kbd>&lt;space&gt;</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>&lt;enter&gt;</kbd>: Przeglądaj pliki commita
</pre>
## Confirmation Panel
## Confirmation panel
<pre>
<kbd>&lt;enter&gt;</kbd>: potwierdź
<kbd>&lt;esc&gt;</kbd>: zamknij
<kbd>&lt;enter&gt;</kbd>: Potwierdź
<kbd>&lt;esc&gt;</kbd>: Zamknij
</pre>
## Local Branches
## Local branches
<pre>
<kbd>&lt;c-o&gt;</kbd>: copy branch name to clipboard
<kbd>i</kbd>: show git-flow options
<kbd>&lt;space&gt;</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>&lt;c-y&gt;</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łąź
<kbd>r</kbd>: zmiana bazy gałęzi
<kbd>M</kbd>: scal do obecnej gałęzi
<kbd>f</kbd>: fast-forward this branch from its upstream
<kbd>T</kbd>: create tag
<kbd>g</kbd>: wyświetl opcje resetu
<kbd>R</kbd>: rename branch
<kbd>u</kbd>: set/unset upstream
<kbd>&lt;enter&gt;</kbd>: view commits
<kbd>&lt;c-o&gt;</kbd>: Copy branch name to clipboard
<kbd>i</kbd>: Show git-flow options
<kbd>&lt;space&gt;</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>&lt;c-y&gt;</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łąź
<kbd>r</kbd>: Zmiana bazy gałęzi
<kbd>M</kbd>: Scal do obecnej gałęzi
<kbd>f</kbd>: Fast-forward this branch from its upstream
<kbd>T</kbd>: Create tag
<kbd>g</kbd>: Wyświetl opcje resetu
<kbd>R</kbd>: Rename branch
<kbd>u</kbd>: Set/Unset upstream
<kbd>&lt;enter&gt;</kbd>: View commits
</pre>
## Main Panel (Patch Building)
## Main panel (patch building)
<pre>
<kbd>&lt;left&gt;</kbd>: poprzedni kawałek
<kbd>&lt;right&gt;</kbd>: następny kawałek
<kbd>v</kbd>: toggle drag select
<kbd>V</kbd>: toggle drag select
<kbd>a</kbd>: toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: copy the selected text to the clipboard
<kbd>o</kbd>: otwórz plik
<kbd>e</kbd>: edytuj plik
<kbd>&lt;space&gt;</kbd>: add/remove line(s) to patch
<kbd>&lt;esc&gt;</kbd>: wyście z trybu "linia po linii"
<kbd>&lt;left&gt;</kbd>: Poprzedni kawałek
<kbd>&lt;right&gt;</kbd>: Następny kawałek
<kbd>v</kbd>: Toggle drag select
<kbd>V</kbd>: Toggle drag select
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Otwórz plik
<kbd>e</kbd>: Edytuj plik
<kbd>&lt;space&gt;</kbd>: Add/Remove line(s) to patch
<kbd>&lt;esc&gt;</kbd>: Wyście z trybu "linia po linii"
</pre>
## Menu
<pre>
<kbd>&lt;enter&gt;</kbd>: wykonaj
<kbd>&lt;esc&gt;</kbd>: zamknij
<kbd>&lt;enter&gt;</kbd>: Wykonaj
<kbd>&lt;esc&gt;</kbd>: Zamknij
</pre>
## Pliki
<pre>
<kbd>&lt;c-o&gt;</kbd>: copy the file name to the clipboard
<kbd>d</kbd>: pokaż opcje porzucania zmian
<kbd>&lt;space&gt;</kbd>: przełącz stan poczekalni
<kbd>&lt;c-o&gt;</kbd>: Copy the file name to the clipboard
<kbd>d</kbd>: Pokaż opcje porzucania zmian
<kbd>&lt;space&gt;</kbd>: Przełącz stan poczekalni
<kbd>&lt;c-b&gt;</kbd>: Filter files (staged/unstaged)
<kbd>c</kbd>: Zatwierdź zmiany
<kbd>w</kbd>: zatwierdź zmiany bez skryptu pre-commit
<kbd>w</kbd>: Zatwierdź zmiany bez skryptu pre-commit
<kbd>A</kbd>: Zmień ostatni commit
<kbd>C</kbd>: Zatwierdź zmiany używając edytora
<kbd>e</kbd>: edytuj plik
<kbd>o</kbd>: otwórz plik
<kbd>i</kbd>: ignore or exclude file
<kbd>r</kbd>: odśwież pliki
<kbd>s</kbd>: przechowaj zmiany
<kbd>S</kbd>: wyświetl opcje schowka
<kbd>a</kbd>: przełącz stan poczekalni wszystkich
<kbd>&lt;enter&gt;</kbd>: zatwierdź pojedyncze linie
<kbd>g</kbd>: view upstream reset options
<kbd>D</kbd>: wyświetl opcje resetu
<kbd>`</kbd>: toggle file tree view
<kbd>M</kbd>: open external merge tool (git mergetool)
<kbd>f</kbd>: pobierz
<kbd>e</kbd>: Edytuj plik
<kbd>o</kbd>: Otwórz plik
<kbd>i</kbd>: Ignore or exclude file
<kbd>r</kbd>: Odśwież pliki
<kbd>s</kbd>: Przechowaj zmiany
<kbd>S</kbd>: Wyświetl opcje schowka
<kbd>a</kbd>: Przełącz stan poczekalni wszystkich
<kbd>&lt;enter&gt;</kbd>: Zatwierdź pojedyncze linie
<kbd>g</kbd>: View upstream reset options
<kbd>D</kbd>: Wyświetl opcje resetu
<kbd>`</kbd>: Toggle file tree view
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>f</kbd>: Pobierz
</pre>
## Pliki commita
<pre>
<kbd>&lt;c-o&gt;</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>&lt;space&gt;</kbd>: toggle file included in patch
<kbd>a</kbd>: toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>`</kbd>: toggle file tree view
<kbd>&lt;c-o&gt;</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>&lt;space&gt;</kbd>: Toggle file included in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: Enter file to add selected lines to the patch (or toggle directory collapsed)
<kbd>`</kbd>: Toggle file tree view
</pre>
## Poczekalnia
<pre>
<kbd>&lt;left&gt;</kbd>: poprzedni kawałek
<kbd>&lt;right&gt;</kbd>: następny kawałek
<kbd>v</kbd>: toggle drag select
<kbd>V</kbd>: toggle drag select
<kbd>a</kbd>: toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: copy the selected text to the clipboard
<kbd>o</kbd>: otwórz plik
<kbd>e</kbd>: edytuj plik
<kbd>&lt;esc&gt;</kbd>: wróć do panelu plików
<kbd>&lt;tab&gt;</kbd>: switch to other panel (staged/unstaged changes)
<kbd>&lt;space&gt;</kbd>: toggle line staged / unstaged
<kbd>d</kbd>: delete change (git reset)
<kbd>E</kbd>: edit hunk
<kbd>&lt;left&gt;</kbd>: Poprzedni kawałek
<kbd>&lt;right&gt;</kbd>: Następny kawałek
<kbd>v</kbd>: Toggle drag select
<kbd>V</kbd>: Toggle drag select
<kbd>a</kbd>: Toggle select hunk
<kbd>&lt;c-o&gt;</kbd>: Copy the selected text to the clipboard
<kbd>o</kbd>: Otwórz plik
<kbd>e</kbd>: Edytuj plik
<kbd>&lt;esc&gt;</kbd>: Wróć do panelu plików
<kbd>&lt;tab&gt;</kbd>: Switch to other panel (staged/unstaged changes)
<kbd>&lt;space&gt;</kbd>: Toggle line staged / unstaged
<kbd>d</kbd>: Delete change (git reset)
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: Zatwierdź zmiany
<kbd>w</kbd>: zatwierdź zmiany bez skryptu pre-commit
<kbd>w</kbd>: Zatwierdź zmiany bez skryptu pre-commit
<kbd>C</kbd>: Zatwierdź zmiany używając edytora
</pre>
## Reflog
<pre>
<kbd>&lt;c-o&gt;</kbd>: copy commit SHA to clipboard
<kbd>&lt;space&gt;</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>&lt;c-r&gt;</kbd>: reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: view commits
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>&lt;space&gt;</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>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: View commits
</pre>
## Remote Branches
## Remote branches
<pre>
<kbd>&lt;c-o&gt;</kbd>: copy branch name to clipboard
<kbd>&lt;space&gt;</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>&lt;esc&gt;</kbd>: wróć do listy repozytoriów zdalnych
<kbd>g</kbd>: wyświetl opcje resetu
<kbd>&lt;enter&gt;</kbd>: view commits
<kbd>&lt;c-o&gt;</kbd>: Copy branch name to clipboard
<kbd>&lt;space&gt;</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>&lt;esc&gt;</kbd>: Wróć do listy repozytoriów zdalnych
<kbd>g</kbd>: Wyświetl opcje resetu
<kbd>&lt;enter&gt;</kbd>: View commits
</pre>
## Remotes
<pre>
<kbd>f</kbd>: fetch remote
<kbd>n</kbd>: add new remote
<kbd>d</kbd>: remove remote
<kbd>e</kbd>: edit remote
<kbd>f</kbd>: Fetch remote
<kbd>n</kbd>: Add new remote
<kbd>d</kbd>: Remove remote
<kbd>e</kbd>: Edit remote
</pre>
## Scalanie
<pre>
<kbd>e</kbd>: edytuj plik
<kbd>o</kbd>: otwórz plik
<kbd>&lt;left&gt;</kbd>: poprzedni konflikt
<kbd>&lt;right&gt;</kbd>: następny konflikt
<kbd>&lt;up&gt;</kbd>: wybierz poprzedni kawałek
<kbd>&lt;down&gt;</kbd>: wybierz następny kawałek
<kbd>z</kbd>: cofnij
<kbd>M</kbd>: open external merge tool (git mergetool)
<kbd>&lt;space&gt;</kbd>: wybierz kawałek
<kbd>b</kbd>: wybierz wszystkie kawałki
<kbd>&lt;esc&gt;</kbd>: wróć do panelu plików
<kbd>e</kbd>: Edytuj plik
<kbd>o</kbd>: Otwórz plik
<kbd>&lt;left&gt;</kbd>: Poprzedni konflikt
<kbd>&lt;right&gt;</kbd>: Następny konflikt
<kbd>&lt;up&gt;</kbd>: Wybierz poprzedni kawałek
<kbd>&lt;down&gt;</kbd>: Wybierz następny kawałek
<kbd>z</kbd>: Cofnij
<kbd>M</kbd>: Open external merge tool (git mergetool)
<kbd>&lt;space&gt;</kbd>: Wybierz kawałek
<kbd>b</kbd>: Wybierz oba kawałki
<kbd>&lt;esc&gt;</kbd>: Wróć do panelu plików
</pre>
## Schowek
<pre>
<kbd>&lt;space&gt;</kbd>: zastosuj
<kbd>g</kbd>: wyciągnij
<kbd>d</kbd>: porzuć
<kbd>n</kbd>: nowa gałąź
<kbd>r</kbd>: rename stash
<kbd>&lt;enter&gt;</kbd>: przeglądaj pliki commita
<kbd>&lt;space&gt;</kbd>: Zastosuj
<kbd>g</kbd>: Wyciągnij
<kbd>d</kbd>: Porzuć
<kbd>n</kbd>: Nowa gałąź
<kbd>r</kbd>: Rename stash
<kbd>&lt;enter&gt;</kbd>: Przeglądaj pliki commita
</pre>
## Status
<pre>
<kbd>o</kbd>: otwórz konfigurację
<kbd>e</kbd>: edytuj konfigurację
<kbd>u</kbd>: sprawdź aktualizacje
<kbd>&lt;enter&gt;</kbd>: switch to a recent repo
<kbd>a</kbd>: pokaż wszystkie logi gałęzi
<kbd>o</kbd>: Otwórz konfigurację
<kbd>e</kbd>: Edytuj konfigurację
<kbd>u</kbd>: Sprawdź aktualizacje
<kbd>&lt;enter&gt;</kbd>: Switch to a recent repo
<kbd>a</kbd>: Pokaż wszystkie logi gałęzi
</pre>
## Sub-commits
<pre>
<kbd>&lt;c-o&gt;</kbd>: copy commit SHA to clipboard
<kbd>&lt;space&gt;</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>&lt;c-r&gt;</kbd>: reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: przeglądaj pliki commita
<kbd>&lt;c-o&gt;</kbd>: Copy commit SHA to clipboard
<kbd>&lt;space&gt;</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>&lt;c-r&gt;</kbd>: Reset cherry-picked (copied) commits selection
<kbd>&lt;enter&gt;</kbd>: Przeglądaj pliki commita
</pre>
## Submodules
<pre>
<kbd>&lt;c-o&gt;</kbd>: copy submodule name to clipboard
<kbd>&lt;enter&gt;</kbd>: enter submodule
<kbd>d</kbd>: remove submodule
<kbd>u</kbd>: update submodule
<kbd>n</kbd>: add new submodule
<kbd>e</kbd>: update submodule URL
<kbd>i</kbd>: initialize submodule
<kbd>b</kbd>: view bulk submodule options
<kbd>&lt;c-o&gt;</kbd>: Copy submodule name to clipboard
<kbd>&lt;enter&gt;</kbd>: Enter submodule
<kbd>d</kbd>: Remove submodule
<kbd>u</kbd>: Update submodule
<kbd>n</kbd>: Add new submodule
<kbd>e</kbd>: Update submodule URL
<kbd>i</kbd>: Initialize submodule
<kbd>b</kbd>: View bulk submodule options
</pre>
## Tags
<pre>
<kbd>&lt;space&gt;</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>&lt;enter&gt;</kbd>: view commits
<kbd>&lt;space&gt;</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>&lt;enter&gt;</kbd>: View commits
</pre>
## Zwykłe
<pre>
<kbd>mouse wheel down</kbd>: przewiń w dół (fn+up)
<kbd>mouse wheel up</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>
+9 -9
View File
@@ -49,7 +49,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将提交的 SHA 复制到剪贴板
<kbd>&lt;space&gt;</kbd>: 检出提交
<kbd>y</kbd>: copy commit attribute
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: 在浏览器中打开提交
<kbd>n</kbd>: 从提交创建新分支
<kbd>g</kbd>: 查看重置选项
@@ -78,7 +78,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>T</kbd>: 创建标签
<kbd>g</kbd>: 查看重置选项
<kbd>R</kbd>: 重命名分支
<kbd>u</kbd>: set/unset upstream
<kbd>u</kbd>: Set/Unset upstream
<kbd>&lt;enter&gt;</kbd>: 查看提交
</pre>
@@ -87,7 +87,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<pre>
<kbd>&lt;c-o&gt;</kbd>: 将提交的 SHA 复制到剪贴板
<kbd>&lt;space&gt;</kbd>: 检出提交
<kbd>y</kbd>: copy commit attribute
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: 在浏览器中打开提交
<kbd>n</kbd>: 从提交创建新分支
<kbd>g</kbd>: 查看重置选项
@@ -123,18 +123,18 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>d</kbd>: 删除提交
<kbd>e</kbd>: 编辑提交
<kbd>p</kbd>: 选择提交(变基过程中)
<kbd>F</kbd>: 为此提交创建修正
<kbd>F</kbd>: 创建修正提交
<kbd>S</kbd>: 压缩在所选提交之上的所有“fixup!”提交(自动压缩)
<kbd>&lt;c-j&gt;</kbd>: 下移提交
<kbd>&lt;c-k&gt;</kbd>: 上移提交
<kbd>v</kbd>: 粘贴提交(拣选)
<kbd>A</kbd>: 用已暂存的更改来修补提交
<kbd>a</kbd>: reset commit author
<kbd>a</kbd>: Set/Reset commit author
<kbd>t</kbd>: 还原提交
<kbd>T</kbd>: 标签提交
<kbd>&lt;c-l&gt;</kbd>: 打开日志菜单
<kbd>&lt;space&gt;</kbd>: 检出提交
<kbd>y</kbd>: copy commit attribute
<kbd>y</kbd>: Copy commit attribute
<kbd>o</kbd>: 在浏览器中打开提交
<kbd>n</kbd>: 从提交创建新分支
<kbd>g</kbd>: 查看重置选项
@@ -152,7 +152,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>o</kbd>: 打开文件
<kbd>e</kbd>: 编辑文件
<kbd>&lt;space&gt;</kbd>: 补丁中包含的切换文件
<kbd>a</kbd>: toggle all files included in patch
<kbd>a</kbd>: Toggle all files included in patch
<kbd>&lt;enter&gt;</kbd>: 输入文件以将所选行添加到补丁中(或切换目录折叠)
<kbd>`</kbd>: 切换文件树视图
</pre>
@@ -247,7 +247,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>&lt;tab&gt;</kbd>: 切换到其他面板
<kbd>&lt;space&gt;</kbd>: 切换行暂存状态
<kbd>d</kbd>: 取消变更 (git reset)
<kbd>E</kbd>: edit hunk
<kbd>E</kbd>: Edit hunk
<kbd>c</kbd>: 提交更改
<kbd>w</kbd>: 提交更改而无需预先提交钩子
<kbd>C</kbd>: 提交更改(使用编辑器编辑提交信息)
@@ -291,7 +291,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_
<kbd>g</kbd>: 应用并删除
<kbd>d</kbd>: 删除
<kbd>n</kbd>: 新分支
<kbd>r</kbd>: rename stash
<kbd>r</kbd>: Rename stash
<kbd>&lt;enter&gt;</kbd>: 查看提交的文件
</pre>
+2
View File
@@ -73,3 +73,5 @@ require (
golang.org/x/text v0.8.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
replace github.com/creack/pty => github.com/photostorm/pty v1.1.19-0.20230324012736-6794a5ba0ba0
+3 -5
View File
@@ -6,8 +6,6 @@ github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzbY=
github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aybabtme/humanlog v0.4.1 h1:D8d9um55rrthJsP8IGSHBcti9lTb/XknmDAX6Zy8tek=
@@ -17,9 +15,6 @@ github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI=
github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21 h1:tuijfIjZyjZaHq9xDUh0tNitwXshJpbLkqMOJv4H3do=
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21/go.mod h1:po7NpZ/QiTKzBKyrsEAxwnTamCoh8uDk/egRpQ7siIc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw=
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -131,6 +126,8 @@ github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/photostorm/pty v1.1.19-0.20230324012736-6794a5ba0ba0 h1:ahg2J7KgBYMH1KJNvde12jjJZGXwGq0AdRPztBgX2Rc=
github.com/photostorm/pty v1.1.19-0.20230324012736-6794a5ba0ba0/go.mod h1:KO+FcPtyLAiRC0hJwreJVvfwc7vnNz77UxBTIGHdPVk=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -207,6 +204,7 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220721230656-c6bc011c0c49/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
+15 -1
View File
@@ -10,6 +10,7 @@ import (
"strings"
"github.com/go-errors/errors"
"github.com/sirupsen/logrus"
"github.com/jesseduffield/generics/slices"
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
@@ -22,6 +23,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/env"
"github.com/jesseduffield/lazygit/pkg/gui"
"github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/logs"
"github.com/jesseduffield/lazygit/pkg/updates"
)
@@ -76,6 +78,18 @@ func NewCommon(config config.AppConfigurer) (*common.Common, error) {
}, nil
}
func newLogger(cfg config.AppConfigurer) *logrus.Entry {
if cfg.GetDebug() {
logPath, err := config.LogPath()
if err != nil {
log.Fatal(err)
}
return logs.NewDevelopmentLogger(logPath)
} else {
return logs.NewProductionLogger()
}
}
// NewApp bootstrap a new application
func NewApp(config config.AppConfigurer, common *common.Common) (*App, error) {
app := &App{
@@ -191,7 +205,7 @@ func (app *App) setupRepo() (bool, error) {
}
if shouldInitRepo {
if err := app.OSCommand.Cmd.New("git init " + initialBranchArg).Run(); err != nil {
if err := app.OSCommand.Cmd.New([]string{"git", "init", initialBranchArg}).Run(); err != nil {
return false, err
}
return false, nil
+7 -2
View File
@@ -16,7 +16,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/env"
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
"github.com/jesseduffield/lazygit/pkg/logs"
"github.com/jesseduffield/lazygit/pkg/logs/tail"
"github.com/jesseduffield/lazygit/pkg/secureexec"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo"
@@ -106,7 +106,12 @@ func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTes
}
if cliArgs.TailLogs {
logs.TailLogs()
logPath, err := config.LogPath()
if err != nil {
log.Fatal(err.Error())
}
tail.TailLogs(logPath)
os.Exit(0)
}
-56
View File
@@ -1,56 +0,0 @@
package app
import (
"io"
"log"
"os"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/sirupsen/logrus"
)
func newLogger(config config.AppConfigurer) *logrus.Entry {
var log *logrus.Logger
if config.GetDebug() {
log = newDevelopmentLogger()
} else {
log = newProductionLogger()
}
// highly recommended: tail -f development.log | humanlog
// https://github.com/aybabtme/humanlog
log.Formatter = &logrus.JSONFormatter{}
return log.WithFields(logrus.Fields{})
}
func newProductionLogger() *logrus.Logger {
log := logrus.New()
log.Out = io.Discard
log.SetLevel(logrus.ErrorLevel)
return log
}
func newDevelopmentLogger() *logrus.Logger {
logger := logrus.New()
logger.SetLevel(getLogLevel())
logPath, err := config.LogPath()
if err != nil {
log.Fatal(err)
}
file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o666)
if err != nil {
log.Fatalf("Unable to log to log file: %v", err)
}
logger.SetOutput(file)
return logger
}
func getLogLevel() logrus.Level {
strLevel := os.Getenv("LOG_LEVEL")
level, err := logrus.ParseLevel(strLevel)
if err != nil {
return logrus.DebugLevel
}
return level
}
+3 -3
View File
@@ -54,7 +54,7 @@ func TestGetBindingSections(t *testing.T) {
},
expected: []*bindingSection{
{
title: "Global Keybindings",
title: "Global keybindings",
bindings: []*types.Binding{
{
ViewName: "",
@@ -139,7 +139,7 @@ func TestGetBindingSections(t *testing.T) {
},
expected: []*bindingSection{
{
title: "List Panel Navigation",
title: "List panel navigation",
bindings: []*types.Binding{
{
ViewName: "files",
@@ -215,7 +215,7 @@ func TestGetBindingSections(t *testing.T) {
},
expected: []*bindingSection{
{
title: "List Panel Navigation",
title: "List panel navigation",
bindings: []*types.Binding{
{
ViewName: "files",
+4 -1
View File
@@ -24,6 +24,7 @@ type GitCommand struct {
Commit *git_commands.CommitCommands
Config *git_commands.ConfigCommands
Custom *git_commands.CustomCommands
Diff *git_commands.DiffCommands
File *git_commands.FileCommands
Flow *git_commands.FlowCommands
Patch *git_commands.PatchCommands
@@ -112,6 +113,7 @@ func NewGitCommandAux(
tagCommands := git_commands.NewTagCommands(gitCommon)
commitCommands := git_commands.NewCommitCommands(gitCommon)
customCommands := git_commands.NewCustomCommands(gitCommon)
diffCommands := git_commands.NewDiffCommands(gitCommon)
fileCommands := git_commands.NewFileCommands(gitCommon)
submoduleCommands := git_commands.NewSubmoduleCommands(gitCommon)
workingTreeCommands := git_commands.NewWorkingTreeCommands(gitCommon, submoduleCommands, fileLoader)
@@ -139,6 +141,7 @@ func NewGitCommandAux(
Commit: commitCommands,
Config: configCommands,
Custom: customCommands,
Diff: diffCommands,
File: fileCommands,
Flow: flowCommands,
Patch: patchCommands,
@@ -274,5 +277,5 @@ func findDotGitDir(stat func(string) (os.FileInfo, error), readFile func(filenam
}
func VerifyInGitRepo(osCommand *oscommands.OSCommand) error {
return osCommand.Cmd.New("git rev-parse --git-dir").DontLog().Run()
return osCommand.Cmd.New(git_commands.NewGitCmd("rev-parse").Arg("--git-dir").ToArgv()).DontLog().Run()
}
+2 -6
View File
@@ -30,12 +30,8 @@ func NewGitCmdObjBuilder(log *logrus.Entry, innerBuilder *oscommands.CmdObjBuild
var defaultEnvVar = "GIT_OPTIONAL_LOCKS=0"
func (self *gitCmdObjBuilder) New(cmdStr string) oscommands.ICmdObj {
return self.innerBuilder.New(cmdStr).AddEnvVars(defaultEnvVar)
}
func (self *gitCmdObjBuilder) NewFromArgs(args []string) oscommands.ICmdObj {
return self.innerBuilder.NewFromArgs(args).AddEnvVars(defaultEnvVar)
func (self *gitCmdObjBuilder) New(args []string) oscommands.ICmdObj {
return self.innerBuilder.New(args).AddEnvVars(defaultEnvVar)
}
func (self *gitCmdObjBuilder) NewShell(cmdStr string) oscommands.ICmdObj {
+11 -11
View File
@@ -97,15 +97,15 @@ func (self *BisectCommands) GetInfo() *BisectInfo {
}
func (self *BisectCommands) Reset() error {
cmdStr := NewGitCmd("bisect").Arg("reset").ToString()
cmdArgs := NewGitCmd("bisect").Arg("reset").ToArgv()
return self.cmd.New(cmdStr).StreamOutput().Run()
return self.cmd.New(cmdArgs).StreamOutput().Run()
}
func (self *BisectCommands) Mark(ref string, term string) error {
cmdStr := NewGitCmd("bisect").Arg(term, ref).ToString()
cmdArgs := NewGitCmd("bisect").Arg(term, ref).ToArgv()
return self.cmd.New(cmdStr).
return self.cmd.New(cmdArgs).
IgnoreEmptyError().
StreamOutput().
Run()
@@ -116,9 +116,9 @@ func (self *BisectCommands) Skip(ref string) error {
}
func (self *BisectCommands) Start() error {
cmdStr := NewGitCmd("bisect").Arg("start").ToString()
cmdArgs := NewGitCmd("bisect").Arg("start").ToArgv()
return self.cmd.New(cmdStr).StreamOutput().Run()
return self.cmd.New(cmdArgs).StreamOutput().Run()
}
// tells us whether we've found our problem commit(s). We return a string slice of
@@ -140,8 +140,8 @@ func (self *BisectCommands) IsDone() (bool, []string, error) {
done := false
candidates := []string{}
cmdStr := NewGitCmd("rev-list").Arg(newSha).ToString()
err := self.cmd.New(cmdStr).RunAndProcessLines(func(line string) (bool, error) {
cmdArgs := NewGitCmd("rev-list").Arg(newSha).ToArgv()
err := self.cmd.New(cmdArgs).RunAndProcessLines(func(line string) (bool, error) {
sha := strings.TrimSpace(line)
if status, ok := info.statusMap[sha]; ok {
@@ -171,11 +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 {
cmdStr := NewGitCmd("merge-base").
cmdArgs := NewGitCmd("merge-base").
Arg("--is-ancestor", bisectInfo.GetNewSha(), bisectInfo.GetStartSha()).
ToString()
ToArgv()
err := self.cmd.New(cmdStr).DontLog().Run()
err := self.cmd.New(cmdArgs).DontLog().Run()
return err == nil
}
+50 -46
View File
@@ -6,6 +6,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/mgutz/str"
)
type BranchCommands struct {
@@ -20,11 +21,11 @@ func NewBranchCommands(gitCommon *GitCommon) *BranchCommands {
// New creates a new branch
func (self *BranchCommands) New(name string, base string) error {
cmdStr := NewGitCmd("checkout").
Arg("-b", self.cmd.Quote(name), self.cmd.Quote(base)).
ToString()
cmdArgs := NewGitCmd("checkout").
Arg("-b", name, base).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// CurrentBranchInfo get the current branch information.
@@ -32,7 +33,7 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
branchName, err := self.cmd.New(
NewGitCmd("symbolic-ref").
Arg("--short", "HEAD").
ToString(),
ToArgv(),
).DontLog().RunWithOutput()
if err == nil && branchName != "HEAD\n" {
trimmedBranchName := strings.TrimSpace(branchName)
@@ -44,8 +45,8 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
}
output, err := self.cmd.New(
NewGitCmd("branch").
Arg("--points-at=HEAD", "--format=\"%(HEAD)%00%(objectname)%00%(refname)\"").
ToString(),
Arg("--points-at=HEAD", "--format=%(HEAD)%00%(objectname)%00%(refname)").
ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
return BranchInfo{}, err
@@ -71,12 +72,12 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
// Delete delete branch
func (self *BranchCommands) Delete(branch string, force bool) error {
cmdStr := NewGitCmd("branch").
cmdArgs := NewGitCmd("branch").
ArgIfElse(force, "-D", "-d").
Arg(self.cmd.Quote(branch)).
ToString()
Arg(branch).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// Checkout checks out a branch (or commit), with --force if you set the force arg to true
@@ -86,12 +87,12 @@ type CheckoutOptions struct {
}
func (self *BranchCommands) Checkout(branch string, options CheckoutOptions) error {
cmdStr := NewGitCmd("checkout").
cmdArgs := NewGitCmd("checkout").
ArgIf(options.Force, "--force").
Arg(self.cmd.Quote(branch)).
ToString()
Arg(branch).
ToArgv()
return self.cmd.New(cmdStr).
return self.cmd.New(cmdArgs).
// 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").
@@ -111,31 +112,34 @@ func (self *BranchCommands) GetGraphCmdObj(branchName string) oscommands.ICmdObj
templateValues := map[string]string{
"branchName": self.cmd.Quote(branchName),
}
return self.cmd.New(utils.ResolvePlaceholderString(branchLogCmdTemplate, templateValues)).DontLog()
resolvedTemplate := utils.ResolvePlaceholderString(branchLogCmdTemplate, templateValues)
return self.cmd.New(str.ToArgv(resolvedTemplate)).DontLog()
}
func (self *BranchCommands) SetCurrentBranchUpstream(remoteName string, remoteBranchName string) error {
cmdStr := NewGitCmd("branch").
Arg(fmt.Sprintf("--set-upstream-to=%s/%s", self.cmd.Quote(remoteName), self.cmd.Quote(remoteBranchName))).
ToString()
cmdArgs := NewGitCmd("branch").
Arg(fmt.Sprintf("--set-upstream-to=%s/%s", remoteName, remoteBranchName)).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *BranchCommands) SetUpstream(remoteName string, remoteBranchName string, branchName string) error {
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()
cmdArgs := NewGitCmd("branch").
Arg(fmt.Sprintf("--set-upstream-to=%s/%s", remoteName, remoteBranchName)).
Arg(branchName).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *BranchCommands) UnsetUpstream(branchName string) error {
cmdStr := NewGitCmd("branch").Arg("--unset-upstream", self.cmd.Quote(branchName)).
ToString()
cmdArgs := NewGitCmd("branch").Arg("--unset-upstream", branchName).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *BranchCommands) GetCurrentBranchUpstreamDifferenceCount() (string, string) {
@@ -161,37 +165,37 @@ func (self *BranchCommands) GetCommitDifferences(from, to string) (string, strin
}
func (self *BranchCommands) countDifferences(from, to string) (string, error) {
cmdStr := NewGitCmd("rev-list").
cmdArgs := NewGitCmd("rev-list").
Arg(fmt.Sprintf("%s..%s", from, to)).
Arg("--count").
ToString()
ToArgv()
return self.cmd.New(cmdStr).DontLog().RunWithOutput()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
}
func (self *BranchCommands) IsHeadDetached() bool {
cmdStr := NewGitCmd("symbolic-ref").Arg("-q", "HEAD").ToString()
cmdArgs := NewGitCmd("symbolic-ref").Arg("-q", "HEAD").ToArgv()
err := self.cmd.New(cmdStr).DontLog().Run()
err := self.cmd.New(cmdArgs).DontLog().Run()
return err != nil
}
func (self *BranchCommands) Rename(oldName string, newName string) error {
cmdStr := NewGitCmd("branch").
Arg("--move", self.cmd.Quote(oldName), self.cmd.Quote(newName)).
ToString()
cmdArgs := NewGitCmd("branch").
Arg("--move", oldName, newName).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *BranchCommands) GetRawBranches() (string, error) {
cmdStr := NewGitCmd("for-each-ref").
cmdArgs := NewGitCmd("for-each-ref").
Arg("--sort=-committerdate").
Arg(`--format="%(HEAD)%00%(refname:short)%00%(upstream:short)%00%(upstream:track)"`).
Arg(`--format=%(HEAD)%00%(refname:short)%00%(upstream:short)%00%(upstream:track)`).
Arg("refs/heads").
ToString()
ToArgv()
return self.cmd.New(cmdStr).DontLog().RunWithOutput()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
}
type MergeOpts struct {
@@ -199,16 +203,16 @@ type MergeOpts struct {
}
func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error {
command := NewGitCmd("merge").
cmdArgs := 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()
Arg(branchName).
ToArgv()
return self.cmd.New(command).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *BranchCommands) AllBranchesLogCmdObj() oscommands.ICmdObj {
return self.cmd.New(self.UserConfig.Git.AllBranchesLogCmd).DontLog()
return self.cmd.New(str.ToArgv(self.UserConfig.Git.AllBranchesLogCmd)).DontLog()
}
+24 -23
View File
@@ -21,21 +21,21 @@ func TestBranchGetCommitDifferences(t *testing.T) {
{
"Can't retrieve pushable count",
oscommands.NewFakeRunner(t).
Expect("git rev-list @{u}..HEAD --count", "", errors.New("error")),
ExpectGitArgs([]string{"rev-list", "@{u}..HEAD", "--count"}, "", errors.New("error")),
"?", "?",
},
{
"Can't retrieve pullable count",
oscommands.NewFakeRunner(t).
Expect("git rev-list @{u}..HEAD --count", "1\n", nil).
Expect("git rev-list HEAD..@{u} --count", "", errors.New("error")),
ExpectGitArgs([]string{"rev-list", "@{u}..HEAD", "--count"}, "1\n", nil).
ExpectGitArgs([]string{"rev-list", "HEAD..@{u}", "--count"}, "", errors.New("error")),
"?", "?",
},
{
"Retrieve pullable and pushable count",
oscommands.NewFakeRunner(t).
Expect("git rev-list @{u}..HEAD --count", "1\n", nil).
Expect("git rev-list HEAD..@{u} --count", "2\n", nil),
ExpectGitArgs([]string{"rev-list", "@{u}..HEAD", "--count"}, "1\n", nil).
ExpectGitArgs([]string{"rev-list", "HEAD..@{u}", "--count"}, "2\n", nil),
"1", "2",
},
}
@@ -54,7 +54,7 @@ func TestBranchGetCommitDifferences(t *testing.T) {
func TestBranchNewBranch(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
Expect(`git checkout -b "test" "refs/heads/master"`, "", nil)
ExpectGitArgs([]string{"checkout", "-b", "test", "refs/heads/master"}, "", nil)
instance := buildBranchCommands(commonDeps{runner: runner})
assert.NoError(t, instance.New("test", "refs/heads/master"))
@@ -73,7 +73,7 @@ func TestBranchDeleteBranch(t *testing.T) {
{
"Delete a branch",
false,
oscommands.NewFakeRunner(t).Expect(`git branch -d "test"`, "", nil),
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"branch", "-d", "test"}, "", nil),
func(err error) {
assert.NoError(t, err)
},
@@ -81,7 +81,7 @@ func TestBranchDeleteBranch(t *testing.T) {
{
"Force delete a branch",
true,
oscommands.NewFakeRunner(t).Expect(`git branch -D "test"`, "", nil),
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"branch", "-D", "test"}, "", nil),
func(err error) {
assert.NoError(t, err)
},
@@ -105,14 +105,14 @@ func TestBranchMerge(t *testing.T) {
userConfig *config.UserConfig
opts MergeOpts
branchName string
expected string
expected []string
}{
{
testName: "basic",
userConfig: &config.UserConfig{},
opts: MergeOpts{},
branchName: "mybranch",
expected: `git merge --no-edit "mybranch"`,
expected: []string{"merge", "--no-edit", "mybranch"},
},
{
testName: "merging args",
@@ -125,14 +125,14 @@ func TestBranchMerge(t *testing.T) {
},
opts: MergeOpts{},
branchName: "mybranch",
expected: `git merge --no-edit --merging-args "mybranch"`,
expected: []string{"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"`,
expected: []string{"merge", "--no-edit", "--ff-only", "mybranch"},
},
}
@@ -140,7 +140,7 @@ func TestBranchMerge(t *testing.T) {
s := s
t.Run(s.testName, func(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
Expect(s.expected, "", nil)
ExpectGitArgs(s.expected, "", nil)
instance := buildBranchCommands(commonDeps{runner: runner, userConfig: s.userConfig})
assert.NoError(t, instance.Merge(s.branchName, s.opts))
@@ -160,7 +160,7 @@ func TestBranchCheckout(t *testing.T) {
scenarios := []scenario{
{
"Checkout",
oscommands.NewFakeRunner(t).Expect(`git checkout "test"`, "", nil),
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"checkout", "test"}, "", nil),
func(err error) {
assert.NoError(t, err)
},
@@ -168,7 +168,7 @@ func TestBranchCheckout(t *testing.T) {
},
{
"Checkout forced",
oscommands.NewFakeRunner(t).Expect(`git checkout --force "test"`, "", nil),
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"checkout", "--force", "test"}, "", nil),
func(err error) {
assert.NoError(t, err)
},
@@ -214,7 +214,7 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
scenarios := []scenario{
{
"says we are on the master branch if we are",
oscommands.NewFakeRunner(t).Expect(`git symbolic-ref --short HEAD`, "master", nil),
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"symbolic-ref", "--short", "HEAD"}, "master", nil),
func(info BranchInfo, err error) {
assert.NoError(t, err)
assert.EqualValues(t, "master", info.RefName)
@@ -225,8 +225,9 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
{
"falls back to git `git branch --points-at=HEAD` if symbolic-ref fails",
oscommands.NewFakeRunner(t).
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
Expect(`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`, "*\x006f71c57a8d4bd6c11399c3f55f42c815527a73a4\x00(HEAD detached at 6f71c57a)\n", nil),
ExpectGitArgs([]string{"symbolic-ref", "--short", "HEAD"}, "", errors.New("error")).
ExpectGitArgs([]string{"branch", "--points-at=HEAD", "--format=%(HEAD)%00%(objectname)%00%(refname)"},
"*\x006f71c57a8d4bd6c11399c3f55f42c815527a73a4\x00(HEAD detached at 6f71c57a)\n", nil),
func(info BranchInfo, err error) {
assert.NoError(t, err)
assert.EqualValues(t, "6f71c57a8d4bd6c11399c3f55f42c815527a73a4", info.RefName)
@@ -237,9 +238,9 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
{
"handles a detached head (LANG=zh_CN.UTF-8)",
oscommands.NewFakeRunner(t).
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
Expect(
`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`,
ExpectGitArgs([]string{"symbolic-ref", "--short", "HEAD"}, "", errors.New("error")).
ExpectGitArgs(
[]string{"branch", "--points-at=HEAD", "--format=%(HEAD)%00%(objectname)%00%(refname)"},
"*\x00679b0456f3db7c505b398def84e7d023e5b55a8d\x00(头指针在 679b0456 分离)\n"+
" \x00679b0456f3db7c505b398def84e7d023e5b55a8d\x00refs/heads/master\n",
nil),
@@ -253,8 +254,8 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
{
"bubbles up error if there is one",
oscommands.NewFakeRunner(t).
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
Expect(`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`, "", errors.New("error")),
ExpectGitArgs([]string{"symbolic-ref", "--short", "HEAD"}, "", errors.New("error")).
ExpectGitArgs([]string{"branch", "--points-at=HEAD", "--format=%(HEAD)%00%(objectname)%00%(refname)"}, "", errors.New("error")),
func(info BranchInfo, err error) {
assert.Error(t, err)
assert.EqualValues(t, "", info.RefName)
+57 -53
View File
@@ -22,27 +22,27 @@ func NewCommitCommands(gitCommon *GitCommon) *CommitCommands {
// ResetAuthor resets the author of the topmost commit
func (self *CommitCommands) ResetAuthor() error {
cmdStr := NewGitCmd("commit").
cmdArgs := NewGitCmd("commit").
Arg("--allow-empty", "--only", "--no-edit", "--amend", "--reset-author").
ToString()
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).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 {
cmdStr := NewGitCmd("commit").
Arg("--allow-empty", "--only", "--no-edit", "--amend", "--author="+self.cmd.Quote(value)).
ToString()
cmdArgs := NewGitCmd("commit").
Arg("--allow-empty", "--only", "--no-edit", "--amend", "--author="+value).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// ResetToCommit reset to commit
func (self *CommitCommands) ResetToCommit(sha string, strength string, envVars []string) error {
cmdStr := NewGitCmd("reset").Arg("--"+strength, sha).ToString()
cmdArgs := NewGitCmd("reset").Arg("--"+strength, sha).ToArgv()
return self.cmd.New(cmdStr).
return self.cmd.New(cmdArgs).
// 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").
@@ -55,33 +55,37 @@ func (self *CommitCommands) CommitCmdObj(message string) oscommands.ICmdObj {
skipHookPrefix := self.UserConfig.Git.SkipHookPrefix
cmdStr := NewGitCmd("commit").
cmdArgs := NewGitCmd("commit").
ArgIf(skipHookPrefix != "" && strings.HasPrefix(message, skipHookPrefix), "--no-verify").
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
Arg(messageArgs...).
ToString()
ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *CommitCommands) RewordLastCommitInEditorCmdObj() oscommands.ICmdObj {
return self.cmd.New(NewGitCmd("commit").Arg("--allow-empty", "--amend", "--only").ToArgv())
}
// RewordLastCommit rewords the topmost commit with the given message
func (self *CommitCommands) RewordLastCommit(message string) error {
messageArgs := self.commitMessageArgs(message)
cmdStr := NewGitCmd("commit").
cmdArgs := NewGitCmd("commit").
Arg("--allow-empty", "--amend", "--only").
Arg(messageArgs...).
ToString()
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *CommitCommands) commitMessageArgs(message string) []string {
msg, description, _ := strings.Cut(message, "\n")
args := []string{"-m", self.cmd.Quote(msg)}
args := []string{"-m", msg}
if description != "" {
args = append(args, "-m", self.cmd.Quote(description))
args = append(args, "-m", description)
}
return args
@@ -89,12 +93,12 @@ func (self *CommitCommands) commitMessageArgs(message string) []string {
// runs git commit without the -m argument meaning it will invoke the user's editor
func (self *CommitCommands) CommitEditorCmdObj() oscommands.ICmdObj {
cmdStr := NewGitCmd("commit").
cmdArgs := NewGitCmd("commit").
ArgIf(self.signoffFlag() != "", self.signoffFlag()).
ArgIf(self.verboseFlag() != "", self.verboseFlag()).
ToString()
ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *CommitCommands) signoffFlag() string {
@@ -118,26 +122,26 @@ func (self *CommitCommands) verboseFlag() string {
// Get the subject of the HEAD commit
func (self *CommitCommands) GetHeadCommitMessage() (string, error) {
cmdStr := NewGitCmd("log").Arg("-1", "--pretty=%s").ToString()
cmdArgs := NewGitCmd("log").Arg("-1", "--pretty=%s").ToArgv()
message, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
message, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return strings.TrimSpace(message), err
}
func (self *CommitCommands) GetCommitMessage(commitSha string) (string, error) {
cmdStr := NewGitCmd("rev-list").
cmdArgs := NewGitCmd("rev-list").
Arg("--format=%B", "--max-count=1", commitSha).
ToString()
ToArgv()
messageWithHeader, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
messageWithHeader, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
message := strings.Join(strings.SplitAfter(messageWithHeader, "\n")[1:], "")
return strings.TrimSpace(message), err
}
func (self *CommitCommands) GetCommitDiff(commitSha string) (string, error) {
cmdStr := NewGitCmd("show").Arg("--no-color", commitSha).ToString()
cmdArgs := NewGitCmd("show").Arg("--no-color", commitSha).ToArgv()
diff, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
diff, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return diff, err
}
@@ -147,11 +151,11 @@ type Author struct {
}
func (self *CommitCommands) GetCommitAuthor(commitSha string) (Author, error) {
cmdStr := NewGitCmd("show").
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--pretty=format:'%an%x00%ae'", commitSha).
ToString()
ToArgv()
output, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
output, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
if err != nil {
return Author{}, err
}
@@ -170,21 +174,21 @@ func (self *CommitCommands) GetCommitMessageFirstLine(sha string) (string, error
}
func (self *CommitCommands) GetCommitMessagesFirstLine(shas []string) (string, error) {
cmdStr := NewGitCmd("show").
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--pretty=format:%s").
Arg(shas...).
ToString()
ToArgv()
return self.cmd.New(cmdStr).DontLog().RunWithOutput()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
}
func (self *CommitCommands) GetCommitsOneline(shas []string) (string, error) {
cmdStr := NewGitCmd("show").
cmdArgs := NewGitCmd("show").
Arg("--no-patch", "--oneline").
Arg(shas...).
ToString()
ToArgv()
return self.cmd.New(cmdStr).DontLog().RunWithOutput()
return self.cmd.New(cmdArgs).DontLog().RunWithOutput()
}
// AmendHead amends HEAD with whatever is staged in your working tree
@@ -193,17 +197,17 @@ func (self *CommitCommands) AmendHead() error {
}
func (self *CommitCommands) AmendHeadCmdObj() oscommands.ICmdObj {
cmdStr := NewGitCmd("commit").
cmdArgs := NewGitCmd("commit").
Arg("--amend", "--no-edit", "--allow-empty").
ToString()
ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *CommitCommands) ShowCmdObj(sha string, filterPath string, ignoreWhitespace bool) oscommands.ICmdObj {
contextSize := self.UserConfig.Git.DiffContextSize
cmdStr := NewGitCmd("show").
cmdArgs := NewGitCmd("show").
Arg("--submodule").
Arg("--color="+self.UserConfig.Git.Paging.ColorArg).
Arg(fmt.Sprintf("--unified=%d", contextSize)).
@@ -211,39 +215,39 @@ func (self *CommitCommands) ShowCmdObj(sha string, filterPath string, ignoreWhit
Arg("-p").
Arg(sha).
ArgIf(ignoreWhitespace, "--ignore-all-space").
ArgIf(filterPath != "", "--", self.cmd.Quote(filterPath)).
ToString()
ArgIf(filterPath != "", "--", filterPath).
ToArgv()
return self.cmd.New(cmdStr).DontLog()
return self.cmd.New(cmdArgs).DontLog()
}
// Revert reverts the selected commit by sha
func (self *CommitCommands) Revert(sha string) error {
cmdStr := NewGitCmd("revert").Arg(sha).ToString()
cmdArgs := NewGitCmd("revert").Arg(sha).ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *CommitCommands) RevertMerge(sha string, parentNumber int) error {
cmdStr := NewGitCmd("revert").Arg(sha, "-m", fmt.Sprintf("%d", parentNumber)).
ToString()
cmdArgs := NewGitCmd("revert").Arg(sha, "-m", fmt.Sprintf("%d", parentNumber)).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// CreateFixupCommit creates a commit that fixes up a previous commit
func (self *CommitCommands) CreateFixupCommit(sha string) error {
cmdStr := NewGitCmd("commit").Arg("--fixup=" + sha).ToString()
cmdArgs := NewGitCmd("commit").Arg("--fixup=" + sha).ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// a value of 0 means the head commit, 1 is the parent commit, etc
func (self *CommitCommands) GetCommitMessageFromHistory(value int) (string, error) {
cmdStr := NewGitCmd("log").Arg("-1", fmt.Sprintf("--skip=%d", value), "--pretty=%H").
ToString()
cmdArgs := NewGitCmd("log").Arg("-1", fmt.Sprintf("--skip=%d", value), "--pretty=%H").
ToArgv()
hash, _ := self.cmd.New(cmdStr).DontLog().RunWithOutput()
hash, _ := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
formattedHash := strings.TrimSpace(hash)
if len(formattedHash) == 0 {
return "", ErrInvalidCommitIndex
@@ -24,7 +24,7 @@ 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) {
cmdStr := NewGitCmd("diff").
cmdArgs := NewGitCmd("diff").
Arg("--submodule").
Arg("--no-ext-diff").
Arg("--name-status").
@@ -33,9 +33,9 @@ func (self *CommitFileLoader) GetFilesInDiff(from string, to string, reverse boo
ArgIf(reverse, "-R").
Arg(from).
Arg(to).
ToString()
ToArgv()
filenames, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
filenames, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
if err != nil {
return nil, err
}
+41 -42
View File
@@ -33,10 +33,11 @@ type CommitLoader struct {
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.
// List of main branches that exist in the repo.
// 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
// When nil, we're yet to obtain the list of existing main branches.
// When an empty slice, we've obtained the list and it's empty.
mainBranches []string
}
// making our dependencies explicit for the sake of easier testing
@@ -47,13 +48,13 @@ func NewCommitLoader(
getRebaseMode func() (enums.RebaseMode, error),
) *CommitLoader {
return &CommitLoader{
Common: cmn,
cmd: cmd,
getRebaseMode: getRebaseMode,
readFile: os.ReadFile,
walkFiles: filepath.Walk,
dotGitDir: dotGitDir,
quotedMainBranches: nil,
Common: cmn,
cmd: cmd,
getRebaseMode: getRebaseMode,
readFile: os.ReadFile,
walkFiles: filepath.Walk,
dotGitDir: dotGitDir,
mainBranches: nil,
}
}
@@ -205,7 +206,7 @@ func (self *CommitLoader) getHydratedRebasingCommits(rebaseMode enums.RebaseMode
Config("log.showSignature=false").
Arg("--no-patch", "--oneline", "--abbrev=20", prettyFormat).
Arg(commitShas...).
ToString(),
ToArgv(),
).DontLog()
fullCommits := map[string]*models.Commit{}
@@ -364,11 +365,11 @@ func (self *CommitLoader) setCommitMergedStatuses(refName string, commits []*mod
}
func (self *CommitLoader) getMergeBase(refName string) string {
if self.quotedMainBranches == nil {
self.quotedMainBranches = lo.ToPtr(self.getExistingMainBranches())
if self.mainBranches == nil {
self.mainBranches = self.getExistingMainBranches()
}
if *self.quotedMainBranches == "" {
if len(self.mainBranches) == 0 {
return ""
}
@@ -376,30 +377,29 @@ func (self *CommitLoader) getMergeBase(refName string) string {
// return the base commit for the closest one.
output, err := self.cmd.New(
NewGitCmd("merge-base").Arg(self.cmd.Quote(refName), *self.quotedMainBranches).
ToString(),
NewGitCmd("merge-base").Arg(refName).Arg(self.mainBranches...).
ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
// 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
self.mainBranches = nil
}
return ignoringWarnings(output)
}
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 (self *CommitLoader) getExistingMainBranches() []string {
return lo.FilterMap(self.UserConfig.Git.MainBranches,
func(branchName string, _ int) (string, bool) {
ref := "refs/heads/" + branchName
if err := self.cmd.New(
NewGitCmd("rev-parse").Arg("--verify", "--quiet", ref).ToArgv(),
).DontLog().Run(); err != nil {
return "", false
}
return ref, true
})
}
func ignoringWarnings(commandOutput string) string {
@@ -415,13 +415,12 @@ func ignoringWarnings(commandOutput string) string {
// getFirstPushedCommit returns the first commit SHA which has been pushed to the ref's upstream.
// all commits above this are deemed unpushed and marked as such.
func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
output, err := self.cmd.
New(
NewGitCmd("merge-base").
Arg(self.cmd.Quote(refName)).
Arg(self.cmd.Quote(strings.TrimPrefix(refName, "refs/heads/")) + "@{u}").
ToString(),
).
output, err := self.cmd.New(
NewGitCmd("merge-base").
Arg(refName).
Arg(strings.TrimPrefix(refName, "refs/heads/") + "@{u}").
ToArgv(),
).
DontLog().
RunWithOutput()
if err != nil {
@@ -435,8 +434,8 @@ func (self *CommitLoader) getFirstPushedCommit(refName string) (string, error) {
func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
config := self.UserConfig.Git.Log
cmdStr := NewGitCmd("log").
Arg(self.cmd.Quote(opts.RefName)).
cmdArgs := NewGitCmd("log").
Arg(opts.RefName).
ArgIf(config.Order != "default", "--"+config.Order).
ArgIf(opts.All, "--all").
Arg("--oneline").
@@ -446,10 +445,10 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
ArgIf(opts.FilterPath != "", "--follow").
Arg("--no-show-signature").
Arg("--").
ArgIf(opts.FilterPath != "", self.cmd.Quote(opts.FilterPath)).
ToString()
ArgIf(opts.FilterPath != "", opts.FilterPath).
ToArgv()
return self.cmd.New(cmdStr).DontLog()
return self.cmd.New(cmdArgs).DontLog()
}
const prettyFormat = `--pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s"`
const prettyFormat = `--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s`
+24 -24
View File
@@ -43,8 +43,8 @@ func TestGetCommits(t *testing.T) {
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),
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"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),
expectedCommits: []*models.Commit{},
expectedError: nil,
@@ -55,8 +55,8 @@ func TestGetCommits(t *testing.T) {
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),
ExpectGitArgs([]string{"merge-base", "refs/heads/mybranch", "mybranch@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"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),
expectedCommits: []*models.Commit{},
expectedError: nil,
@@ -69,14 +69,14 @@ func TestGetCommits(t *testing.T) {
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).
ExpectGitArgs([]string{"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).
ExpectGitArgs([]string{"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
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/master"}, "", nil). // this one does
ExpectGitArgs([]string{"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" "refs/heads/master"`, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
ExpectGitArgs([]string{"merge-base", "HEAD", "refs/heads/master"}, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
expectedCommits: []*models.Commit{
{
@@ -202,12 +202,12 @@ func TestGetCommits(t *testing.T) {
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).
ExpectGitArgs([]string{"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).
ExpectGitArgs([]string{"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")),
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/master"}, "", errors.New("error")).
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/main"}, "", errors.New("error")),
expectedCommits: []*models.Commit{
{
@@ -235,16 +235,16 @@ func TestGetCommits(t *testing.T) {
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).
ExpectGitArgs([]string{"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).
ExpectGitArgs([]string{"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).
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/master"}, "", nil).
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/main"}, "", errors.New("error")).
ExpectGitArgs([]string{"rev-parse", "--verify", "--quiet", "refs/heads/develop"}, "", nil).
ExpectGitArgs([]string{"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),
ExpectGitArgs([]string{"merge-base", "HEAD", "refs/heads/master", "refs/heads/develop", "refs/heads/1.0-hotfixes"}, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
expectedCommits: []*models.Commit{
{
@@ -270,8 +270,8 @@ func TestGetCommits(t *testing.T) {
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),
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"log", "HEAD", "--oneline", "--pretty=format:%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s", "--abbrev=40", "--no-show-signature", "--"}, "", nil),
expectedCommits: []*models.Commit{},
expectedError: nil,
@@ -282,8 +282,8 @@ func TestGetCommits(t *testing.T) {
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),
ExpectGitArgs([]string{"merge-base", "HEAD", "HEAD@{u}"}, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
ExpectGitArgs([]string{"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),
expectedCommits: []*models.Commit{},
expectedError: nil,
+33 -30
View File
@@ -53,7 +53,7 @@ func TestCommitCommitCmdObj(t *testing.T) {
message string
configSignoff bool
configSkipHookPrefix string
expected string
expectedArgs []string
}
scenarios := []scenario{
@@ -62,35 +62,35 @@ func TestCommitCommitCmdObj(t *testing.T) {
message: "test",
configSignoff: false,
configSkipHookPrefix: "",
expected: `git commit -m "test"`,
expectedArgs: []string{"commit", "-m", "test"},
},
{
testName: "Commit with --no-verify flag",
message: "WIP: test",
configSignoff: false,
configSkipHookPrefix: "WIP",
expected: `git commit --no-verify -m "WIP: test"`,
expectedArgs: []string{"commit", "--no-verify", "-m", "WIP: test"},
},
{
testName: "Commit with multiline message",
message: "line1\nline2",
configSignoff: false,
configSkipHookPrefix: "",
expected: `git commit -m "line1" -m "line2"`,
expectedArgs: []string{"commit", "-m", "line1", "-m", "line2"},
},
{
testName: "Commit with signoff",
message: "test",
configSignoff: true,
configSkipHookPrefix: "",
expected: `git commit --signoff -m "test"`,
expectedArgs: []string{"commit", "--signoff", "-m", "test"},
},
{
testName: "Commit with signoff and no-verify",
message: "WIP: test",
configSignoff: true,
configSkipHookPrefix: "WIP",
expected: `git commit --no-verify --signoff -m "WIP: test"`,
expectedArgs: []string{"commit", "--no-verify", "--signoff", "-m", "WIP: test"},
},
}
@@ -101,10 +101,11 @@ func TestCommitCommitCmdObj(t *testing.T) {
userConfig.Git.Commit.SignOff = s.configSignoff
userConfig.Git.SkipHookPrefix = s.configSkipHookPrefix
instance := buildCommitCommands(commonDeps{userConfig: userConfig})
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expectedArgs, "", nil)
instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner})
cmdStr := instance.CommitCmdObj(s.message).ToString()
assert.Equal(t, s.expected, cmdStr)
assert.NoError(t, instance.CommitCmdObj(s.message).Run())
runner.CheckForMissingCalls()
})
}
}
@@ -114,7 +115,7 @@ func TestCommitCommitEditorCmdObj(t *testing.T) {
testName string
configSignoff bool
configVerbose string
expected string
expected []string
}
scenarios := []scenario{
@@ -122,31 +123,31 @@ func TestCommitCommitEditorCmdObj(t *testing.T) {
testName: "Commit using editor",
configSignoff: false,
configVerbose: "default",
expected: `git commit`,
expected: []string{"commit"},
},
{
testName: "Commit with --no-verbose flag",
configSignoff: false,
configVerbose: "never",
expected: `git commit --no-verbose`,
expected: []string{"commit", "--no-verbose"},
},
{
testName: "Commit with --verbose flag",
configSignoff: false,
configVerbose: "always",
expected: `git commit --verbose`,
expected: []string{"commit", "--verbose"},
},
{
testName: "Commit with --signoff",
configSignoff: true,
configVerbose: "default",
expected: `git commit --signoff`,
expected: []string{"commit", "--signoff"},
},
{
testName: "Commit with --signoff and --no-verbose",
configSignoff: true,
configVerbose: "never",
expected: `git commit --signoff --no-verbose`,
expected: []string{"commit", "--signoff", "--no-verbose"},
},
}
@@ -157,10 +158,11 @@ func TestCommitCommitEditorCmdObj(t *testing.T) {
userConfig.Git.Commit.SignOff = s.configSignoff
userConfig.Git.Commit.Verbose = s.configVerbose
instance := buildCommitCommands(commonDeps{userConfig: userConfig})
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil)
instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner})
cmdStr := instance.CommitEditorCmdObj().ToString()
assert.Equal(t, s.expected, cmdStr)
assert.NoError(t, instance.CommitEditorCmdObj().Run())
runner.CheckForMissingCalls()
})
}
}
@@ -178,7 +180,7 @@ func TestCommitCreateFixupCommit(t *testing.T) {
testName: "valid case",
sha: "12345",
runner: oscommands.NewFakeRunner(t).
Expect(`git commit --fixup=12345`, "", nil),
ExpectGitArgs([]string{"commit", "--fixup=12345"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@@ -201,7 +203,7 @@ func TestCommitShowCmdObj(t *testing.T) {
filterPath string
contextSize int
ignoreWhitespace bool
expected string
expected []string
}
scenarios := []scenario{
@@ -210,28 +212,28 @@ func TestCommitShowCmdObj(t *testing.T) {
filterPath: "",
contextSize: 3,
ignoreWhitespace: false,
expected: "git show --submodule --color=always --unified=3 --stat -p 1234567890",
expected: []string{"show", "--submodule", "--color=always", "--unified=3", "--stat", "-p", "1234567890"},
},
{
testName: "Default case with filter path",
filterPath: "file.txt",
contextSize: 3,
ignoreWhitespace: false,
expected: `git show --submodule --color=always --unified=3 --stat -p 1234567890 -- "file.txt"`,
expected: []string{"show", "--submodule", "--color=always", "--unified=3", "--stat", "-p", "1234567890", "--", "file.txt"},
},
{
testName: "Show diff with custom context size",
filterPath: "",
contextSize: 77,
ignoreWhitespace: false,
expected: "git show --submodule --color=always --unified=77 --stat -p 1234567890",
expected: []string{"show", "--submodule", "--color=always", "--unified=77", "--stat", "-p", "1234567890"},
},
{
testName: "Show diff, ignoring whitespace",
filterPath: "",
contextSize: 77,
ignoreWhitespace: true,
expected: "git show --submodule --color=always --unified=77 --stat -p 1234567890 --ignore-all-space",
expected: []string{"show", "--submodule", "--color=always", "--unified=77", "--stat", "-p", "1234567890", "--ignore-all-space"},
},
}
@@ -241,10 +243,11 @@ func TestCommitShowCmdObj(t *testing.T) {
userConfig := config.GetDefaultConfig()
userConfig.Git.DiffContextSize = s.contextSize
instance := buildCommitCommands(commonDeps{userConfig: userConfig})
runner := oscommands.NewFakeRunner(t).ExpectGitArgs(s.expected, "", nil)
instance := buildCommitCommands(commonDeps{userConfig: userConfig, runner: runner})
cmdStr := instance.ShowCmdObj("1234567890", s.filterPath, s.ignoreWhitespace).ToString()
assert.Equal(t, s.expected, cmdStr)
assert.NoError(t, instance.ShowCmdObj("1234567890", s.filterPath, s.ignoreWhitespace).Run())
runner.CheckForMissingCalls()
})
}
}
@@ -283,7 +286,7 @@ Merge pull request #1750 from mark2185/fix-issue-template
s := s
t.Run(s.testName, func(t *testing.T) {
instance := buildCommitCommands(commonDeps{
runner: oscommands.NewFakeRunner(t).Expect("git rev-list --format=%B --max-count=1 deadbeef", s.input, nil),
runner: oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1", "deadbeef"}, s.input, nil),
})
output, err := instance.GetCommitMessage("deadbeef")
@@ -304,14 +307,14 @@ func TestGetCommitMessageFromHistory(t *testing.T) {
scenarios := []scenario{
{
"Empty message",
oscommands.NewFakeRunner(t).Expect("git log -1 --skip=2 --pretty=%H", "", nil).Expect("git rev-list --format=%B --max-count=1 ", "", nil),
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "", nil).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1"}, "", nil),
func(output string, err error) {
assert.Error(t, err)
},
},
{
"Default case to retrieve a commit in history",
oscommands.NewFakeRunner(t).Expect("git log -1 --skip=2 --pretty=%H", "sha3 \n", nil).Expect("git rev-list --format=%B --max-count=1 sha3", `commit sha3
oscommands.NewFakeRunner(t).ExpectGitArgs([]string{"log", "-1", "--skip=2", "--pretty=%H"}, "sha3 \n", nil).ExpectGitArgs([]string{"rev-list", "--format=%B", "--max-count=1", "sha3"}, `commit sha3
use generics to DRY up context code`, nil),
func(output string, err error) {
assert.NoError(t, err)
+3 -1
View File
@@ -1,5 +1,7 @@
package git_commands
import "github.com/mgutz/str"
type CustomCommands struct {
*GitCommon
}
@@ -14,5 +16,5 @@ func NewCustomCommands(gitCommon *GitCommon) *CustomCommands {
// If you want to run a new command, try finding a place for it in one of the neighbouring
// files, or creating a new BlahCommands struct to hold it.
func (self *CustomCommands) RunWithOutput(cmdStr string) (string, error) {
return self.cmd.New(cmdStr).RunWithOutput()
return self.cmd.New(str.ToArgv(cmdStr)).RunWithOutput()
}
+19
View File
@@ -0,0 +1,19 @@
package git_commands
import "github.com/jesseduffield/lazygit/pkg/commands/oscommands"
type DiffCommands struct {
*GitCommon
}
func NewDiffCommands(gitCommon *GitCommon) *DiffCommands {
return &DiffCommands{
GitCommon: gitCommon,
}
}
func (self *DiffCommands) DiffCmdObj(diffArgs []string) oscommands.ICmdObj {
return self.cmd.New(
NewGitCmd("diff").Arg("--submodule", "--no-ext-diff", "--color").Arg(diffArgs...).ToArgv(),
)
}
+1 -1
View File
@@ -45,7 +45,7 @@ func (self *FileCommands) GetEditCmdStrLegacy(filename string, lineNumber int) (
editor = self.os.Getenv("EDITOR")
}
if editor == "" {
if err := self.cmd.New("which vi").DontLog().Run(); err == nil {
if err := self.cmd.New([]string{"which", "vi"}).DontLog().Run(); err == nil {
editor = "vi"
}
}
+3 -3
View File
@@ -82,14 +82,14 @@ type FileStatus struct {
}
func (c *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
cmdStr := NewGitCmd("status").
cmdArgs := NewGitCmd("status").
Arg(opts.UntrackedFilesArg).
Arg("--porcelain").
Arg("-z").
ArgIf(opts.NoRenames, "--no-renames").
ToString()
ToArgv()
statusLines, _, err := c.cmd.New(cmdStr).DontLog().RunWithOutputs()
statusLines, _, err := c.cmd.New(cmdArgs).DontLog().RunWithOutputs()
if err != nil {
return []FileStatus{}, err
}
@@ -20,14 +20,13 @@ func TestFileGetStatusFiles(t *testing.T) {
{
"No files found",
oscommands.NewFakeRunner(t).
Expect(`git status --untracked-files=yes --porcelain -z`, "", nil),
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"}, "", nil),
[]*models.File{},
},
{
"Several files found",
oscommands.NewFakeRunner(t).
Expect(
`git status --untracked-files=yes --porcelain -z`,
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
"MM file1.txt\x00A file3.txt\x00AM file2.txt\x00?? file4.txt\x00UU file5.txt",
nil,
),
@@ -102,7 +101,7 @@ func TestFileGetStatusFiles(t *testing.T) {
{
"File with new line char",
oscommands.NewFakeRunner(t).
Expect(`git status --untracked-files=yes --porcelain -z`, "MM a\nb.txt", nil),
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"}, "MM a\nb.txt", nil),
[]*models.File{
{
Name: "a\nb.txt",
@@ -122,8 +121,7 @@ func TestFileGetStatusFiles(t *testing.T) {
{
"Renamed files",
oscommands.NewFakeRunner(t).
Expect(
`git status --untracked-files=yes --porcelain -z`,
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
"R after1.txt\x00before1.txt\x00RM after2.txt\x00before2.txt",
nil,
),
@@ -161,8 +159,7 @@ func TestFileGetStatusFiles(t *testing.T) {
{
"File with arrow in name",
oscommands.NewFakeRunner(t).
Expect(
`git status --untracked-files=yes --porcelain -z`,
ExpectGitArgs([]string{"status", "--untracked-files=yes", "--porcelain", "-z"},
`?? a -> b.txt`,
nil,
),
+3 -3
View File
@@ -27,7 +27,7 @@ func TestEditFileCmdStrLegacy(t *testing.T) {
configEditCommand: "",
configEditCommandTemplate: "{{editor}} {{filename}}",
runner: oscommands.NewFakeRunner(t).
Expect(`which vi`, "", errors.New("error")),
ExpectArgs([]string{"which", "vi"}, "", errors.New("error")),
getenv: func(env string) string {
return ""
},
@@ -105,7 +105,7 @@ func TestEditFileCmdStrLegacy(t *testing.T) {
configEditCommand: "",
configEditCommandTemplate: "{{editor}} {{filename}}",
runner: oscommands.NewFakeRunner(t).
Expect(`which vi`, "/usr/bin/vi", nil),
ExpectArgs([]string{"which", "vi"}, "/usr/bin/vi", nil),
getenv: func(env string) string {
return ""
},
@@ -120,7 +120,7 @@ func TestEditFileCmdStrLegacy(t *testing.T) {
configEditCommand: "",
configEditCommandTemplate: "{{editor}} {{filename}}",
runner: oscommands.NewFakeRunner(t).
Expect(`which vi`, "/usr/bin/vi", nil),
ExpectArgs([]string{"which", "vi"}, "/usr/bin/vi", nil),
getenv: func(env string) string {
return ""
},
+4 -4
View File
@@ -49,13 +49,13 @@ func (self *FlowCommands) FinishCmdObj(branchName string) (oscommands.ICmdObj, e
return nil, errors.New(self.Tr.NotAGitFlowBranch)
}
cmdStr := NewGitCmd("flow").Arg(branchType, "finish", suffix).ToString()
cmdArgs := NewGitCmd("flow").Arg(branchType, "finish", suffix).ToArgv()
return self.cmd.New(cmdStr), nil
return self.cmd.New(cmdArgs), nil
}
func (self *FlowCommands) StartCmdObj(branchType string, name string) oscommands.ICmdObj {
cmdStr := NewGitCmd("flow").Arg(branchType, "start", name).ToString()
cmdArgs := NewGitCmd("flow").Arg(branchType, "start", name).ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
+8 -8
View File
@@ -12,13 +12,13 @@ func TestStartCmdObj(t *testing.T) {
testName string
branchType string
name string
expected string
expected []string
}{
{
testName: "basic",
branchType: "feature",
name: "test",
expected: "git flow feature start test",
expected: []string{"git", "flow", "feature", "start", "test"},
},
}
@@ -28,7 +28,7 @@ func TestStartCmdObj(t *testing.T) {
instance := buildFlowCommands(commonDeps{})
assert.Equal(t,
instance.StartCmdObj(s.branchType, s.name).ToString(),
instance.StartCmdObj(s.branchType, s.name).Args(),
s.expected,
)
})
@@ -39,28 +39,28 @@ func TestFinishCmdObj(t *testing.T) {
scenarios := []struct {
testName string
branchName string
expected string
expected []string
expectedError string
gitConfigMockResponses map[string]string
}{
{
testName: "not a git flow branch",
branchName: "mybranch",
expected: "",
expected: nil,
expectedError: "This does not seem to be a git flow branch",
gitConfigMockResponses: nil,
},
{
testName: "feature branch without config",
branchName: "feature/mybranch",
expected: "",
expected: nil,
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",
expected: []string{"git", "flow", "feature", "finish", "mybranch"},
expectedError: "",
gitConfigMockResponses: map[string]string{
"--local --get-regexp gitflow.prefix": "gitflow.prefix.feature feature/",
@@ -85,7 +85,7 @@ func TestFinishCmdObj(t *testing.T) {
}
} else {
assert.NoError(t, err)
assert.Equal(t, cmd.ToString(), s.expected)
assert.Equal(t, cmd.Args(), s.expected)
}
})
}
@@ -49,6 +49,10 @@ func (self *GitCommandBuilder) RepoPath(value string) *GitCommandBuilder {
return self
}
func (self *GitCommandBuilder) ToArgv() []string {
return append([]string{"git"}, self.args...)
}
func (self *GitCommandBuilder) ToString() string {
return "git " + strings.Join(self.args, " ")
return strings.Join(self.ToArgv(), " ")
}
@@ -8,8 +8,8 @@ import (
func TestGitCommandBuilder(t *testing.T) {
scenarios := []struct {
input string
expected string
input []string
expected []string
}{
{
input: NewGitCmd("push").
@@ -17,36 +17,36 @@ func TestGitCommandBuilder(t *testing.T) {
Arg("--set-upstream").
Arg("origin").
Arg("master").
ToString(),
expected: "git push --force-with-lease --set-upstream origin master",
ToArgv(),
expected: []string{"git", "push", "--force-with-lease", "--set-upstream", "origin", "master"},
},
{
input: NewGitCmd("push").ArgIf(true, "--test").ToString(),
expected: "git push --test",
input: NewGitCmd("push").ArgIf(true, "--test").ToArgv(),
expected: []string{"git", "push", "--test"},
},
{
input: NewGitCmd("push").ArgIf(false, "--test").ToString(),
expected: "git push",
input: NewGitCmd("push").ArgIf(false, "--test").ToArgv(),
expected: []string{"git", "push"},
},
{
input: NewGitCmd("push").ArgIfElse(true, "-b", "-a").ToString(),
expected: "git push -b",
input: NewGitCmd("push").ArgIfElse(true, "-b", "-a").ToArgv(),
expected: []string{"git", "push", "-b"},
},
{
input: NewGitCmd("push").ArgIfElse(false, "-a", "-b").ToString(),
expected: "git push -b",
input: NewGitCmd("push").ArgIfElse(false, "-a", "-b").ToArgv(),
expected: []string{"git", "push", "-b"},
},
{
input: NewGitCmd("push").Arg("-a", "-b").ToString(),
expected: "git push -a -b",
input: NewGitCmd("push").Arg("-a", "-b").ToArgv(),
expected: []string{"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").Config("user.name=foo").Config("user.email=bar").ToArgv(),
expected: []string{"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",
input: NewGitCmd("push").RepoPath("a/b/c").ToArgv(),
expected: []string{"git", "-C", "a/b/c", "push"},
},
}
+13 -10
View File
@@ -69,15 +69,15 @@ func (self *PatchCommands) ApplyPatch(patch string, opts ApplyPatchOpts) error {
}
func (self *PatchCommands) applyPatchFile(filepath string, opts ApplyPatchOpts) error {
cmdStr := NewGitCmd("apply").
cmdArgs := NewGitCmd("apply").
ArgIf(opts.ThreeWay, "--3way").
ArgIf(opts.Cached, "--cached").
ArgIf(opts.Index, "--index").
ArgIf(opts.Reverse, "--reverse").
Arg(self.cmd.Quote(filepath)).
ToString()
Arg(filepath).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *PatchCommands) SaveTemporaryPatch(patch string) (string, error) {
@@ -91,7 +91,7 @@ func (self *PatchCommands) SaveTemporaryPatch(patch string) (string, error) {
// 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 {
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, commitIndex, false); err != nil {
return err
}
@@ -117,7 +117,10 @@ func (self *PatchCommands) DeletePatchesFromCommit(commits []*models.Commit, com
func (self *PatchCommands) MovePatchToSelectedCommit(commits []*models.Commit, sourceCommitIdx int, destinationCommitIdx int) error {
if sourceCommitIdx < destinationCommitIdx {
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, destinationCommitIdx); err != nil {
// Passing true for keepCommitsThatBecomeEmpty: if the moved-from
// commit becomes empty, we want to keep it, mainly for consistency with
// moving the patch to a *later* commit, which behaves the same.
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, destinationCommitIdx, true); err != nil {
return err
}
@@ -223,7 +226,7 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId
}
}
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, commitIdx); err != nil {
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, commitIdx, false); err != nil {
return err
}
@@ -272,7 +275,7 @@ func (self *PatchCommands) MovePatchIntoIndex(commits []*models.Commit, commitId
}
func (self *PatchCommands) PullPatchIntoNewCommit(commits []*models.Commit, commitIdx int) error {
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, commitIdx); err != nil {
if err := self.rebase.BeginInteractiveRebaseForCommit(commits, commitIdx, false); err != nil {
return err
}
@@ -317,7 +320,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) {
cmdStr := NewGitCmd("diff").Arg("HEAD.." + commit.Sha).ToString()
cmdArgs := NewGitCmd("diff").Arg("HEAD.." + commit.Sha).ToArgv()
return self.cmd.New(cmdStr).RunWithOutput()
return self.cmd.New(cmdArgs).RunWithOutput()
}
+13 -11
View File
@@ -3,7 +3,6 @@ package git_commands
import (
"fmt"
"os"
"regexp"
"testing"
"github.com/go-errors/errors"
@@ -11,21 +10,22 @@ import (
"github.com/stretchr/testify/assert"
)
func TestWorkingTreeApplyPatch(t *testing.T) {
func TestPatchApplyPatch(t *testing.T) {
type scenario struct {
testName string
opts ApplyPatchOpts
runner *oscommands.FakeCmdObjRunner
test func(error)
}
expectFn := func(regexStr string, errToReturn error) func(cmdObj oscommands.ICmdObj) (string, error) {
// expectedArgs excludes the last argument which is an indeterminate filename
expectFn := func(expectedArgs []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))
args := cmdObj.Args()
filename := matches[1]
assert.Equal(t, len(args), len(expectedArgs)+1, fmt.Sprintf("unexpected command: %s", cmdObj.ToString()))
filename := args[len(args)-1]
content, err := os.ReadFile(filename)
assert.NoError(t, err)
@@ -39,16 +39,18 @@ func TestWorkingTreeApplyPatch(t *testing.T) {
scenarios := []scenario{
{
testName: "valid case",
opts: ApplyPatchOpts{Cached: true},
runner: oscommands.NewFakeRunner(t).
ExpectFunc(expectFn(`git apply --cached "(.*)"`, nil)),
ExpectFunc(expectFn([]string{"git", "apply", "--cached"}, nil)),
test: func(err error) {
assert.NoError(t, err)
},
},
{
testName: "command returns error",
opts: ApplyPatchOpts{Cached: true},
runner: oscommands.NewFakeRunner(t).
ExpectFunc(expectFn(`git apply --cached "(.*)"`, errors.New("error"))),
ExpectFunc(expectFn([]string{"git", "apply", "--cached"}, errors.New("error"))),
test: func(err error) {
assert.Error(t, err)
},
@@ -59,7 +61,7 @@ func TestWorkingTreeApplyPatch(t *testing.T) {
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.test(instance.ApplyPatch("test", s.opts))
s.runner.CheckForMissingCalls()
})
}
+28 -24
View File
@@ -41,7 +41,7 @@ func (self *RebaseCommands) RewordCommit(commits []*models.Commit, index int, me
return self.commit.RewordLastCommit(message)
}
err := self.BeginInteractiveRebaseForCommit(commits, index)
err := self.BeginInteractiveRebaseForCommit(commits, index, false)
if err != nil {
return err
}
@@ -86,7 +86,7 @@ func (self *RebaseCommands) GenericAmend(commits []*models.Commit, index int, f
return f()
}
err := self.BeginInteractiveRebaseForCommit(commits, index)
err := self.BeginInteractiveRebaseForCommit(commits, index, false)
if err != nil {
return err
}
@@ -165,9 +165,10 @@ func logTodoChanges(changes []daemon.ChangeTodoAction) string {
}
type PrepareInteractiveRebaseCommandOpts struct {
baseShaOrRoot string
instruction daemon.Instruction
overrideEditor bool
baseShaOrRoot string
instruction daemon.Instruction
overrideEditor bool
keepCommitsThatBecomeEmpty bool
}
// PrepareInteractiveRebaseCommand returns the cmd for an interactive rebase
@@ -176,24 +177,24 @@ type PrepareInteractiveRebaseCommandOpts struct {
func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteractiveRebaseCommandOpts) oscommands.ICmdObj {
ex := oscommands.GetLazygitPath()
cmdStr := NewGitCmd("rebase").
cmdArgs := NewGitCmd("rebase").
Arg("--interactive").
Arg("--autostash").
Arg("--keep-empty").
ArgIf(!self.version.IsOlderThan(2, 26, 0), "--empty=keep").
ArgIf(opts.keepCommitsThatBecomeEmpty && !self.version.IsOlderThan(2, 26, 0), "--empty=keep").
Arg("--no-autosquash").
ArgIf(!self.version.IsOlderThan(2, 22, 0), "--rebase-merges").
Arg(opts.baseShaOrRoot).
ToString()
ToArgv()
debug := "FALSE"
if self.Debug {
debug = "TRUE"
}
self.Log.WithField("command", cmdStr).Debug("RunCommand")
self.Log.WithField("command", cmdArgs).Debug("RunCommand")
cmdObj := self.cmd.New(cmdStr)
cmdObj := self.cmd.New(cmdArgs)
gitSequenceEditor := ex
@@ -226,8 +227,8 @@ func (self *RebaseCommands) AmendTo(commits []*models.Commit, commitIndex int) e
}
// Get the sha of the commit we just created
cmdStr := NewGitCmd("rev-parse").Arg("--verify", "HEAD").ToString()
fixupSha, err := self.cmd.New(cmdStr).RunWithOutput()
cmdArgs := NewGitCmd("rev-parse").Arg("--verify", "HEAD").ToArgv()
fixupSha, err := self.cmd.New(cmdArgs).RunWithOutput()
if err != nil {
return err
}
@@ -264,16 +265,18 @@ func (self *RebaseCommands) SquashAllAboveFixupCommits(commit *models.Commit) er
shaOrRoot = "--root"
}
cmdStr := NewGitCmd("rebase").
cmdArgs := NewGitCmd("rebase").
Arg("--interactive", "--rebase-merges", "--autostash", "--autosquash", shaOrRoot).
ToString()
ToArgv()
return self.runSkipEditorCommand(self.cmd.New(cmdStr))
return self.runSkipEditorCommand(self.cmd.New(cmdArgs))
}
// BeginInteractiveRebaseForCommit starts an interactive rebase to edit the current
// commit and pick all others. After this you'll want to call `self.ContinueRebase()
func (self *RebaseCommands) BeginInteractiveRebaseForCommit(commits []*models.Commit, commitIndex int) error {
func (self *RebaseCommands) BeginInteractiveRebaseForCommit(
commits []*models.Commit, commitIndex int, keepCommitsThatBecomeEmpty bool,
) error {
if len(commits)-1 < commitIndex {
return errors.New("index outside of range of commits")
}
@@ -292,9 +295,10 @@ func (self *RebaseCommands) BeginInteractiveRebaseForCommit(commits []*models.Co
self.os.LogCommand(logTodoChanges(changes), false)
return self.PrepareInteractiveRebaseCommand(PrepareInteractiveRebaseCommandOpts{
baseShaOrRoot: getBaseShaOrRoot(commits, commitIndex+1),
overrideEditor: true,
instruction: daemon.NewChangeTodoActionsInstruction(changes),
baseShaOrRoot: getBaseShaOrRoot(commits, commitIndex+1),
overrideEditor: true,
keepCommitsThatBecomeEmpty: keepCommitsThatBecomeEmpty,
instruction: daemon.NewChangeTodoActionsInstruction(changes),
}).Run()
}
@@ -304,9 +308,9 @@ func (self *RebaseCommands) RebaseBranch(branchName string) error {
}
func (self *RebaseCommands) GenericMergeOrRebaseActionCmdObj(commandType string, command string) oscommands.ICmdObj {
cmdStr := NewGitCmd(commandType).Arg("--" + command).ToString()
cmdArgs := NewGitCmd(commandType).Arg("--" + command).ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *RebaseCommands) ContinueRebase() error {
@@ -358,14 +362,14 @@ func (self *RebaseCommands) runSkipEditorCommand(cmdObj oscommands.ICmdObj) erro
// DiscardOldFileChanges discards changes to a file from an old commit
func (self *RebaseCommands) DiscardOldFileChanges(commits []*models.Commit, commitIndex int, fileName string) error {
if err := self.BeginInteractiveRebaseForCommit(commits, commitIndex); err != nil {
if err := self.BeginInteractiveRebaseForCommit(commits, commitIndex, false); err != nil {
return err
}
// check if file exists in previous commit (this command returns an error if the file doesn't exist)
cmdStr := NewGitCmd("cat-file").Arg("-e", "HEAD^:"+self.cmd.Quote(fileName)).ToString()
cmdArgs := NewGitCmd("cat-file").Arg("-e", "HEAD^:"+fileName).ToArgv()
if err := self.cmd.New(cmdStr).Run(); err != nil {
if err := self.cmd.New(cmdArgs).Run(); err != nil {
if err := self.os.Remove(fileName); err != nil {
return err
}
+12 -12
View File
@@ -29,7 +29,7 @@ func TestRebaseRebaseBranch(t *testing.T) {
arg: "master",
gitVersion: &GitVersion{2, 26, 0, ""},
runner: oscommands.NewFakeRunner(t).
Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash --rebase-merges master`, "", nil),
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "master"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@@ -39,7 +39,7 @@ func TestRebaseRebaseBranch(t *testing.T) {
arg: "master",
gitVersion: &GitVersion{2, 26, 0, ""},
runner: oscommands.NewFakeRunner(t).
Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash --rebase-merges master`, "", errors.New("error")),
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "master"}, "", errors.New("error")),
test: func(err error) {
assert.Error(t, err)
},
@@ -49,7 +49,7 @@ func TestRebaseRebaseBranch(t *testing.T) {
arg: "master",
gitVersion: &GitVersion{2, 25, 5, ""},
runner: oscommands.NewFakeRunner(t).
Expect(`git rebase --interactive --autostash --keep-empty --no-autosquash --rebase-merges master`, "", nil),
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "master"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@@ -59,7 +59,7 @@ func TestRebaseRebaseBranch(t *testing.T) {
arg: "master",
gitVersion: &GitVersion{2, 21, 9, ""},
runner: oscommands.NewFakeRunner(t).
Expect(`git rebase --interactive --autostash --keep-empty --no-autosquash master`, "", nil),
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "master"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@@ -78,9 +78,9 @@ func TestRebaseRebaseBranch(t *testing.T) {
// TestRebaseSkipEditorCommand confirms that SkipEditorCommand injects
// environment variables that suppress an interactive editor
func TestRebaseSkipEditorCommand(t *testing.T) {
commandStr := "git blah"
cmdArgs := []string{"git", "blah"}
runner := oscommands.NewFakeRunner(t).ExpectFunc(func(cmdObj oscommands.ICmdObj) (string, error) {
assert.Equal(t, commandStr, cmdObj.ToString())
assert.EqualValues(t, cmdArgs, cmdObj.Args())
envVars := cmdObj.GetEnvVars()
for _, regexStr := range []string{
`^VISUAL=.*$`,
@@ -100,7 +100,7 @@ func TestRebaseSkipEditorCommand(t *testing.T) {
return "", nil
})
instance := buildRebaseCommands(commonDeps{runner: runner})
err := instance.runSkipEditorCommand(instance.cmd.New(commandStr))
err := instance.runSkipEditorCommand(instance.cmd.New(cmdArgs))
assert.NoError(t, err)
runner.CheckForMissingCalls()
}
@@ -149,11 +149,11 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
commitIndex: 0,
fileName: "test999.txt",
runner: oscommands.NewFakeRunner(t).
Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash --rebase-merges abcdef`, "", nil).
Expect(`git cat-file -e HEAD^:"test999.txt"`, "", nil).
Expect(`git checkout HEAD^ -- "test999.txt"`, "", nil).
Expect(`git commit --amend --no-edit --allow-empty`, "", nil).
Expect(`git rebase --continue`, "", nil),
ExpectGitArgs([]string{"rebase", "--interactive", "--autostash", "--keep-empty", "--no-autosquash", "--rebase-merges", "abcdef"}, "", nil).
ExpectGitArgs([]string{"cat-file", "-e", "HEAD^:test999.txt"}, "", nil).
ExpectGitArgs([]string{"checkout", "HEAD^", "--", "test999.txt"}, "", nil).
ExpectGitArgs([]string{"commit", "--amend", "--no-edit", "--allow-empty"}, "", nil).
ExpectGitArgs([]string{"rebase", "--continue"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@@ -1,7 +1,6 @@
package git_commands
import (
"fmt"
"strconv"
"strings"
@@ -27,12 +26,15 @@ func NewReflogCommitLoader(common *common.Common, cmd oscommands.ICmdObjBuilder)
func (self *ReflogCommitLoader) GetReflogCommits(lastReflogCommit *models.Commit, filterPath string) ([]*models.Commit, bool, error) {
commits := make([]*models.Commit, 0)
filterPathArg := ""
if filterPath != "" {
filterPathArg = fmt.Sprintf(" --follow -- %s", self.cmd.Quote(filterPath))
}
cmdArgs := NewGitCmd("log").
Config("log.showSignature=false").
Arg("-g").
Arg("--abbrev=40").
Arg("--format=%h%x00%ct%x00%gs%x00%p").
ArgIf(filterPath != "", "--follow", "--", filterPath).
ToArgv()
cmdObj := self.cmd.New(fmt.Sprintf(`git -c log.showSignature=false log -g --abbrev=40 --format="%s"%s`, "%h%x00%ct%x00%gs%x00%p", filterPathArg)).DontLog()
cmdObj := self.cmd.New(cmdArgs).DontLog()
onlyObtainedNewReflogCommits := false
err := cmdObj.RunAndProcessLines(func(line string) (bool, error) {
fields := strings.SplitN(line, "\x00", 4)
@@ -34,7 +34,7 @@ func TestGetReflogCommits(t *testing.T) {
{
testName: "no reflog entries",
runner: oscommands.NewFakeRunner(t).
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p"`, "", nil),
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, "", nil),
lastReflogCommit: nil,
expectedCommits: []*models.Commit{},
@@ -44,7 +44,7 @@ func TestGetReflogCommits(t *testing.T) {
{
testName: "some reflog entries",
runner: oscommands.NewFakeRunner(t).
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p"`, reflogOutput, nil),
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, reflogOutput, nil),
lastReflogCommit: nil,
expectedCommits: []*models.Commit{
@@ -90,7 +90,7 @@ func TestGetReflogCommits(t *testing.T) {
{
testName: "some reflog entries where last commit is given",
runner: oscommands.NewFakeRunner(t).
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p"`, reflogOutput, nil),
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, reflogOutput, nil),
lastReflogCommit: &models.Commit{
Sha: "c3c4b66b64c97ffeecde",
@@ -114,7 +114,7 @@ func TestGetReflogCommits(t *testing.T) {
{
testName: "when passing filterPath",
runner: oscommands.NewFakeRunner(t).
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p" --follow -- "path"`, reflogOutput, nil),
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p", "--follow", "--", "path"}, reflogOutput, nil),
lastReflogCommit: &models.Commit{
Sha: "c3c4b66b64c97ffeecde",
@@ -139,7 +139,7 @@ func TestGetReflogCommits(t *testing.T) {
{
testName: "when command returns error",
runner: oscommands.NewFakeRunner(t).
Expect(`git -c log.showSignature=false log -g --abbrev=40 --format="%h%x00%ct%x00%gs%x00%p"`, "", errors.New("haha")),
ExpectGitArgs([]string{"-c", "log.showSignature=false", "log", "-g", "--abbrev=40", "--format=%h%x00%ct%x00%gs%x00%p"}, "", errors.New("haha")),
lastReflogCommit: nil,
filterPath: "",
+24 -24
View File
@@ -15,52 +15,52 @@ func NewRemoteCommands(gitCommon *GitCommon) *RemoteCommands {
}
func (self *RemoteCommands) AddRemote(name string, url string) error {
cmdStr := NewGitCmd("remote").
Arg("add", self.cmd.Quote(name), self.cmd.Quote(url)).
ToString()
cmdArgs := NewGitCmd("remote").
Arg("add", name, url).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *RemoteCommands) RemoveRemote(name string) error {
cmdStr := NewGitCmd("remote").
Arg("remove", self.cmd.Quote(name)).
ToString()
cmdArgs := NewGitCmd("remote").
Arg("remove", name).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *RemoteCommands) RenameRemote(oldRemoteName string, newRemoteName string) error {
cmdStr := NewGitCmd("remote").
Arg("rename", self.cmd.Quote(oldRemoteName), self.cmd.Quote(newRemoteName)).
ToString()
cmdArgs := NewGitCmd("remote").
Arg("rename", oldRemoteName, newRemoteName).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *RemoteCommands) UpdateRemoteUrl(remoteName string, updatedUrl string) error {
cmdStr := NewGitCmd("remote").
Arg("set-url", self.cmd.Quote(remoteName), self.cmd.Quote(updatedUrl)).
ToString()
cmdArgs := NewGitCmd("remote").
Arg("set-url", remoteName, updatedUrl).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *RemoteCommands) DeleteRemoteBranch(remoteName string, branchName string) error {
cmdStr := NewGitCmd("push").
Arg(self.cmd.Quote(remoteName), "--delete", self.cmd.Quote(branchName)).
ToString()
cmdArgs := NewGitCmd("push").
Arg(remoteName, "--delete", branchName).
ToArgv()
return self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
}
// CheckRemoteBranchExists Returns remote branch
func (self *RemoteCommands) CheckRemoteBranchExists(branchName string) bool {
cmdStr := NewGitCmd("show-ref").
Arg("--verify", "--", fmt.Sprintf("refs/remotes/origin/%s", self.cmd.Quote(branchName))).
ToString()
cmdArgs := NewGitCmd("show-ref").
Arg("--verify", "--", fmt.Sprintf("refs/remotes/origin/%s", branchName)).
ToArgv()
_, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
_, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return err == nil
}
+2 -2
View File
@@ -31,8 +31,8 @@ func NewRemoteLoader(
}
func (self *RemoteLoader) GetRemotes() ([]*models.Remote, error) {
cmdStr := NewGitCmd("branch").Arg("-r").ToString()
remoteBranchesStr, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
cmdArgs := NewGitCmd("branch").Arg("-r").ToArgv()
remoteBranchesStr, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
if err != nil {
return nil, err
}
+37 -37
View File
@@ -26,84 +26,84 @@ func NewStashCommands(
}
func (self *StashCommands) DropNewest() error {
cmdStr := NewGitCmd("stash").Arg("drop").ToString()
cmdArgs := NewGitCmd("stash").Arg("drop").ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) Drop(index int) error {
cmdStr := NewGitCmd("stash").Arg("drop", fmt.Sprintf("stash@{%d}", index)).
ToString()
cmdArgs := NewGitCmd("stash").Arg("drop", fmt.Sprintf("stash@{%d}", index)).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) Pop(index int) error {
cmdStr := NewGitCmd("stash").Arg("pop", fmt.Sprintf("stash@{%d}", index)).
ToString()
cmdArgs := NewGitCmd("stash").Arg("pop", fmt.Sprintf("stash@{%d}", index)).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) Apply(index int) error {
cmdStr := NewGitCmd("stash").Arg("apply", fmt.Sprintf("stash@{%d}", index)).
ToString()
cmdArgs := NewGitCmd("stash").Arg("apply", fmt.Sprintf("stash@{%d}", index)).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// Save save stash
func (self *StashCommands) Save(message string) error {
cmdStr := NewGitCmd("stash").Arg("save", self.cmd.Quote(message)).
ToString()
cmdArgs := NewGitCmd("stash").Arg("save", message).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) Store(sha string, message string) error {
trimmedMessage := strings.Trim(message, " \t")
cmdStr := NewGitCmd("stash").Arg("store", self.cmd.Quote(sha)).
ArgIf(trimmedMessage != "", "-m", self.cmd.Quote(trimmedMessage)).
ToString()
cmdArgs := NewGitCmd("stash").Arg("store", sha).
ArgIf(trimmedMessage != "", "-m", trimmedMessage).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) Sha(index int) (string, error) {
cmdStr := NewGitCmd("rev-parse").
cmdArgs := NewGitCmd("rev-parse").
Arg(fmt.Sprintf("refs/stash@{%d}", index)).
ToString()
ToArgv()
sha, _, err := self.cmd.New(cmdStr).DontLog().RunWithOutputs()
sha, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
return strings.Trim(sha, "\r\n"), err
}
func (self *StashCommands) ShowStashEntryCmdObj(index int, ignoreWhitespace bool) oscommands.ICmdObj {
cmdStr := NewGitCmd("stash").Arg("show").
cmdArgs := 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()
ToArgv()
return self.cmd.New(cmdStr).DontLog()
return self.cmd.New(cmdArgs).DontLog()
}
func (self *StashCommands) StashAndKeepIndex(message string) error {
cmdStr := NewGitCmd("stash").Arg("save", self.cmd.Quote(message), "--keep-index").
ToString()
cmdArgs := NewGitCmd("stash").Arg("save", message, "--keep-index").
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *StashCommands) StashUnstagedChanges(message string) error {
if err := self.cmd.New(
NewGitCmd("commit").
Arg("--no-verify", "-m", self.cmd.Quote("[lazygit] stashing unstaged changes")).
ToString(),
Arg("--no-verify", "-m", "[lazygit] stashing unstaged changes").
ToArgv(),
).Run(); err != nil {
return err
}
@@ -112,7 +112,7 @@ func (self *StashCommands) StashUnstagedChanges(message string) error {
}
if err := self.cmd.New(
NewGitCmd("reset").Arg("--soft", "HEAD^").ToString(),
NewGitCmd("reset").Arg("--soft", "HEAD^").ToArgv(),
).Run(); err != nil {
return err
}
@@ -124,7 +124,7 @@ func (self *StashCommands) StashUnstagedChanges(message string) error {
func (self *StashCommands) SaveStagedChanges(message string) error {
// wrap in 'writing', which uses a mutex
if err := self.cmd.New(
NewGitCmd("stash").Arg("--keep-index").ToString(),
NewGitCmd("stash").Arg("--keep-index").ToArgv(),
).Run(); err != nil {
return err
}
@@ -134,20 +134,20 @@ func (self *StashCommands) SaveStagedChanges(message string) error {
}
if err := self.cmd.New(
NewGitCmd("stash").Arg("apply", "stash@{1}").ToString(),
NewGitCmd("stash").Arg("apply", "stash@{1}").ToArgv(),
).Run(); err != nil {
return err
}
if err := self.os.PipeCommands(
NewGitCmd("stash").Arg("show", "-p").ToString(),
NewGitCmd("apply").Arg("-R").ToString(),
self.cmd.New(NewGitCmd("stash").Arg("show", "-p").ToArgv()),
self.cmd.New(NewGitCmd("apply").Arg("-R").ToArgv()),
); err != nil {
return err
}
if err := self.cmd.New(
NewGitCmd("stash").Arg("drop", "stash@{1}").ToString(),
NewGitCmd("stash").Arg("drop", "stash@{1}").ToArgv(),
).Run(); err != nil {
return err
}
@@ -171,8 +171,8 @@ func (self *StashCommands) SaveStagedChanges(message string) error {
func (self *StashCommands) StashIncludeUntrackedChanges(message string) error {
return self.cmd.New(
NewGitCmd("stash").Arg("save", self.cmd.Quote(message), "--include-untracked").
ToString(),
NewGitCmd("stash").Arg("save", message, "--include-untracked").
ToArgv(),
).Run()
}
+4 -4
View File
@@ -32,8 +32,8 @@ func (self *StashLoader) GetStashEntries(filterPath string) []*models.StashEntry
return self.getUnfilteredStashEntries()
}
cmdStr := NewGitCmd("stash").Arg("list", "--name-only").ToString()
rawString, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
cmdArgs := NewGitCmd("stash").Arg("list", "--name-only").ToArgv()
rawString, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
if err != nil {
return self.getUnfilteredStashEntries()
}
@@ -66,9 +66,9 @@ outer:
}
func (self *StashLoader) getUnfilteredStashEntries() []*models.StashEntry {
cmdStr := NewGitCmd("stash").Arg("list", "-z", "--pretty='%gs'").ToString()
cmdArgs := NewGitCmd("stash").Arg("list", "-z", "--pretty=%gs").ToArgv()
rawString, _ := self.cmd.New(cmdStr).DontLog().RunWithOutput()
rawString, _ := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
return slices.MapWithIndex(utils.SplitNul(rawString), func(line string, index int) *models.StashEntry {
return self.stashEntryFromLine(line, index)
})
@@ -22,15 +22,14 @@ func TestGetStashEntries(t *testing.T) {
"No stash entries found",
"",
oscommands.NewFakeRunner(t).
Expect(`git stash list -z --pretty='%gs'`, "", nil),
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%gs"}, "", nil),
[]*models.StashEntry{},
},
{
"Several stash entries found",
"",
oscommands.NewFakeRunner(t).
Expect(
`git stash list -z --pretty='%gs'`,
ExpectGitArgs([]string{"stash", "list", "-z", "--pretty=%gs"},
"WIP on add-pkg-commands-test: 55c6af2 increase parallel build\x00WIP on master: bb86a3f update github template\x00",
nil,
),
+5 -5
View File
@@ -103,7 +103,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
index int
contextSize int
ignoreWhitespace bool
expected string
expected []string
}
scenarios := []scenario{
@@ -112,21 +112,21 @@ func TestStashStashEntryCmdObj(t *testing.T) {
index: 5,
contextSize: 3,
ignoreWhitespace: false,
expected: "git stash show -p --stat --color=always --unified=3 stash@{5}",
expected: []string{"git", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "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}",
expected: []string{"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}",
expected: []string{"git", "stash", "show", "-p", "--stat", "--color=always", "--unified=3", "--ignore-all-space", "stash@{5}"},
},
}
@@ -137,7 +137,7 @@ func TestStashStashEntryCmdObj(t *testing.T) {
userConfig.Git.DiffContextSize = s.contextSize
instance := buildStashCommands(commonDeps{userConfig: userConfig})
cmdStr := instance.ShowStashEntryCmdObj(s.index, s.ignoreWhitespace).ToString()
cmdStr := instance.ShowStashEntryCmdObj(s.index, s.ignoreWhitespace).Args()
assert.Equal(t, s.expected, cmdStr)
})
}
+1 -1
View File
@@ -57,7 +57,7 @@ func (self *StatusCommands) IsBareRepo() (bool, error) {
func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
res, err := osCommand.Cmd.New(
NewGitCmd("rev-parse").Arg("--is-bare-repository").ToString(),
NewGitCmd("rev-parse").Arg("--is-bare-repository").ToArgv(),
).DontLog().RunWithOutput()
if err != nil {
return false, err
+44 -44
View File
@@ -81,27 +81,27 @@ func (self *SubmoduleCommands) Stash(submodule *models.SubmoduleConfig) error {
return nil
}
cmdStr := NewGitCmd("stash").
RepoPath(self.cmd.Quote(submodule.Path)).
cmdArgs := NewGitCmd("stash").
RepoPath(submodule.Path).
Arg("--include-untracked").
ToString()
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *SubmoduleCommands) Reset(submodule *models.SubmoduleConfig) error {
cmdStr := NewGitCmd("submodule").
Arg("update", "--init", "--force", "--", self.cmd.Quote(submodule.Path)).
ToString()
cmdArgs := NewGitCmd("submodule").
Arg("update", "--init", "--force", "--", submodule.Path).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *SubmoduleCommands) UpdateAll() error {
// not doing an --init here because the user probably doesn't want that
cmdStr := NewGitCmd("submodule").Arg("update", "--force").ToString()
cmdArgs := NewGitCmd("submodule").Arg("update", "--force").ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
@@ -109,7 +109,7 @@ func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
if err := self.cmd.New(
NewGitCmd("submodule").
Arg("deinit", "--force", "--", self.cmd.Quote(submodule.Path)).ToString(),
Arg("deinit", "--force", "--", submodule.Path).ToArgv(),
).Run(); err != nil {
if !strings.Contains(err.Error(), "did not match any file(s) known to git") {
return err
@@ -117,23 +117,23 @@ func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
if err := self.cmd.New(
NewGitCmd("config").
Arg("--file", ".gitmodules", "--remove-section", "submodule."+self.cmd.Quote(submodule.Path)).
ToString(),
Arg("--file", ".gitmodules", "--remove-section", "submodule."+submodule.Path).
ToArgv(),
).Run(); err != nil {
return err
}
if err := self.cmd.New(
NewGitCmd("config").
Arg("--remove-section", "submodule."+self.cmd.Quote(submodule.Path)).
ToString(),
Arg("--remove-section", "submodule."+submodule.Path).
ToArgv(),
).Run(); err != nil {
return err
}
}
if err := self.cmd.New(
NewGitCmd("rm").Arg("--force", "-r", submodule.Path).ToString(),
NewGitCmd("rm").Arg("--force", "-r", submodule.Path).ToArgv(),
).Run(); err != nil {
// if the directory isn't there then that's fine
self.Log.Error(err)
@@ -143,33 +143,33 @@ func (self *SubmoduleCommands) Delete(submodule *models.SubmoduleConfig) error {
}
func (self *SubmoduleCommands) Add(name string, path string, url string) error {
cmdStr := NewGitCmd("submodule").
cmdArgs := NewGitCmd("submodule").
Arg("add").
Arg("--force").
Arg("--name").
Arg(self.cmd.Quote(name)).
Arg(name).
Arg("--").
Arg(self.cmd.Quote(url)).
Arg(self.cmd.Quote(path)).
ToString()
Arg(url).
Arg(path).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).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),
"--file", ".gitmodules", "submodule."+name+".url", newUrl,
).
ToString()
ToArgv()
// the set-url command is only for later git versions so we're doing it manually here
if err := self.cmd.New(setUrlCmdStr).Run(); err != nil {
return err
}
syncCmdStr := NewGitCmd("submodule").Arg("sync", "--", self.cmd.Quote(path)).
ToString()
syncCmdStr := NewGitCmd("submodule").Arg("sync", "--", path).
ToArgv()
if err := self.cmd.New(syncCmdStr).Run(); err != nil {
return err
@@ -179,45 +179,45 @@ func (self *SubmoduleCommands) UpdateUrl(name string, path string, newUrl string
}
func (self *SubmoduleCommands) Init(path string) error {
cmdStr := NewGitCmd("submodule").Arg("init", "--", self.cmd.Quote(path)).
ToString()
cmdArgs := NewGitCmd("submodule").Arg("init", "--", path).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *SubmoduleCommands) Update(path string) error {
cmdStr := NewGitCmd("submodule").Arg("update", "--init", "--", self.cmd.Quote(path)).
ToString()
cmdArgs := NewGitCmd("submodule").Arg("update", "--init", "--", path).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *SubmoduleCommands) BulkInitCmdObj() oscommands.ICmdObj {
cmdStr := NewGitCmd("submodule").Arg("init").
ToString()
cmdArgs := NewGitCmd("submodule").Arg("init").
ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *SubmoduleCommands) BulkUpdateCmdObj() oscommands.ICmdObj {
cmdStr := NewGitCmd("submodule").Arg("update").
ToString()
cmdArgs := NewGitCmd("submodule").Arg("update").
ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *SubmoduleCommands) ForceBulkUpdateCmdObj() oscommands.ICmdObj {
cmdStr := NewGitCmd("submodule").Arg("update", "--force").
ToString()
cmdArgs := NewGitCmd("submodule").Arg("update", "--force").
ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *SubmoduleCommands) BulkDeinitCmdObj() oscommands.ICmdObj {
cmdStr := NewGitCmd("submodule").Arg("deinit", "--all", "--force").
ToString()
cmdArgs := NewGitCmd("submodule").Arg("deinit", "--all", "--force").
ToArgv()
return self.cmd.New(cmdStr)
return self.cmd.New(cmdArgs)
}
func (self *SubmoduleCommands) ResetSubmodules(submodules []*models.SubmoduleConfig) error {
+24 -24
View File
@@ -28,14 +28,14 @@ func (self *SyncCommands) PushCmdObj(opts PushOpts) (oscommands.ICmdObj, error)
return nil, errors.New(self.Tr.MustSpecifyOriginError)
}
cmdStr := NewGitCmd("push").
cmdArgs := 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()
ArgIf(opts.UpstreamRemote != "", opts.UpstreamRemote).
ArgIf(opts.UpstreamBranch != "", opts.UpstreamBranch).
ToArgv()
cmdObj := self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex)
cmdObj := self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex)
return cmdObj, nil
}
@@ -56,12 +56,12 @@ type FetchOptions struct {
// Fetch fetch git repo
func (self *SyncCommands) Fetch(opts FetchOptions) error {
cmdStr := NewGitCmd("fetch").
ArgIf(opts.RemoteName != "", self.cmd.Quote(opts.RemoteName)).
ArgIf(opts.BranchName != "", self.cmd.Quote(opts.BranchName)).
ToString()
cmdArgs := NewGitCmd("fetch").
ArgIf(opts.RemoteName != "", opts.RemoteName).
ArgIf(opts.BranchName != "", opts.BranchName).
ToArgv()
cmdObj := self.cmd.New(cmdStr)
cmdObj := self.cmd.New(cmdArgs)
if opts.Background {
cmdObj.DontLog().FailOnCredentialRequest()
} else {
@@ -77,31 +77,31 @@ type PullOptions struct {
}
func (self *SyncCommands) Pull(opts PullOptions) error {
cmdStr := NewGitCmd("pull").
cmdArgs := 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()
ArgIf(opts.RemoteName != "", opts.RemoteName).
ArgIf(opts.BranchName != "", opts.BranchName).
ToArgv()
// setting GIT_SEQUENCE_EDITOR to ':' as a way of skipping it, in case the user
// has 'pull.rebase = interactive' configured.
return self.cmd.New(cmdStr).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).AddEnvVars("GIT_SEQUENCE_EDITOR=:").PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
}
func (self *SyncCommands) FastForward(branchName string, remoteName string, remoteBranchName string) error {
cmdStr := NewGitCmd("fetch").
Arg(self.cmd.Quote(remoteName)).
Arg(self.cmd.Quote(remoteBranchName) + ":" + self.cmd.Quote(branchName)).
ToString()
cmdArgs := NewGitCmd("fetch").
Arg(remoteName).
Arg(remoteBranchName + ":" + branchName).
ToArgv()
return self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
}
func (self *SyncCommands) FetchRemote(remoteName string) error {
cmdStr := NewGitCmd("fetch").
Arg(self.cmd.Quote(remoteName)).
ToString()
cmdArgs := NewGitCmd("fetch").
Arg(remoteName).
ToArgv()
return self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
}
+5 -5
View File
@@ -19,7 +19,7 @@ func TestSyncPush(t *testing.T) {
testName: "Push with force disabled",
opts: PushOpts{Force: false},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.ToString(), "git push")
assert.Equal(t, cmdObj.Args(), []string{"git", "push"})
assert.NoError(t, err)
},
},
@@ -27,7 +27,7 @@ func TestSyncPush(t *testing.T) {
testName: "Push with force enabled",
opts: PushOpts{Force: true},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.ToString(), "git push --force-with-lease")
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease"})
assert.NoError(t, err)
},
},
@@ -39,7 +39,7 @@ func TestSyncPush(t *testing.T) {
UpstreamBranch: "master",
},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.ToString(), `git push "origin" "master"`)
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "origin", "master"})
assert.NoError(t, err)
},
},
@@ -52,7 +52,7 @@ func TestSyncPush(t *testing.T) {
SetUpstream: true,
},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.ToString(), `git push --set-upstream "origin" "master"`)
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--set-upstream", "origin", "master"})
assert.NoError(t, err)
},
},
@@ -65,7 +65,7 @@ func TestSyncPush(t *testing.T) {
SetUpstream: true,
},
test: func(cmdObj oscommands.ICmdObj, err error) {
assert.Equal(t, cmdObj.ToString(), `git push --force-with-lease --set-upstream "origin" "master"`)
assert.Equal(t, cmdObj.Args(), []string{"git", "push", "--force-with-lease", "--set-upstream", "origin", "master"})
assert.NoError(t, err)
},
},
+15 -15
View File
@@ -11,32 +11,32 @@ func NewTagCommands(gitCommon *GitCommon) *TagCommands {
}
func (self *TagCommands) CreateLightweight(tagName string, ref string) error {
cmdStr := NewGitCmd("tag").Arg("--", self.cmd.Quote(tagName)).
ArgIf(len(ref) > 0, self.cmd.Quote(ref)).
ToString()
cmdArgs := NewGitCmd("tag").Arg("--", tagName).
ArgIf(len(ref) > 0, ref).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *TagCommands) CreateAnnotated(tagName, ref, msg string) error {
cmdStr := NewGitCmd("tag").Arg(self.cmd.Quote(tagName)).
ArgIf(len(ref) > 0, self.cmd.Quote(ref)).
Arg("-m", self.cmd.Quote(msg)).
ToString()
cmdArgs := NewGitCmd("tag").Arg(tagName).
ArgIf(len(ref) > 0, ref).
Arg("-m", msg).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *TagCommands) Delete(tagName string) error {
cmdStr := NewGitCmd("tag").Arg("-d", self.cmd.Quote(tagName)).
ToString()
cmdArgs := NewGitCmd("tag").Arg("-d", tagName).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *TagCommands) Push(remoteName string, tagName string) error {
cmdStr := NewGitCmd("push").Arg(self.cmd.Quote(remoteName), "tag", self.cmd.Quote(tagName)).
ToString()
cmdArgs := NewGitCmd("push").Arg(remoteName, "tag", tagName).
ToArgv()
return self.cmd.New(cmdStr).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
return self.cmd.New(cmdArgs).PromptOnCredentialRequest().WithMutex(self.syncMutex).Run()
}
+2 -2
View File
@@ -28,8 +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
cmdStr := NewGitCmd("tag").Arg("--list", "-n", "--sort=-creatordate").ToString()
tagsOutput, err := self.cmd.New(cmdStr).DontLog().RunWithOutput()
cmdArgs := NewGitCmd("tag").Arg("--list", "-n", "--sort=-creatordate").ToArgv()
tagsOutput, err := self.cmd.New(cmdArgs).DontLog().RunWithOutput()
if err != nil {
return nil, err
}
+2 -2
View File
@@ -26,14 +26,14 @@ func TestGetTags(t *testing.T) {
{
testName: "should return no tags if there are none",
runner: oscommands.NewFakeRunner(t).
Expect(`git tag --list -n --sort=-creatordate`, "", nil),
ExpectGitArgs([]string{"tag", "--list", "-n", "--sort=-creatordate"}, "", nil),
expectedTags: []*models.Tag{},
expectedError: nil,
},
{
testName: "should return tags if present",
runner: oscommands.NewFakeRunner(t).
Expect(`git tag --list -n --sort=-creatordate`, tagsOutput, nil),
ExpectGitArgs([]string{"tag", "--list", "-n", "--sort=-creatordate"}, tagsOutput, nil),
expectedTags: []*models.Tag{
{Name: "tag1", Message: "this is my message"},
{Name: "tag2", Message: ""},
+1 -1
View File
@@ -15,7 +15,7 @@ type GitVersion struct {
}
func GetGitVersion(osCommand *oscommands.OSCommand) (*GitVersion, error) {
versionStr, _, err := osCommand.Cmd.New(NewGitCmd("--version").ToString()).RunWithOutputs()
versionStr, _, err := osCommand.Cmd.New(NewGitCmd("--version").ToArgv()).RunWithOutputs()
if err != nil {
return nil, err
}
+47 -56
View File
@@ -5,7 +5,6 @@ import (
"os"
"github.com/go-errors/errors"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
)
@@ -29,7 +28,7 @@ func NewWorkingTreeCommands(
}
func (self *WorkingTreeCommands) OpenMergeToolCmdObj() oscommands.ICmdObj {
return self.cmd.New(NewGitCmd("mergetool").ToString())
return self.cmd.New(NewGitCmd("mergetool").ToArgv())
}
func (self *WorkingTreeCommands) OpenMergeTool() error {
@@ -42,25 +41,21 @@ func (self *WorkingTreeCommands) StageFile(path string) error {
}
func (self *WorkingTreeCommands) StageFiles(paths []string) error {
quotedPaths := slices.Map(paths, func(path string) string {
return self.cmd.Quote(path)
})
cmdArgs := NewGitCmd("add").Arg("--").Arg(paths...).ToArgv()
cmdStr := NewGitCmd("add").Arg("--").Arg(quotedPaths...).ToString()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// StageAll stages all files
func (self *WorkingTreeCommands) StageAll() error {
cmdStr := NewGitCmd("add").Arg("-A").ToString()
cmdArgs := NewGitCmd("add").Arg("-A").ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// UnstageAll unstages all files
func (self *WorkingTreeCommands) UnstageAll() error {
return self.cmd.New(NewGitCmd("reset").ToString()).Run()
return self.cmd.New(NewGitCmd("reset").ToArgv()).Run()
}
// UnStageFile unstages a file
@@ -68,14 +63,14 @@ func (self *WorkingTreeCommands) UnstageAll() error {
// we accept the current name and the previous name
func (self *WorkingTreeCommands) UnStageFile(fileNames []string, reset bool) error {
for _, name := range fileNames {
var cmdStr string
var cmdArgs []string
if reset {
cmdStr = NewGitCmd("reset").Arg("HEAD", "--", self.cmd.Quote(name)).ToString()
cmdArgs = NewGitCmd("reset").Arg("HEAD", "--", name).ToArgv()
} else {
cmdStr = NewGitCmd("rm").Arg("--cached", "--force", "--", self.cmd.Quote(name)).ToString()
cmdArgs = NewGitCmd("rm").Arg("--cached", "--force", "--", name).ToArgv()
}
err := self.cmd.New(cmdStr).Run()
err := self.cmd.New(cmdArgs).Run()
if err != nil {
return err
}
@@ -137,17 +132,15 @@ func (self *WorkingTreeCommands) DiscardAllFileChanges(file *models.File) error
return nil
}
quotedFileName := self.cmd.Quote(file.Name)
if file.ShortStatus == "AA" {
if err := self.cmd.New(
NewGitCmd("checkout").Arg("--ours", "--", quotedFileName).ToString(),
NewGitCmd("checkout").Arg("--ours", "--", file.Name).ToArgv(),
).Run(); err != nil {
return err
}
if err := self.cmd.New(
NewGitCmd("add").Arg("--", quotedFileName).ToString(),
NewGitCmd("add").Arg("--", file.Name).ToArgv(),
).Run(); err != nil {
return err
}
@@ -156,14 +149,14 @@ func (self *WorkingTreeCommands) DiscardAllFileChanges(file *models.File) error
if file.ShortStatus == "DU" {
return self.cmd.New(
NewGitCmd("rm").Arg("rm", "--", quotedFileName).ToString(),
NewGitCmd("rm").Arg("--", file.Name).ToArgv(),
).Run()
}
// if the file isn't tracked, we assume you want to delete it
if file.HasStagedChanges || file.HasMergeConflicts {
if err := self.cmd.New(
NewGitCmd("reset").Arg("--", quotedFileName).ToString(),
NewGitCmd("reset").Arg("--", file.Name).ToArgv(),
).Run(); err != nil {
return err
}
@@ -195,9 +188,8 @@ func (self *WorkingTreeCommands) DiscardUnstagedDirChanges(node IFileNode) error
return err
}
quotedPath := self.cmd.Quote(node.GetPath())
cmdStr := NewGitCmd("checkout").Arg("--", quotedPath).ToString()
if err := self.cmd.New(cmdStr).Run(); err != nil {
cmdArgs := NewGitCmd("checkout").Arg("--", node.GetPath()).ToArgv()
if err := self.cmd.New(cmdArgs).Run(); err != nil {
return err
}
@@ -221,9 +213,8 @@ func (self *WorkingTreeCommands) RemoveUntrackedDirFiles(node IFileNode) error {
// DiscardUnstagedFileChanges directly
func (self *WorkingTreeCommands) DiscardUnstagedFileChanges(file *models.File) error {
quotedFileName := self.cmd.Quote(file.Name)
cmdStr := NewGitCmd("checkout").Arg("--", quotedFileName).ToString()
return self.cmd.New(cmdStr).Run()
cmdArgs := NewGitCmd("checkout").Arg("--", file.Name).ToArgv()
return self.cmd.New(cmdArgs).Run()
}
// Ignore adds a file to the gitignore for the repo
@@ -253,7 +244,7 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain
prevPath := node.GetPreviousPath()
noIndex := !node.GetIsTracked() && !node.GetHasStagedChanges() && !cached && node.GetIsFile()
cmdStr := NewGitCmd("diff").
cmdArgs := NewGitCmd("diff").
Arg("--submodule").
Arg("--no-ext-diff").
Arg(fmt.Sprintf("--unified=%d", contextSize)).
@@ -263,11 +254,11 @@ func (self *WorkingTreeCommands) WorktreeFileDiffCmdObj(node models.IFile, plain
ArgIf(noIndex, "--no-index").
Arg("--").
ArgIf(noIndex, "/dev/null").
Arg(self.cmd.Quote(node.GetPath())).
ArgIf(prevPath != "", self.cmd.Quote(prevPath)).
ToString()
Arg(node.GetPath()).
ArgIf(prevPath != "", prevPath).
ToArgv()
return self.cmd.New(cmdStr).DontLog()
return self.cmd.New(cmdArgs).DontLog()
}
// ShowFileDiff get the diff of specified from and to. Typically this will be used for a single commit so it'll be 123abc^..123abc
@@ -288,7 +279,7 @@ func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reve
colorArg = "never"
}
cmdStr := NewGitCmd("diff").
cmdArgs := NewGitCmd("diff").
Arg("--submodule").
Arg("--no-ext-diff").
Arg(fmt.Sprintf("--unified=%d", contextSize)).
@@ -299,41 +290,41 @@ func (self *WorkingTreeCommands) ShowFileDiffCmdObj(from string, to string, reve
ArgIf(reverse, "-R").
ArgIf(ignoreWhitespace, "--ignore-all-space").
Arg("--").
Arg(self.cmd.Quote(fileName)).
ToString()
Arg(fileName).
ToArgv()
return self.cmd.New(cmdStr).DontLog()
return self.cmd.New(cmdArgs).DontLog()
}
// CheckoutFile checks out the file for the given commit
func (self *WorkingTreeCommands) CheckoutFile(commitSha, fileName string) error {
cmdStr := NewGitCmd("checkout").Arg(commitSha, "--", self.cmd.Quote(fileName)).
ToString()
cmdArgs := NewGitCmd("checkout").Arg(commitSha, "--", fileName).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// DiscardAnyUnstagedFileChanges discards any unstaged file changes via `git checkout -- .`
func (self *WorkingTreeCommands) DiscardAnyUnstagedFileChanges() error {
cmdStr := NewGitCmd("checkout").Arg("--", ".").
ToString()
cmdArgs := NewGitCmd("checkout").Arg("--", ".").
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// RemoveTrackedFiles will delete the given file(s) even if they are currently tracked
func (self *WorkingTreeCommands) RemoveTrackedFiles(name string) error {
cmdStr := NewGitCmd("rm").Arg("-r", "--cached", "--", self.cmd.Quote(name)).
ToString()
cmdArgs := NewGitCmd("rm").Arg("-r", "--cached", "--", name).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// RemoveUntrackedFiles runs `git clean -fd`
func (self *WorkingTreeCommands) RemoveUntrackedFiles() error {
cmdStr := NewGitCmd("clean").Arg("-fd").ToString()
cmdArgs := NewGitCmd("clean").Arg("-fd").ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// ResetAndClean removes all unstaged changes and removes all untracked files
@@ -358,23 +349,23 @@ func (self *WorkingTreeCommands) ResetAndClean() error {
// ResetHardHead runs `git reset --hard`
func (self *WorkingTreeCommands) ResetHard(ref string) error {
cmdStr := NewGitCmd("reset").Arg("--hard", self.cmd.Quote(ref)).
ToString()
cmdArgs := NewGitCmd("reset").Arg("--hard", ref).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
// ResetSoft runs `git reset --soft HEAD`
func (self *WorkingTreeCommands) ResetSoft(ref string) error {
cmdStr := NewGitCmd("reset").Arg("--soft", self.cmd.Quote(ref)).
ToString()
cmdArgs := NewGitCmd("reset").Arg("--soft", ref).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
func (self *WorkingTreeCommands) ResetMixed(ref string) error {
cmdStr := NewGitCmd("reset").Arg("--mixed", self.cmd.Quote(ref)).
ToString()
cmdArgs := NewGitCmd("reset").Arg("--mixed", ref).
ToArgv()
return self.cmd.New(cmdStr).Run()
return self.cmd.New(cmdArgs).Run()
}
+27 -27
View File
@@ -13,7 +13,7 @@ import (
func TestWorkingTreeStageFile(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
Expect(`git add -- "test.txt"`, "", nil)
ExpectGitArgs([]string{"add", "--", "test.txt"}, "", nil)
instance := buildWorkingTreeCommands(commonDeps{runner: runner})
@@ -23,7 +23,7 @@ func TestWorkingTreeStageFile(t *testing.T) {
func TestWorkingTreeStageFiles(t *testing.T) {
runner := oscommands.NewFakeRunner(t).
Expect(`git add -- "test.txt" "test2.txt"`, "", nil)
ExpectGitArgs([]string{"add", "--", "test.txt", "test2.txt"}, "", nil)
instance := buildWorkingTreeCommands(commonDeps{runner: runner})
@@ -44,7 +44,7 @@ func TestWorkingTreeUnstageFile(t *testing.T) {
testName: "Remove an untracked file from staging",
reset: false,
runner: oscommands.NewFakeRunner(t).
Expect(`git rm --cached --force -- "test.txt"`, "", nil),
ExpectGitArgs([]string{"rm", "--cached", "--force", "--", "test.txt"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@@ -53,7 +53,7 @@ func TestWorkingTreeUnstageFile(t *testing.T) {
testName: "Remove a tracked file from staging",
reset: true,
runner: oscommands.NewFakeRunner(t).
Expect(`git reset HEAD -- "test.txt"`, "", nil),
ExpectGitArgs([]string{"reset", "HEAD", "--", "test.txt"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@@ -90,7 +90,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
},
removeFile: func(string) error { return nil },
runner: oscommands.NewFakeRunner(t).
Expect(`git reset -- "test"`, "", errors.New("error")),
ExpectGitArgs([]string{"reset", "--", "test"}, "", errors.New("error")),
expectedError: "error",
},
{
@@ -115,7 +115,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
},
removeFile: func(string) error { return nil },
runner: oscommands.NewFakeRunner(t).
Expect(`git checkout -- "test"`, "", errors.New("error")),
ExpectGitArgs([]string{"checkout", "--", "test"}, "", errors.New("error")),
expectedError: "error",
},
{
@@ -127,7 +127,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
},
removeFile: func(string) error { return nil },
runner: oscommands.NewFakeRunner(t).
Expect(`git checkout -- "test"`, "", nil),
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
expectedError: "",
},
{
@@ -139,8 +139,8 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
},
removeFile: func(string) error { return nil },
runner: oscommands.NewFakeRunner(t).
Expect(`git reset -- "test"`, "", nil).
Expect(`git checkout -- "test"`, "", nil),
ExpectGitArgs([]string{"reset", "--", "test"}, "", nil).
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
expectedError: "",
},
{
@@ -152,8 +152,8 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
},
removeFile: func(string) error { return nil },
runner: oscommands.NewFakeRunner(t).
Expect(`git reset -- "test"`, "", nil).
Expect(`git checkout -- "test"`, "", nil),
ExpectGitArgs([]string{"reset", "--", "test"}, "", nil).
ExpectGitArgs([]string{"checkout", "--", "test"}, "", nil),
expectedError: "",
},
{
@@ -169,7 +169,7 @@ func TestWorkingTreeDiscardAllFileChanges(t *testing.T) {
return nil
},
runner: oscommands.NewFakeRunner(t).
Expect(`git reset -- "test"`, "", nil),
ExpectGitArgs([]string{"reset", "--", "test"}, "", nil),
expectedError: "",
},
{
@@ -231,7 +231,7 @@ func TestWorkingTreeDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=always -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "cached",
@@ -245,7 +245,7 @@ func TestWorkingTreeDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=always --cached -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--cached", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "plain",
@@ -259,7 +259,7 @@ func TestWorkingTreeDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=never -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=never", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "File not tracked and file has no staged changes",
@@ -273,7 +273,7 @@ func TestWorkingTreeDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=always --no-index -- /dev/null "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--no-index", "--", "/dev/null", "test.txt"}, expectedResult, nil),
},
{
testName: "Default case (ignore whitespace)",
@@ -287,7 +287,7 @@ func TestWorkingTreeDiff(t *testing.T) {
ignoreWhitespace: true,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=3 --color=always --ignore-all-space -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--color=always", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "Show diff with custom context size",
@@ -301,7 +301,7 @@ func TestWorkingTreeDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 17,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=17 --color=always -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=17", "--color=always", "--", "test.txt"}, expectedResult, nil),
},
}
@@ -343,7 +343,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=3 --no-renames --color=always 1234567890 0987654321 -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "Show diff with custom context size",
@@ -354,7 +354,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
ignoreWhitespace: false,
contextSize: 123,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=123 --no-renames --color=always 1234567890 0987654321 -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=123", "--no-renames", "--color=always", "1234567890", "0987654321", "--", "test.txt"}, expectedResult, nil),
},
{
testName: "Default case (ignore whitespace)",
@@ -365,7 +365,7 @@ func TestWorkingTreeShowFileDiff(t *testing.T) {
ignoreWhitespace: true,
contextSize: 3,
runner: oscommands.NewFakeRunner(t).
Expect(`git diff --submodule --no-ext-diff --unified=3 --no-renames --color=always 1234567890 0987654321 --ignore-all-space -- "test.txt"`, expectedResult, nil),
ExpectGitArgs([]string{"diff", "--submodule", "--no-ext-diff", "--unified=3", "--no-renames", "--color=always", "1234567890", "0987654321", "--ignore-all-space", "--", "test.txt"}, expectedResult, nil),
},
}
@@ -400,7 +400,7 @@ func TestWorkingTreeCheckoutFile(t *testing.T) {
commitSha: "11af912",
fileName: "test999.txt",
runner: oscommands.NewFakeRunner(t).
Expect(`git checkout 11af912 -- "test999.txt"`, "", nil),
ExpectGitArgs([]string{"checkout", "11af912", "--", "test999.txt"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@@ -410,7 +410,7 @@ func TestWorkingTreeCheckoutFile(t *testing.T) {
commitSha: "11af912",
fileName: "test999.txt",
runner: oscommands.NewFakeRunner(t).
Expect(`git checkout 11af912 -- "test999.txt"`, "", errors.New("error")),
ExpectGitArgs([]string{"checkout", "11af912", "--", "test999.txt"}, "", errors.New("error")),
test: func(err error) {
assert.Error(t, err)
},
@@ -441,7 +441,7 @@ func TestWorkingTreeDiscardUnstagedFileChanges(t *testing.T) {
testName: "valid case",
file: &models.File{Name: "test.txt"},
runner: oscommands.NewFakeRunner(t).
Expect(`git checkout -- "test.txt"`, "", nil),
ExpectGitArgs([]string{"checkout", "--", "test.txt"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@@ -469,7 +469,7 @@ func TestWorkingTreeDiscardAnyUnstagedFileChanges(t *testing.T) {
{
testName: "valid case",
runner: oscommands.NewFakeRunner(t).
Expect(`git checkout -- .`, "", nil),
ExpectGitArgs([]string{"checkout", "--", "."}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@@ -497,7 +497,7 @@ func TestWorkingTreeRemoveUntrackedFiles(t *testing.T) {
{
testName: "valid case",
runner: oscommands.NewFakeRunner(t).
Expect(`git clean -fd`, "", nil),
ExpectGitArgs([]string{"clean", "-fd"}, "", nil),
test: func(err error) {
assert.NoError(t, err)
},
@@ -527,7 +527,7 @@ func TestWorkingTreeResetHard(t *testing.T) {
"valid case",
"HEAD",
oscommands.NewFakeRunner(t).
Expect(`git reset --hard "HEAD"`, "", nil),
ExpectGitArgs([]string{"reset", "--hard", "HEAD"}, "", nil),
func(err error) {
assert.NoError(t, err)
},
+3 -3
View File
@@ -33,9 +33,9 @@ var bitbucketServiceDef = ServiceDefinition{
var gitLabServiceDef = ServiceDefinition{
provider: "gitlab",
pullRequestURLIntoDefaultBranch: "/merge_requests/new?merge_request[source_branch]={{.From}}",
pullRequestURLIntoTargetBranch: "/merge_requests/new?merge_request[source_branch]={{.From}}&merge_request[target_branch]={{.To}}",
commitURL: "/commit/{{.CommitSha}}",
pullRequestURLIntoDefaultBranch: "/-/merge_requests/new?merge_request[source_branch]={{.From}}",
pullRequestURLIntoTargetBranch: "/-/merge_requests/new?merge_request[source_branch]={{.From}}&merge_request[target_branch]={{.To}}",
commitURL: "/-/commit/{{.CommitSha}}",
regexStrings: defaultUrlRegexStrings,
repoURLTemplate: defaultRepoURLTemplate,
}
@@ -112,7 +112,7 @@ func TestGetPullRequestURL(t *testing.T) {
remoteUrl: "git@gitlab.com:peter/calculator.git",
test: func(url string, err error) {
assert.NoError(t, err)
assert.Equal(t, "https://gitlab.com/peter/calculator/merge_requests/new?merge_request[source_branch]=feature%2Fui", url)
assert.Equal(t, "https://gitlab.com/peter/calculator/-/merge_requests/new?merge_request[source_branch]=feature%2Fui", url)
},
},
{
@@ -121,7 +121,7 @@ func TestGetPullRequestURL(t *testing.T) {
remoteUrl: "git@gitlab.com:peter/public/calculator.git",
test: func(url string, err error) {
assert.NoError(t, err)
assert.Equal(t, "https://gitlab.com/peter/public/calculator/merge_requests/new?merge_request[source_branch]=feature%2Fui", url)
assert.Equal(t, "https://gitlab.com/peter/public/calculator/-/merge_requests/new?merge_request[source_branch]=feature%2Fui", url)
},
},
{
@@ -130,7 +130,7 @@ func TestGetPullRequestURL(t *testing.T) {
remoteUrl: "https://gitlab.com/peter/public/calculator.git",
test: func(url string, err error) {
assert.NoError(t, err)
assert.Equal(t, "https://gitlab.com/peter/public/calculator/merge_requests/new?merge_request[source_branch]=feature%2Fui", url)
assert.Equal(t, "https://gitlab.com/peter/public/calculator/-/merge_requests/new?merge_request[source_branch]=feature%2Fui", url)
},
},
{
@@ -140,7 +140,7 @@ func TestGetPullRequestURL(t *testing.T) {
remoteUrl: "git@gitlab.com:peter/calculator.git",
test: func(url string, err error) {
assert.NoError(t, err)
assert.Equal(t, "https://gitlab.com/peter/calculator/merge_requests/new?merge_request[source_branch]=feature%2Fcommit-ui&merge_request[target_branch]=epic%2Fui", url)
assert.Equal(t, "https://gitlab.com/peter/calculator/-/merge_requests/new?merge_request[source_branch]=feature%2Fcommit-ui&merge_request[target_branch]=epic%2Fui", url)
},
},
{
@@ -150,7 +150,7 @@ func TestGetPullRequestURL(t *testing.T) {
remoteUrl: "git@gitlab.com:peter/public/calculator.git",
test: func(url string, err error) {
assert.NoError(t, err)
assert.Equal(t, "https://gitlab.com/peter/public/calculator/merge_requests/new?merge_request[source_branch]=feature%2Fcommit-ui&merge_request[target_branch]=epic%2Fui", url)
assert.Equal(t, "https://gitlab.com/peter/public/calculator/-/merge_requests/new?merge_request[source_branch]=feature%2Fcommit-ui&merge_request[target_branch]=epic%2Fui", url)
},
},
{
@@ -160,7 +160,7 @@ func TestGetPullRequestURL(t *testing.T) {
remoteUrl: "https://gitlab.com/peter/public/calculator.git",
test: func(url string, err error) {
assert.NoError(t, err)
assert.Equal(t, "https://gitlab.com/peter/public/calculator/merge_requests/new?merge_request[source_branch]=feature%2Fcommit-ui&merge_request[target_branch]=epic%2Fui", url)
assert.Equal(t, "https://gitlab.com/peter/public/calculator/-/merge_requests/new?merge_request[source_branch]=feature%2Fcommit-ui&merge_request[target_branch]=epic%2Fui", url)
},
},
{
@@ -373,7 +373,7 @@ func TestGetPullRequestURL(t *testing.T) {
remoteUrl: "git@gitlab.com:me/public/repo-with-issues.git",
test: func(url string, err error) {
assert.NoError(t, err)
assert.Equal(t, "https://gitlab.com/me/public/repo-with-issues/merge_requests/new?merge_request[source_branch]=feature%2FsomeIssue%23123&merge_request[target_branch]=master", url)
assert.Equal(t, "https://gitlab.com/me/public/repo-with-issues/-/merge_requests/new?merge_request[source_branch]=feature%2FsomeIssue%23123&merge_request[target_branch]=master", url)
},
},
{
@@ -383,7 +383,7 @@ func TestGetPullRequestURL(t *testing.T) {
remoteUrl: "git@gitlab.com:me/public/repo-with-issues.git",
test: func(url string, err error) {
assert.NoError(t, err)
assert.Equal(t, "https://gitlab.com/me/public/repo-with-issues/merge_requests/new?merge_request[source_branch]=yolo&merge_request[target_branch]=archive%2Fnever-ending-feature%23666", url)
assert.Equal(t, "https://gitlab.com/me/public/repo-with-issues/-/merge_requests/new?merge_request[source_branch]=yolo&merge_request[target_branch]=archive%2Fnever-ending-feature%23666", url)
},
},
}
+23 -3
View File
@@ -2,7 +2,9 @@ package oscommands
import (
"os/exec"
"strings"
"github.com/samber/lo"
"github.com/sasha-s/go-deadlock"
)
@@ -15,6 +17,9 @@ type ICmdObj interface {
// into a terminal e.g. 'sh -c git commit' as opposed to 'sh -c "git commit"'
ToString() string
// outputs args vector e.g. ["git", "commit", "-m", "my message"]
Args() []string
AddEnvVars(...string) ICmdObj
GetEnvVars() []string
@@ -61,8 +66,11 @@ type ICmdObj interface {
}
type CmdObj struct {
cmdStr string
cmd *exec.Cmd
// the secureexec package will swap out the first arg with the full path to the binary,
// so we store these args separately so that ToString() will output the original
args []string
cmd *exec.Cmd
runner ICmdObjRunner
@@ -104,7 +112,19 @@ func (self *CmdObj) GetCmd() *exec.Cmd {
}
func (self *CmdObj) ToString() string {
return self.cmdStr
// if a given arg contains a space, we need to wrap it in quotes
quotedArgs := lo.Map(self.args, func(arg string, _ int) string {
if strings.Contains(arg, " ") {
return `"` + arg + `"`
}
return arg
})
return strings.Join(quotedArgs, " ")
}
func (self *CmdObj) Args() []string {
return self.args
}
func (self *CmdObj) AddEnvVars(vars ...string) ICmdObj {
+10 -20
View File
@@ -10,12 +10,10 @@ import (
)
type ICmdObjBuilder interface {
// New returns a new command object based on the string provided
New(cmdStr string) ICmdObj
// NewFromArgs takes a slice of strings like []string{"git", "commit"} and returns a new command object.
New(args []string) ICmdObj
// NewShell takes a string like `git commit` and returns an executable shell command for it e.g. `sh -c 'git commit'`
NewShell(commandStr string) ICmdObj
// NewFromArgs takes a slice of strings like []string{"git", "commit"} and returns a new command object. This can be useful when you don't want to worry about whitespace and quoting and stuff.
NewFromArgs(args []string) ICmdObj
// Quote wraps a string in quotes with any necessary escaping applied. The reason for bundling this up with the other methods in this interface is that we basically always need to make use of this when creating new command objects.
Quote(str string) string
}
@@ -28,24 +26,12 @@ type CmdObjBuilder struct {
// poor man's version of explicitly saying that struct X implements interface Y
var _ ICmdObjBuilder = &CmdObjBuilder{}
func (self *CmdObjBuilder) New(cmdStr string) ICmdObj {
args := str.ToArgv(cmdStr)
func (self *CmdObjBuilder) New(args []string) ICmdObj {
cmd := secureexec.Command(args[0], args[1:]...)
cmd.Env = os.Environ()
return &CmdObj{
cmdStr: cmdStr,
cmd: cmd,
runner: self.runner,
}
}
func (self *CmdObjBuilder) NewFromArgs(args []string) ICmdObj {
cmd := secureexec.Command(args[0], args[1:]...)
cmd.Env = os.Environ()
return &CmdObj{
cmdStr: strings.Join(args, " "),
args: args,
cmd: cmd,
runner: self.runner,
}
@@ -67,8 +53,9 @@ func (self *CmdObjBuilder) NewShell(commandStr string) ICmdObj {
quotedCommand = self.Quote(commandStr)
}
shellCommand := fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand)
return self.New(shellCommand)
cmdArgs := str.ToArgv(fmt.Sprintf("%s %s %s", self.platform.Shell, self.platform.ShellArg, quotedCommand))
return self.New(cmdArgs)
}
func (self *CmdObjBuilder) CloneWithNewRunner(decorate func(ICmdObjRunner) ICmdObjRunner) *CmdObjBuilder {
@@ -80,6 +67,9 @@ func (self *CmdObjBuilder) CloneWithNewRunner(decorate func(ICmdObjRunner) ICmdO
}
}
const CHARS_REQUIRING_QUOTES = "\"\\$` "
// If you update this method, be sure to update CHARS_REQUIRING_QUOTES
func (self *CmdObjBuilder) Quote(message string) string {
var quote string
if self.platform.OS == "windows" {
+6 -34
View File
@@ -4,48 +4,20 @@
package oscommands
import (
"bytes"
"io"
"os/exec"
"github.com/sasha-s/go-deadlock"
"github.com/creack/pty"
)
type Buffer struct {
b bytes.Buffer
m deadlock.Mutex
}
func (b *Buffer) Read(p []byte) (n int, err error) {
b.m.Lock()
defer b.m.Unlock()
return b.b.Read(p)
}
func (b *Buffer) Write(p []byte) (n int, err error) {
b.m.Lock()
defer b.m.Unlock()
return b.b.Write(p)
}
// TODO: Remove this hack and replace it with a proper way to run commands live on windows. We still have an issue where if a password is requested, the request for a password is written straight to stdout because we can't control the stdout of a subprocess of a subprocess. Keep an eye on https://github.com/creack/pty/pull/109
func (self *cmdObjRunner) getCmdHandler(cmd *exec.Cmd) (*cmdHandler, error) {
stdoutReader, stdoutWriter := io.Pipe()
cmd.Stdout = stdoutWriter
buf := &Buffer{}
cmd.Stdin = buf
if err := cmd.Start(); err != nil {
ptmx, err := pty.Start(cmd)
if err != nil {
return nil, err
}
// because we don't yet have windows support for a pty, we instead just
// pass our standard stream handlers and because there's no pty to close
// we pass a no-op function for that.
return &cmdHandler{
stdoutPipe: stdoutReader,
stdinPipe: buf,
close: func() error { return nil },
stdoutPipe: ptmx,
stdinPipe: ptmx,
close: ptmx.Close,
}, nil
}
+33
View File
@@ -0,0 +1,33 @@
package oscommands
import (
"testing"
)
func TestCmdObjToString(t *testing.T) {
quote := func(s string) string {
return "\"" + s + "\""
}
scenarios := []struct {
cmdArgs []string
expected string
}{
{
cmdArgs: []string{"git", "push", "myfile.txt"},
expected: "git push myfile.txt",
},
{
cmdArgs: []string{"git", "push", "my file.txt"},
expected: "git push \"my file.txt\"",
},
}
for _, scenario := range scenarios {
cmdObj := &CmdObj{args: scenario.cmdArgs}
actual := cmdObj.ToString()
if actual != scenario.expected {
t.Errorf("Expected %s, got %s", quote(scenario.expected), quote(actual))
}
}
}
+13 -1
View File
@@ -4,6 +4,7 @@ import (
"bufio"
"fmt"
"regexp"
"runtime"
"strings"
"testing"
@@ -91,7 +92,18 @@ func (self *FakeCmdObjRunner) Expect(expectedCmdStr string, output string, err e
func (self *FakeCmdObjRunner) ExpectArgs(expectedArgs []string, output string, err error) *FakeCmdObjRunner {
self.ExpectFunc(func(cmdObj ICmdObj) (string, error) {
args := cmdObj.GetCmd().Args
assert.EqualValues(self.t, expectedArgs, args, fmt.Sprintf("command %d did not match expectation", self.expectedCmdIndex+1))
if runtime.GOOS == "windows" {
// thanks to the secureexec package, the first arg is something like
// '"C:\\Program Files\\Git\\mingw64\\bin\\<command>.exe"
// on windows so we'll just ensure it contains our program
assert.Contains(self.t, args[0], expectedArgs[0])
} else {
// first arg is the program name
assert.Equal(self.t, expectedArgs[0], args[0])
}
assert.EqualValues(self.t, expectedArgs[1:], args[1:], fmt.Sprintf("command %d did not match expectation", self.expectedCmdIndex+1))
return output, err
})
+11 -4
View File
@@ -10,6 +10,7 @@ import (
"sync"
"github.com/go-errors/errors"
"github.com/samber/lo"
"github.com/atotto/clipboard"
"github.com/jesseduffield/generics/slices"
@@ -187,12 +188,18 @@ func (c *OSCommand) FileExists(path string) (bool, error) {
}
// PipeCommands runs a heap of commands and pipes their inputs/outputs together like A | B | C
func (c *OSCommand) PipeCommands(commandStrings ...string) error {
cmds := slices.Map(commandStrings, func(cmdString string) *exec.Cmd {
return c.Cmd.New(cmdString).GetCmd()
func (c *OSCommand) PipeCommands(cmdObjs ...ICmdObj) error {
cmds := slices.Map(cmdObjs, func(cmdObj ICmdObj) *exec.Cmd {
return cmdObj.GetCmd()
})
logCmdStr := strings.Join(commandStrings, " | ")
logCmdStr := strings.Join(
lo.Map(cmdObjs, func(cmdObj ICmdObj, _ int) string {
return cmdObj.ToString()
}),
" | ",
)
c.LogCommand(logCmdStr, true)
for i := 0; i < len(cmds)-1; i++ {
+5 -5
View File
@@ -12,20 +12,20 @@ import (
func TestOSCommandRunWithOutput(t *testing.T) {
type scenario struct {
command string
test func(string, error)
args []string
test func(string, error)
}
scenarios := []scenario{
{
"echo -n '123'",
[]string{"echo", "-n", "123"},
func(output string, err error) {
assert.NoError(t, err)
assert.EqualValues(t, "123", output)
},
},
{
"rmdir unexisting-folder",
[]string{"rmdir", "unexisting-folder"},
func(output string, err error) {
assert.Regexp(t, "rmdir.*unexisting-folder.*", err.Error())
},
@@ -34,7 +34,7 @@ func TestOSCommandRunWithOutput(t *testing.T) {
for _, s := range scenarios {
c := NewDummyOSCommand()
s.test(c.Cmd.New(s.command).RunWithOutput())
s.test(c.Cmd.New(s.args).RunWithOutput())
}
}
+4 -4
View File
@@ -10,13 +10,13 @@ import (
func TestOSCommandRun(t *testing.T) {
type scenario struct {
command string
test func(error)
args []string
test func(error)
}
scenarios := []scenario{
{
"rmdir unexisting-folder",
[]string{"rmdir", "unexisting-folder"},
func(err error) {
assert.Regexp(t, "rmdir.*unexisting-folder.*", err.Error())
},
@@ -25,7 +25,7 @@ func TestOSCommandRun(t *testing.T) {
for _, s := range scenarios {
c := NewDummyOSCommand()
s.test(c.Cmd.New(s.command).Run())
s.test(c.Cmd.New(s.args).Run())
}
}
+4
View File
@@ -301,5 +301,9 @@ func getDefaultAppState() *AppState {
}
func LogPath() (string, error) {
if os.Getenv("LAZYGIT_LOG_PATH") != "" {
return os.Getenv("LAZYGIT_LOG_PATH"), nil
}
return configFilePath("development.log")
}
+7
View File
@@ -44,6 +44,12 @@ func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset
"emacs": standardTerminalEditorPreset("emacs"),
"nano": standardTerminalEditorPreset("nano"),
"kakoune": standardTerminalEditorPreset("kakoune"),
"helix": {
editTemplate: "hx -- {{filename}}",
editAtLineTemplate: "hx -- {{filename}}:{{line}}",
editAtLineAndWaitTemplate: "hx -- {{filename}}:{{line}}",
editInTerminal: true,
},
"vscode": {
editTemplate: "code --reuse-window -- {{filename}}",
editAtLineTemplate: "code --reuse-window --goto -- {{filename}}:{{line}}",
@@ -73,6 +79,7 @@ func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset
// Some of our presets have a different name than the editor they are using.
editorToPreset := map[string]string{
"kak": "kakoune",
"hx": "helix",
"code": "vscode",
"subl": "sublime",
"xed": "xcode",
+3 -1
View File
@@ -39,6 +39,7 @@ type GuiConfig struct {
MainPanelSplitMode string `yaml:"mainPanelSplitMode"`
Language string `yaml:"language"`
TimeFormat string `yaml:"timeFormat"`
ShortTimeFormat string `yaml:"shortTimeFormat"`
Theme ThemeConfig `yaml:"theme"`
CommitLength CommitLengthConfig `yaml:"commitLength"`
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
@@ -398,7 +399,8 @@ func GetDefaultConfig() *UserConfig {
ExpandFocusedSidePanel: false,
MainPanelSplitMode: "flexible",
Language: "auto",
TimeFormat: time.RFC822,
TimeFormat: "02 Jan 06",
ShortTimeFormat: time.Kitchen,
Theme: ThemeConfig{
ActiveBorderColor: []string{"green", "bold"},
InactiveBorderColor: []string{"default"},
-3
View File
@@ -1,3 +0,0 @@
package constants
const SEARCH_PREFIX = "search: "
+19 -2
View File
@@ -13,8 +13,15 @@ type ListContextTrait struct {
c *ContextCommon
list types.IList
getDisplayStrings func(startIdx int, length int) [][]string
// alignment for each column. If nil, the default is left alignment
// Alignment for each column. If nil, the default is left alignment
columnAlignments []utils.Alignment
// Some contexts, like the commit context, will highlight the path from the selected commit
// to its parents, because it's ambiguous otherwise. For these, we need to refresh the viewport
// so that we show the highlighted path.
// TODO: now that we allow scrolling, we should be smarter about what gets refreshed:
// we should find out exactly which lines are now part of the path and refresh those.
// We should also keep track of the previous path and refresh those lines too.
refreshViewportOnLineFocus bool
}
func (self *ListContextTrait) IsListContext() {}
@@ -24,9 +31,19 @@ func (self *ListContextTrait) GetList() types.IList {
}
func (self *ListContextTrait) FocusLine() {
// we need a way of knowing whether we've rendered to the view yet.
self.GetViewTrait().FocusPoint(self.list.GetSelectedLineIdx())
self.setFooter()
if self.refreshViewportOnLineFocus {
self.refreshViewport()
}
}
func (self *ListContextTrait) refreshViewport() {
startIdx, length := self.GetViewTrait().ViewPortYBounds()
displayStrings := self.getDisplayStrings(startIdx, length)
content := utils.RenderDisplayStrings(displayStrings, nil)
self.GetViewTrait().SetViewPortContent(content)
}
func (self *ListContextTrait) setFooter() {
+16 -14
View File
@@ -2,6 +2,7 @@ package context
import (
"log"
"time"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
@@ -11,7 +12,7 @@ import (
type LocalCommitsContext struct {
*LocalCommitsViewModel
*ViewportListContextTrait
*ListContextTrait
}
var (
@@ -44,6 +45,8 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
c.Modes().CherryPicking.SelectedShaSet(),
c.Modes().Diffing.Ref,
c.UserConfig.Gui.TimeFormat,
c.UserConfig.Gui.ShortTimeFormat,
time.Now(),
c.UserConfig.Git.ParseEmoji,
selectedCommitSha,
startIdx,
@@ -56,19 +59,18 @@ func NewLocalCommitsContext(c *ContextCommon) *LocalCommitsContext {
return &LocalCommitsContext{
LocalCommitsViewModel: viewModel,
ViewportListContextTrait: &ViewportListContextTrait{
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().Commits,
WindowName: "commits",
Key: LOCAL_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
})),
list: viewModel,
getDisplayStrings: getDisplayStrings,
c: c,
},
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().Commits,
WindowName: "commits",
Key: LOCAL_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
})),
list: viewModel,
getDisplayStrings: getDisplayStrings,
c: c,
refreshViewportOnLineFocus: true,
},
}
}
@@ -1,6 +1,8 @@
package context
import (
"time"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
"github.com/jesseduffield/lazygit/pkg/gui/types"
@@ -25,7 +27,9 @@ func NewReflogCommitsContext(c *ContextCommon) *ReflogCommitsContext {
c.State().GetRepoState().GetScreenMode() != types.SCREEN_NORMAL,
c.Modes().CherryPicking.SelectedShaSet(),
c.Modes().Diffing.Ref,
time.Now(),
c.UserConfig.Gui.TimeFormat,
c.UserConfig.Gui.ShortTimeFormat,
c.UserConfig.Git.ParseEmoji,
)
}
+17 -15
View File
@@ -2,6 +2,7 @@ package context
import (
"fmt"
"time"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/models"
@@ -12,7 +13,7 @@ import (
type SubCommitsContext struct {
*SubCommitsViewModel
*ViewportListContextTrait
*ListContextTrait
*DynamicTitleBuilder
}
@@ -47,6 +48,8 @@ func NewSubCommitsContext(
c.Modes().CherryPicking.SelectedShaSet(),
c.Modes().Diffing.Ref,
c.UserConfig.Gui.TimeFormat,
c.UserConfig.Gui.ShortTimeFormat,
time.Now(),
c.UserConfig.Git.ParseEmoji,
selectedCommitSha,
startIdx,
@@ -60,20 +63,19 @@ func NewSubCommitsContext(
return &SubCommitsContext{
SubCommitsViewModel: viewModel,
DynamicTitleBuilder: NewDynamicTitleBuilder(c.Tr.SubCommitsDynamicTitle),
ViewportListContextTrait: &ViewportListContextTrait{
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().SubCommits,
WindowName: "branches",
Key: SUB_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
Transient: true,
})),
list: viewModel,
getDisplayStrings: getDisplayStrings,
c: c,
},
ListContextTrait: &ListContextTrait{
Context: NewSimpleContext(NewBaseContext(NewBaseContextOpts{
View: c.Views().SubCommits,
WindowName: "branches",
Key: SUB_COMMITS_CONTEXT_KEY,
Kind: types.SIDE_CONTEXT,
Focusable: true,
Transient: true,
})),
list: viewModel,
getDisplayStrings: getDisplayStrings,
c: c,
refreshViewportOnLineFocus: true,
},
}
}
@@ -1,22 +0,0 @@
package context
import (
"github.com/jesseduffield/lazygit/pkg/utils"
)
// This embeds a list context trait and adds logic to re-render the viewport
// whenever a line is focused. We use this in the commits panel because different
// sections of the log graph need to be highlighted depending on the currently selected line
type ViewportListContextTrait struct {
*ListContextTrait
}
func (self *ViewportListContextTrait) FocusLine() {
self.ListContextTrait.FocusLine()
startIdx, length := self.GetViewTrait().ViewPortYBounds()
displayStrings := self.ListContextTrait.getDisplayStrings(startIdx, length)
content := utils.RenderDisplayStrings(displayStrings, nil)
self.GetViewTrait().SetViewPortContent(content)
}
+14 -14
View File
@@ -37,44 +37,44 @@ func (self *BasicCommitsController) GetKeybindings(opts types.KeybindingsOpts) [
{
Key: opts.GetKey(opts.Config.Commits.CheckoutCommit),
Handler: self.checkSelected(self.checkout),
Description: self.c.Tr.LcCheckoutCommit,
Description: self.c.Tr.CheckoutCommit,
},
{
Key: opts.GetKey(opts.Config.Commits.CopyCommitAttributeToClipboard),
Handler: self.checkSelected(self.copyCommitAttribute),
Description: self.c.Tr.LcCopyCommitAttributeToClipboard,
Description: self.c.Tr.CopyCommitAttributeToClipboard,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Commits.OpenInBrowser),
Handler: self.checkSelected(self.openInBrowser),
Description: self.c.Tr.LcOpenCommitInBrowser,
Description: self.c.Tr.OpenCommitInBrowser,
},
{
Key: opts.GetKey(opts.Config.Universal.New),
Handler: self.checkSelected(self.newBranch),
Description: self.c.Tr.LcCreateNewBranchFromCommit,
Description: self.c.Tr.CreateNewBranchFromCommit,
},
{
Key: opts.GetKey(opts.Config.Commits.ViewResetOptions),
Handler: self.checkSelected(self.createResetMenu),
Description: self.c.Tr.LcViewResetOptions,
Description: self.c.Tr.ViewResetOptions,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Commits.CherryPickCopy),
Handler: self.checkSelected(self.copy),
Description: self.c.Tr.LcCherryPickCopy,
Description: self.c.Tr.CherryPickCopy,
},
{
Key: opts.GetKey(opts.Config.Commits.CherryPickCopyRange),
Handler: self.checkSelected(self.copyRange),
Description: self.c.Tr.LcCherryPickCopyRange,
Description: self.c.Tr.CherryPickCopyRange,
},
{
Key: opts.GetKey(opts.Config.Commits.ResetCherryPick),
Handler: self.c.Helpers().CherryPick.Reset,
Description: self.c.Tr.LcResetCherryPick,
Description: self.c.Tr.ResetCherryPick,
},
}
@@ -101,35 +101,35 @@ func (self *BasicCommitsController) copyCommitAttribute(commit *models.Commit) e
Title: self.c.Tr.Actions.CopyCommitAttributeToClipboard,
Items: []*types.MenuItem{
{
Label: self.c.Tr.LcCommitSha,
Label: self.c.Tr.CommitSha,
OnPress: func() error {
return self.copyCommitSHAToClipboard(commit)
},
Key: 's',
},
{
Label: self.c.Tr.LcCommitURL,
Label: self.c.Tr.CommitURL,
OnPress: func() error {
return self.copyCommitURLToClipboard(commit)
},
Key: 'u',
},
{
Label: self.c.Tr.LcCommitDiff,
Label: self.c.Tr.CommitDiff,
OnPress: func() error {
return self.copyCommitDiffToClipboard(commit)
},
Key: 'd',
},
{
Label: self.c.Tr.LcCommitMessage,
Label: self.c.Tr.CommitMessage,
OnPress: func() error {
return self.copyCommitMessageToClipboard(commit)
},
Key: 'm',
},
{
Label: self.c.Tr.LcCommitAuthor,
Label: self.c.Tr.CommitAuthor,
OnPress: func() error {
return self.copyAuthorToClipboard(commit)
},
@@ -235,7 +235,7 @@ func (self *BasicCommitsController) createResetMenu(commit *models.Commit) error
func (self *BasicCommitsController) checkout(commit *models.Commit) error {
return self.c.Confirm(types.ConfirmOpts{
Title: self.c.Tr.LcCheckoutCommit,
Title: self.c.Tr.CheckoutCommit,
Prompt: self.c.Tr.SureCheckoutThisCommit,
HandleConfirm: func() error {
self.c.LogAction(self.c.Tr.Actions.CheckoutCommit)
+1 -1
View File
@@ -31,7 +31,7 @@ func (self *BisectController) GetKeybindings(opts types.KeybindingsOpts) []*type
{
Key: opts.GetKey(opts.Config.Commits.ViewBisectOptions),
Handler: opts.Guards.OutsideFilterMode(self.checkSelected(self.openMenu)),
Description: self.c.Tr.LcViewBisectOptions,
Description: self.c.Tr.ViewBisectOptions,
OpensMenu: true,
},
}
+19 -19
View File
@@ -33,53 +33,53 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty
{
Key: opts.GetKey(opts.Config.Universal.Select),
Handler: self.checkSelected(self.press),
Description: self.c.Tr.LcCheckout,
Description: self.c.Tr.Checkout,
},
{
Key: opts.GetKey(opts.Config.Universal.New),
Handler: self.checkSelected(self.newBranch),
Description: self.c.Tr.LcNewBranch,
Description: self.c.Tr.NewBranch,
},
{
Key: opts.GetKey(opts.Config.Branches.CreatePullRequest),
Handler: self.checkSelected(self.handleCreatePullRequest),
Description: self.c.Tr.LcCreatePullRequest,
Description: self.c.Tr.CreatePullRequest,
},
{
Key: opts.GetKey(opts.Config.Branches.ViewPullRequestOptions),
Handler: self.checkSelected(self.handleCreatePullRequestMenu),
Description: self.c.Tr.LcCreatePullRequestOptions,
Description: self.c.Tr.CreatePullRequestOptions,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Branches.CopyPullRequestURL),
Handler: self.copyPullRequestURL,
Description: self.c.Tr.LcCopyPullRequestURL,
Description: self.c.Tr.CopyPullRequestURL,
},
{
Key: opts.GetKey(opts.Config.Branches.CheckoutBranchByName),
Handler: self.checkoutByName,
Description: self.c.Tr.LcCheckoutByName,
Description: self.c.Tr.CheckoutByName,
},
{
Key: opts.GetKey(opts.Config.Branches.ForceCheckoutBranch),
Handler: self.forceCheckout,
Description: self.c.Tr.LcForceCheckout,
Description: self.c.Tr.ForceCheckout,
},
{
Key: opts.GetKey(opts.Config.Universal.Remove),
Handler: self.checkSelectedAndReal(self.delete),
Description: self.c.Tr.LcDeleteBranch,
Description: self.c.Tr.DeleteBranch,
},
{
Key: opts.GetKey(opts.Config.Branches.RebaseBranch),
Handler: opts.Guards.OutsideFilterMode(self.rebase),
Description: self.c.Tr.LcRebaseBranch,
Description: self.c.Tr.RebaseBranch,
},
{
Key: opts.GetKey(opts.Config.Branches.MergeIntoCurrentBranch),
Handler: opts.Guards.OutsideFilterMode(self.merge),
Description: self.c.Tr.LcMergeIntoCurrentBranch,
Description: self.c.Tr.MergeIntoCurrentBranch,
},
{
Key: opts.GetKey(opts.Config.Branches.FastForward),
@@ -89,23 +89,23 @@ func (self *BranchesController) GetKeybindings(opts types.KeybindingsOpts) []*ty
{
Key: opts.GetKey(opts.Config.Branches.CreateTag),
Handler: self.checkSelected(self.createTag),
Description: self.c.Tr.LcCreateTag,
Description: self.c.Tr.CreateTag,
},
{
Key: opts.GetKey(opts.Config.Commits.ViewResetOptions),
Handler: self.checkSelected(self.createResetMenu),
Description: self.c.Tr.LcViewResetOptions,
Description: self.c.Tr.ViewResetOptions,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Branches.RenameBranch),
Handler: self.checkSelectedAndReal(self.rename),
Description: self.c.Tr.LcRenameBranch,
Description: self.c.Tr.RenameBranch,
},
{
Key: opts.GetKey(opts.Config.Branches.SetUpstream),
Handler: self.checkSelected(self.setUpstream),
Description: self.c.Tr.LcSetUnsetUpstream,
Description: self.c.Tr.SetUnsetUpstream,
OpensMenu: true,
},
}
@@ -140,7 +140,7 @@ func (self *BranchesController) setUpstream(selectedBranch *models.Branch) error
Title: self.c.Tr.Actions.SetUnsetUpstream,
Items: []*types.MenuItem{
{
LabelColumns: []string{self.c.Tr.LcUnsetUpstream},
LabelColumns: []string{self.c.Tr.UnsetUpstream},
OnPress: func() error {
if err := self.c.Git().Branch.UnsetUpstream(selectedBranch.Name); err != nil {
return self.c.Error(err)
@@ -159,7 +159,7 @@ func (self *BranchesController) setUpstream(selectedBranch *models.Branch) error
Key: 'u',
},
{
LabelColumns: []string{self.c.Tr.LcSetUpstream},
LabelColumns: []string{self.c.Tr.SetUpstream},
OnPress: func() error {
return self.c.Helpers().Upstream.PromptForUpstreamWithoutInitialContent(selectedBranch, func(upstream string) error {
upstreamRemote, upstreamBranch, err := self.c.Helpers().Upstream.ParseUpstream(upstream)
@@ -437,7 +437,7 @@ func (self *BranchesController) rename(branch *models.Branch) error {
}
return self.c.Confirm(types.ConfirmOpts{
Title: self.c.Tr.LcRenameBranch,
Title: self.c.Tr.RenameBranch,
Prompt: self.c.Tr.RenameBranchWarning,
HandleConfirm: promptForNewName,
})
@@ -457,13 +457,13 @@ func (self *BranchesController) createPullRequestMenu(selectedBranch *models.Bra
menuItemsForBranch := func(branch *models.Branch) []*types.MenuItem {
return []*types.MenuItem{
{
LabelColumns: fromToLabelColumns(branch.Name, self.c.Tr.LcDefaultBranch),
LabelColumns: fromToLabelColumns(branch.Name, self.c.Tr.DefaultBranch),
OnPress: func() error {
return self.createPullRequest(branch.Name, "")
},
},
{
LabelColumns: fromToLabelColumns(branch.Name, self.c.Tr.LcSelectBranch),
LabelColumns: fromToLabelColumns(branch.Name, self.c.Tr.SelectBranch),
OnPress: func() error {
return self.c.Prompt(types.PromptOpts{
Title: branch.Name + " →",
@@ -29,12 +29,12 @@ func (self *CommitMessageController) GetKeybindings(opts types.KeybindingsOpts)
{
Key: opts.GetKey(opts.Config.Universal.SubmitEditorText),
Handler: self.confirm,
Description: self.c.Tr.LcConfirm,
Description: self.c.Tr.Confirm,
},
{
Key: opts.GetKey(opts.Config.Universal.Return),
Handler: self.close,
Description: self.c.Tr.LcClose,
Description: self.c.Tr.Close,
},
{
Key: opts.GetKey(opts.Config.Universal.PrevItem),
@@ -30,42 +30,42 @@ func (self *CommitFilesController) GetKeybindings(opts types.KeybindingsOpts) []
{
Key: opts.GetKey(opts.Config.CommitFiles.CheckoutCommitFile),
Handler: self.checkSelected(self.checkout),
Description: self.c.Tr.LcCheckoutCommitFile,
Description: self.c.Tr.CheckoutCommitFile,
},
{
Key: opts.GetKey(opts.Config.Universal.Remove),
Handler: self.checkSelected(self.discard),
Description: self.c.Tr.LcDiscardOldFileChange,
Description: self.c.Tr.DiscardOldFileChange,
},
{
Key: opts.GetKey(opts.Config.Universal.OpenFile),
Handler: self.checkSelected(self.open),
Description: self.c.Tr.LcOpenFile,
Description: self.c.Tr.OpenFile,
},
{
Key: opts.GetKey(opts.Config.Universal.Edit),
Handler: self.checkSelected(self.edit),
Description: self.c.Tr.LcEditFile,
Description: self.c.Tr.EditFile,
},
{
Key: opts.GetKey(opts.Config.Universal.Select),
Handler: self.checkSelected(self.toggleForPatch),
Description: self.c.Tr.LcToggleAddToPatch,
Description: self.c.Tr.ToggleAddToPatch,
},
{
Key: opts.GetKey(opts.Config.Files.ToggleStagedAll),
Handler: self.checkSelected(self.toggleAllForPatch),
Description: self.c.Tr.LcToggleAllInPatch,
Description: self.c.Tr.ToggleAllInPatch,
},
{
Key: opts.GetKey(opts.Config.Universal.GoInto),
Handler: self.checkSelected(self.enter),
Description: self.c.Tr.LcEnterFile,
Description: self.c.Tr.EnterFile,
},
{
Key: opts.GetKey(opts.Config.Files.ToggleTreeView),
Handler: self.toggleTreeView,
Description: self.c.Tr.LcToggleTreeView,
Description: self.c.Tr.ToggleTreeView,
},
}
@@ -189,7 +189,7 @@ func (self *CommitFilesController) edit(node *filetree.CommitFileNode) error {
func (self *CommitFilesController) toggleForPatch(node *filetree.CommitFileNode) error {
toggle := func() error {
return self.c.WithWaitingStatus(self.c.Tr.LcUpdatingPatch, func() error {
return self.c.WithWaitingStatus(self.c.Tr.UpdatingPatch, func() error {
if !self.c.Git().Patch.PatchBuilder.Active() {
if err := self.startPatchBuilder(); err != nil {
return err
@@ -26,13 +26,13 @@ func (self *ConfirmationController) GetKeybindings(opts types.KeybindingsOpts) [
{
Key: opts.GetKey(opts.Config.Universal.Confirm),
Handler: func() error { return self.context().State.OnConfirm() },
Description: self.c.Tr.LcConfirm,
Description: self.c.Tr.Confirm,
Display: true,
},
{
Key: opts.GetKey(opts.Config.Universal.Return),
Handler: func() error { return self.context().State.OnClose() },
Description: self.c.Tr.LcCloseCancel,
Description: self.c.Tr.CloseCancel,
Display: true,
},
{
@@ -18,17 +18,17 @@ func (self *CustomPatchOptionsMenuAction) Call() error {
menuItems := []*types.MenuItem{
{
Label: "reset patch",
Label: self.c.Tr.ResetPatch,
OnPress: self.c.Helpers().PatchBuilding.Reset,
Key: 'c',
},
{
Label: "apply patch",
Label: self.c.Tr.ApplyPatch,
OnPress: func() error { return self.handleApplyPatch(false) },
Key: 'a',
},
{
Label: "apply patch in reverse",
Label: self.c.Tr.ApplyPatchInReverse,
OnPress: func() error { return self.handleApplyPatch(true) },
Key: 'r',
},
@@ -37,17 +37,17 @@ func (self *CustomPatchOptionsMenuAction) Call() error {
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),
Label: fmt.Sprintf(self.c.Tr.RemovePatchFromOriginalCommit, self.c.Git().Patch.PatchBuilder.To),
OnPress: self.handleDeletePatchFromCommit,
Key: 'd',
},
{
Label: "move patch out into index",
Label: self.c.Tr.MovePatchOutIntoIndex,
OnPress: self.handleMovePatchIntoWorkingTree,
Key: 'i',
},
{
Label: "move patch into new commit",
Label: self.c.Tr.MovePatchIntoNewCommit,
OnPress: self.handlePullPatchIntoNewCommit,
Key: 'n',
},
@@ -62,7 +62,7 @@ func (self *CustomPatchOptionsMenuAction) Call() error {
append(
[]*types.MenuItem{
{
Label: fmt.Sprintf("move patch to selected commit (%s)", selectedCommit.Sha),
Label: fmt.Sprintf(self.c.Tr.MovePatchToSelectedCommit, selectedCommit.Sha),
OnPress: self.handleMovePatchToSelectedCommit,
Key: 'm',
},
@@ -75,7 +75,7 @@ func (self *CustomPatchOptionsMenuAction) Call() error {
menuItems = append(menuItems, []*types.MenuItem{
{
Label: "copy patch to clipboard",
Label: self.c.Tr.CopyPatchToClipboard,
OnPress: func() error { return self.copyPatchToClipboard() },
Key: 'y',
},
+5 -5
View File
@@ -20,7 +20,7 @@ func (self *DiffingMenuAction) Call() error {
name := name
menuItems = append(menuItems, []*types.MenuItem{
{
Label: fmt.Sprintf("%s %s", self.c.Tr.LcDiff, name),
Label: fmt.Sprintf("%s %s", self.c.Tr.Diff, name),
OnPress: func() error {
self.c.Modes().Diffing.Ref = name
// can scope this down based on current view but too lazy right now
@@ -32,10 +32,10 @@ func (self *DiffingMenuAction) Call() error {
menuItems = append(menuItems, []*types.MenuItem{
{
Label: self.c.Tr.LcEnterRefToDiff,
Label: self.c.Tr.EnterRefToDiff,
OnPress: func() error {
return self.c.Prompt(types.PromptOpts{
Title: self.c.Tr.LcEnteRefName,
Title: self.c.Tr.EnteRefName,
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetRefsSuggestionsFunc(),
HandleConfirm: func(response string) error {
self.c.Modes().Diffing.Ref = strings.TrimSpace(response)
@@ -49,14 +49,14 @@ func (self *DiffingMenuAction) Call() error {
if self.c.Modes().Diffing.Active() {
menuItems = append(menuItems, []*types.MenuItem{
{
Label: self.c.Tr.LcSwapDiff,
Label: self.c.Tr.SwapDiff,
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,
Label: self.c.Tr.ExitDiffMode,
OnPress: func() error {
self.c.Modes().Diffing = diffing.New()
return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
+25 -25
View File
@@ -31,12 +31,12 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
{
Key: opts.GetKey(opts.Config.Universal.Select),
Handler: self.checkSelectedFileNode(self.press),
Description: self.c.Tr.LcToggleStaged,
Description: self.c.Tr.ToggleStaged,
},
{
Key: opts.GetKey(opts.Config.Files.OpenStatusFilter),
Handler: self.handleStatusFilterPressed,
Description: self.c.Tr.LcFileFilter,
Description: self.c.Tr.FileFilter,
},
{
Key: opts.GetKey(opts.Config.Files.CommitChanges),
@@ -46,7 +46,7 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
{
Key: opts.GetKey(opts.Config.Files.CommitChangesWithoutHook),
Handler: self.c.Helpers().WorkingTree.HandleWIPCommitPress,
Description: self.c.Tr.LcCommitChangesWithoutHook,
Description: self.c.Tr.CommitChangesWithoutHook,
},
{
Key: opts.GetKey(opts.Config.Files.AmendLastCommit),
@@ -61,39 +61,39 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
{
Key: opts.GetKey(opts.Config.Universal.Edit),
Handler: self.checkSelectedFileNode(self.edit),
Description: self.c.Tr.LcEditFile,
Description: self.c.Tr.EditFile,
},
{
Key: opts.GetKey(opts.Config.Universal.OpenFile),
Handler: self.Open,
Description: self.c.Tr.LcOpenFile,
Description: self.c.Tr.OpenFile,
},
{
Key: opts.GetKey(opts.Config.Files.IgnoreFile),
Handler: self.checkSelectedFileNode(self.ignoreOrExcludeMenu),
Description: self.c.Tr.Actions.LcIgnoreExcludeFile,
Description: self.c.Tr.Actions.IgnoreExcludeFile,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Files.RefreshFiles),
Handler: self.refresh,
Description: self.c.Tr.LcRefreshFiles,
Description: self.c.Tr.RefreshFiles,
},
{
Key: opts.GetKey(opts.Config.Files.StashAllChanges),
Handler: self.stash,
Description: self.c.Tr.LcStashAllChanges,
Description: self.c.Tr.StashAllChanges,
},
{
Key: opts.GetKey(opts.Config.Files.ViewStashOptions),
Handler: self.createStashMenu,
Description: self.c.Tr.LcViewStashOptions,
Description: self.c.Tr.ViewStashOptions,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Files.ToggleStagedAll),
Handler: self.toggleStagedAll,
Description: self.c.Tr.LcToggleStagedAll,
Description: self.c.Tr.ToggleStagedAll,
},
{
Key: opts.GetKey(opts.Config.Universal.GoInto),
@@ -103,29 +103,29 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types
{
Key: opts.GetKey(opts.Config.Commits.ViewResetOptions),
Handler: self.createResetToUpstreamMenu,
Description: self.c.Tr.LcViewResetToUpstreamOptions,
Description: self.c.Tr.ViewResetToUpstreamOptions,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Files.ViewResetOptions),
Handler: self.createResetMenu,
Description: self.c.Tr.LcViewResetOptions,
Description: self.c.Tr.ViewResetOptions,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Files.ToggleTreeView),
Handler: self.toggleTreeView,
Description: self.c.Tr.LcToggleTreeView,
Description: self.c.Tr.ToggleTreeView,
},
{
Key: opts.GetKey(opts.Config.Files.OpenMergeTool),
Handler: self.c.Helpers().WorkingTree.OpenMergeTool,
Description: self.c.Tr.LcOpenMergeTool,
Description: self.c.Tr.OpenMergeTool,
},
{
Key: opts.GetKey(opts.Config.Files.Fetch),
Handler: self.fetch,
Description: self.c.Tr.LcFetch,
Description: self.c.Tr.Fetch,
},
}
}
@@ -559,7 +559,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.c.Git().WorkingTree.Ignore)
err := self.ignoreOrExcludeFile(node, self.c.Tr.IgnoreTracked, self.c.Tr.IgnoreTrackedPrompt, self.c.Tr.Actions.IgnoreExcludeFile, self.c.Git().WorkingTree.Ignore)
if err != nil {
return err
}
@@ -585,10 +585,10 @@ func (self *FilesController) exclude(node *filetree.FileNode) error {
func (self *FilesController) ignoreOrExcludeMenu(node *filetree.FileNode) error {
return self.c.Menu(types.CreateMenuOptions{
Title: self.c.Tr.Actions.LcIgnoreExcludeFile,
Title: self.c.Tr.Actions.IgnoreExcludeFile,
Items: []*types.MenuItem{
{
LabelColumns: []string{self.c.Tr.LcIgnoreFile},
LabelColumns: []string{self.c.Tr.IgnoreFile},
OnPress: func() error {
if err := self.ignore(node); err != nil {
return self.c.Error(err)
@@ -598,7 +598,7 @@ func (self *FilesController) ignoreOrExcludeMenu(node *filetree.FileNode) error
Key: 'i',
},
{
LabelColumns: []string{self.c.Tr.LcExcludeFile},
LabelColumns: []string{self.c.Tr.ExcludeFile},
OnPress: func() error {
if err := self.exclude(node); err != nil {
return self.c.Error(err)
@@ -690,10 +690,10 @@ func (self *FilesController) switchToMerge() error {
func (self *FilesController) createStashMenu() error {
return self.c.Menu(types.CreateMenuOptions{
Title: self.c.Tr.LcStashOptions,
Title: self.c.Tr.StashOptions,
Items: []*types.MenuItem{
{
Label: self.c.Tr.LcStashAllChanges,
Label: self.c.Tr.StashAllChanges,
OnPress: func() error {
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
return self.c.ErrorMsg(self.c.Tr.NoFilesToStash)
@@ -703,7 +703,7 @@ func (self *FilesController) createStashMenu() error {
Key: 'a',
},
{
Label: self.c.Tr.LcStashAllChangesKeepIndex,
Label: self.c.Tr.StashAllChangesKeepIndex,
OnPress: func() error {
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
return self.c.ErrorMsg(self.c.Tr.NoFilesToStash)
@@ -714,14 +714,14 @@ func (self *FilesController) createStashMenu() error {
Key: 'i',
},
{
Label: self.c.Tr.LcStashIncludeUntrackedChanges,
Label: self.c.Tr.StashIncludeUntrackedChanges,
OnPress: func() error {
return self.handleStashSave(self.c.Git().Stash.StashIncludeUntrackedChanges, self.c.Tr.Actions.StashIncludeUntrackedChanges)
},
Key: 'U',
},
{
Label: self.c.Tr.LcStashStagedChanges,
Label: self.c.Tr.StashStagedChanges,
OnPress: func() error {
// there must be something in staging otherwise the current implementation mucks the stash up
if !self.c.Helpers().WorkingTree.AnyStagedFiles() {
@@ -732,7 +732,7 @@ func (self *FilesController) createStashMenu() error {
Key: 's',
},
{
Label: self.c.Tr.LcStashUnstagedChanges,
Label: self.c.Tr.StashUnstagedChanges,
OnPress: func() error {
if !self.c.Helpers().WorkingTree.IsWorkingTreeDirty() {
return self.c.ErrorMsg(self.c.Tr.NoFilesToStash)
@@ -31,7 +31,7 @@ func (self *FilesRemoveController) GetKeybindings(opts types.KeybindingsOpts) []
{
Key: opts.GetKey(opts.Config.Universal.Remove),
Handler: self.checkSelectedFileNode(self.remove),
Description: self.c.Tr.LcViewDiscardOptions,
Description: self.c.Tr.ViewDiscardOptions,
OpensMenu: true,
},
}
@@ -44,7 +44,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
if node.File == nil {
menuItems = []*types.MenuItem{
{
Label: self.c.Tr.LcDiscardAllChanges,
Label: self.c.Tr.DiscardAllChanges,
OnPress: func() error {
self.c.LogAction(self.c.Tr.Actions.DiscardAllChangesInDirectory)
if err := self.c.Git().WorkingTree.DiscardAllDirChanges(node); err != nil {
@@ -64,7 +64,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
if node.GetHasStagedChanges() && node.GetHasUnstagedChanges() {
menuItems = append(menuItems, &types.MenuItem{
Label: self.c.Tr.LcDiscardUnstagedChanges,
Label: self.c.Tr.DiscardUnstagedChanges,
OnPress: func() error {
self.c.LogAction(self.c.Tr.Actions.DiscardUnstagedChangesInDirectory)
if err := self.c.Git().WorkingTree.DiscardUnstagedDirChanges(node); err != nil {
@@ -91,7 +91,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
menuItems = []*types.MenuItem{
{
Label: self.c.Tr.LcSubmoduleStashAndReset,
Label: self.c.Tr.SubmoduleStashAndReset,
OnPress: func() error {
return self.ResetSubmodule(submodule)
},
@@ -100,7 +100,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
} else {
menuItems = []*types.MenuItem{
{
Label: self.c.Tr.LcDiscardAllChanges,
Label: self.c.Tr.DiscardAllChanges,
OnPress: func() error {
self.c.LogAction(self.c.Tr.Actions.DiscardAllChangesInFile)
if err := self.c.Git().WorkingTree.DiscardAllFileChanges(file); err != nil {
@@ -120,7 +120,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
if file.HasStagedChanges && file.HasUnstagedChanges {
menuItems = append(menuItems, &types.MenuItem{
Label: self.c.Tr.LcDiscardUnstagedChanges,
Label: self.c.Tr.DiscardUnstagedChanges,
OnPress: func() error {
self.c.LogAction(self.c.Tr.Actions.DiscardAllUnstagedChangesInFile)
if err := self.c.Git().WorkingTree.DiscardUnstagedFileChanges(file); err != nil {
@@ -145,7 +145,7 @@ func (self *FilesRemoveController) remove(node *filetree.FileNode) error {
}
func (self *FilesRemoveController) ResetSubmodule(submodule *models.SubmoduleConfig) error {
return self.c.WithWaitingStatus(self.c.Tr.LcResettingSubmoduleStatus, func() error {
return self.c.WithWaitingStatus(self.c.Tr.ResettingSubmoduleStatus, func() error {
self.c.LogAction(self.c.Tr.Actions.ResetSubmodule)
file := self.c.Helpers().WorkingTree.FileForSubmodule(submodule)
+3 -3
View File
@@ -30,7 +30,7 @@ func (self *FilteringMenuAction) Call() error {
if fileName != "" {
menuItems = append(menuItems, &types.MenuItem{
Label: fmt.Sprintf("%s '%s'", self.c.Tr.LcFilterBy, fileName),
Label: fmt.Sprintf("%s '%s'", self.c.Tr.FilterBy, fileName),
OnPress: func() error {
return self.setFiltering(fileName)
},
@@ -38,7 +38,7 @@ func (self *FilteringMenuAction) Call() error {
}
menuItems = append(menuItems, &types.MenuItem{
Label: self.c.Tr.LcFilterPathOption,
Label: self.c.Tr.FilterPathOption,
OnPress: func() error {
return self.c.Prompt(types.PromptOpts{
FindSuggestionsFunc: self.c.Helpers().Suggestions.GetFilePathSuggestionsFunc(),
@@ -52,7 +52,7 @@ func (self *FilteringMenuAction) Call() error {
if self.c.Modes().Filtering.Active() {
menuItems = append(menuItems, &types.MenuItem{
Label: self.c.Tr.LcExitFilterMode,
Label: self.c.Tr.ExitFilterMode,
OnPress: self.c.Helpers().Mode.ClearFiltering,
})
}
+1 -1
View File
@@ -30,7 +30,7 @@ func (self *GitFlowController) GetKeybindings(opts types.KeybindingsOpts) []*typ
{
Key: opts.GetKey(opts.Config.Branches.ViewGitFlowOptions),
Handler: self.checkSelected(self.handleCreateGitFlowMenu),
Description: self.c.Tr.LcGitFlowOptions,
Description: self.c.Tr.GitFlowOptions,
OpensMenu: true,
},
}
+8 -8
View File
@@ -24,7 +24,7 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type
{
Key: opts.GetKey(opts.Config.Universal.ExecuteCustomCommand),
Handler: self.customCommand,
Description: self.c.Tr.LcExecuteCustomCommand,
Description: self.c.Tr.ExecuteCustomCommand,
},
{
Key: opts.GetKey(opts.Config.Universal.CreatePatchOptionsMenu),
@@ -41,17 +41,17 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type
{
Key: opts.GetKey(opts.Config.Universal.Refresh),
Handler: self.refresh,
Description: self.c.Tr.LcRefresh,
Description: self.c.Tr.Refresh,
},
{
Key: opts.GetKey(opts.Config.Universal.NextScreenMode),
Handler: self.nextScreenMode,
Description: self.c.Tr.LcNextScreenMode,
Description: self.c.Tr.NextScreenMode,
},
{
Key: opts.GetKey(opts.Config.Universal.PrevScreenMode),
Handler: self.prevScreenMode,
Description: self.c.Tr.LcPrevScreenMode,
Description: self.c.Tr.PrevScreenMode,
},
{
ViewName: "",
@@ -65,26 +65,26 @@ func (self *GlobalController) GetKeybindings(opts types.KeybindingsOpts) []*type
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,
Description: self.c.Tr.OpenMenu,
Handler: self.createOptionsMenu,
},
{
ViewName: "",
Key: opts.GetKey(opts.Config.Universal.FilteringMenu),
Handler: self.createFilteringMenu,
Description: self.c.Tr.LcOpenFilteringMenu,
Description: self.c.Tr.OpenFilteringMenu,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Universal.DiffingMenu),
Handler: self.createDiffingMenu,
Description: self.c.Tr.LcOpenDiffingMenu,
Description: self.c.Tr.OpenDiffingMenu,
OpensMenu: true,
},
{
Key: opts.GetKey(opts.Config.Universal.DiffingMenuAlt),
Handler: self.createDiffingMenu,
Description: self.c.Tr.LcOpenDiffingMenu,
Description: self.c.Tr.OpenDiffingMenu,
OpensMenu: true,
},
{
+10 -12
View File
@@ -1,8 +1,6 @@
package helpers
import (
"fmt"
"github.com/jesseduffield/lazygit/pkg/gui/context"
"github.com/jesseduffield/lazygit/pkg/gui/modes/diffing"
"github.com/jesseduffield/lazygit/pkg/gui/types"
@@ -19,27 +17,29 @@ func NewDiffHelper(c *HelperCommon) *DiffHelper {
}
}
func (self *DiffHelper) DiffStr() string {
output := self.c.Modes().Diffing.Ref
func (self *DiffHelper) DiffArgs() []string {
output := []string{self.c.Modes().Diffing.Ref}
right := self.currentDiffTerminal()
if right != "" {
output += " " + right
output = append(output, right)
}
if self.c.Modes().Diffing.Reverse {
output += " -R"
output = append(output, "-R")
}
if self.c.State().GetIgnoreWhitespaceInDiffView() {
output += " --ignore-all-space"
output = append(output, "--ignore-all-space")
}
output = append(output, "--")
file := self.currentlySelectedFilename()
if file != "" {
output += " -- " + file
output = append(output, file)
} else if self.c.Modes().Filtering.Active() {
output += " -- " + self.c.Modes().Filtering.GetPath()
output = append(output, self.c.Modes().Filtering.GetPath())
}
return output
@@ -51,9 +51,7 @@ func (self *DiffHelper) ExitDiffMode() error {
}
func (self *DiffHelper) RenderDiff() error {
cmdObj := self.c.OS().Cmd.New(
fmt.Sprintf("git diff --submodule --no-ext-diff --color %s", self.DiffStr()),
)
cmdObj := self.c.Git().Diff.DiffCmdObj(self.DiffArgs())
task := types.NewRunPtyTask(cmdObj.GetCmd())
return self.c.RenderToMainViews(types.RefreshMainOpts{
+1 -4
View File
@@ -21,11 +21,10 @@ func NewGpgHelper(c *HelperCommon) *GpgHelper {
// WithWaitingStatus we get stuck there and can't return to lazygit. We could
// fix this bug, or just stop running subprocesses from within there, given that
// we don't need to see a loading status if we're in a subprocess.
// TODO: we shouldn't need to use a shell here, but looks like that NewShell function contains some windows specific quoting stuff. We should centralise that.
func (self *GpgHelper) WithGpgHandling(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error {
useSubprocess := self.c.Git().Config.UsingGpg()
if useSubprocess {
success, err := self.c.RunSubprocess(self.c.OS().Cmd.NewShell(cmdObj.ToString()))
success, err := self.c.RunSubprocess(cmdObj)
if success && onSuccess != nil {
if err := onSuccess(); err != nil {
return err
@@ -42,8 +41,6 @@ func (self *GpgHelper) WithGpgHandling(cmdObj oscommands.ICmdObj, waitingStatus
}
func (self *GpgHelper) runAndStream(cmdObj oscommands.ICmdObj, waitingStatus string, onSuccess func() error) error {
cmdObj = self.c.OS().Cmd.NewShell(cmdObj.ToString())
return self.c.WithWaitingStatus(waitingStatus, func() error {
if err := cmdObj.StreamOutput().Run(); err != nil {
_ = self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
@@ -180,7 +180,7 @@ func (self *MergeAndRebaseHelper) workingTreeStateNoun() string {
// PromptToContinueRebase asks the user if they want to continue the rebase/merge that's in progress
func (self *MergeAndRebaseHelper) PromptToContinueRebase() error {
return self.c.Confirm(types.ConfirmOpts{
Title: "continue",
Title: self.c.Tr.Continue,
Prompt: self.c.Tr.ConflictsResolved,
HandleConfirm: func() error {
return self.genericMergeCommand(REBASE_OPTION_CONTINUE)
+9 -8
View File
@@ -2,6 +2,7 @@ package helpers
import (
"fmt"
"strings"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
@@ -52,8 +53,8 @@ func (self *ModeHelper) Statuses() []ModeStatus {
return self.withResetButton(
fmt.Sprintf(
"%s %s",
self.c.Tr.LcShowingGitDiff,
"git diff "+self.diffHelper.DiffStr(),
self.c.Tr.ShowingGitDiff,
"git diff "+strings.Join(self.diffHelper.DiffArgs(), " "),
),
style.FgMagenta,
)
@@ -63,7 +64,7 @@ func (self *ModeHelper) Statuses() []ModeStatus {
{
IsActive: self.c.Git().Patch.PatchBuilder.Active,
Description: func() string {
return self.withResetButton(self.c.Tr.LcBuildingPatch, style.FgYellow.SetBold())
return self.withResetButton(self.c.Tr.BuildingPatch, style.FgYellow.SetBold())
},
Reset: self.patchBuildingHelper.Reset,
},
@@ -73,7 +74,7 @@ func (self *ModeHelper) Statuses() []ModeStatus {
return self.withResetButton(
fmt.Sprintf(
"%s '%s'",
self.c.Tr.LcFilteringBy,
self.c.Tr.FilteringBy,
self.c.Modes().Filtering.GetPath(),
),
style.FgRed,
@@ -85,9 +86,9 @@ func (self *ModeHelper) Statuses() []ModeStatus {
IsActive: self.c.Modes().CherryPicking.Active,
Description: func() string {
copiedCount := len(self.c.Modes().CherryPicking.CherryPickedCommits)
text := self.c.Tr.LcCommitsCopied
text := self.c.Tr.CommitsCopied
if copiedCount == 1 {
text = self.c.Tr.LcCommitCopied
text = self.c.Tr.CommitCopied
}
return self.withResetButton(
@@ -108,7 +109,7 @@ func (self *ModeHelper) Statuses() []ModeStatus {
Description: func() string {
workingTreeState := self.c.Git().Status.WorkingTreeState()
return self.withResetButton(
presentation.FormatWorkingTreeState(workingTreeState), style.FgYellow,
presentation.FormatWorkingTreeStateTitle(self.c.Tr, workingTreeState), style.FgYellow,
)
},
Reset: self.mergeAndRebaseHelper.AbortMergeOrRebaseWithConfirm,
@@ -118,7 +119,7 @@ func (self *ModeHelper) Statuses() []ModeStatus {
return self.c.Model().BisectInfo.Started()
},
Description: func() string {
return self.withResetButton("bisecting", style.FgGreen)
return self.withResetButton(self.c.Tr.Bisect.Bisecting, style.FgGreen)
},
Reset: self.bisectHelper.Reset,
},
@@ -584,7 +584,7 @@ func (self *RefreshHelper) refreshStatus() {
workingTreeState := self.c.Git().Status.WorkingTreeState()
if workingTreeState != enums.REBASE_MODE_NONE {
status += style.FgYellow.Sprintf("(%s) ", presentation.FormatWorkingTreeState(workingTreeState))
status += style.FgYellow.Sprintf("(%s) ", presentation.FormatWorkingTreeStateLower(self.c.Tr, workingTreeState))
}
name := presentation.GetBranchTextStyle(currentBranch.Name).Sprint(currentBranch.Name)
+7 -5
View File
@@ -121,18 +121,20 @@ func (self *RefsHelper) ResetToRef(ref string, strength string, envVars []string
func (self *RefsHelper) CreateGitResetMenu(ref string) error {
type strengthWithKey struct {
strength string
label string
key types.Key
}
strengths := []strengthWithKey{
{strength: "soft", key: 's'},
{strength: "mixed", key: 'm'},
{strength: "hard", key: 'h'},
// not i18'ing because it's git terminology
{strength: "soft", label: "Soft reset", key: 's'},
{strength: "mixed", label: "Mixed reset", key: 'm'},
{strength: "hard", label: "Hard reset", key: 'h'},
}
menuItems := slices.Map(strengths, func(row strengthWithKey) *types.MenuItem {
return &types.MenuItem{
LabelColumns: []string{
fmt.Sprintf("%s reset", row.strength),
row.label,
style.FgRed.Sprintf("reset --%s %s", row.strength, ref),
},
OnPress: func() error {
@@ -144,7 +146,7 @@ func (self *RefsHelper) CreateGitResetMenu(ref string) error {
})
return self.c.Menu(types.CreateMenuOptions{
Title: fmt.Sprintf("%s %s", self.c.Tr.LcResetTo, ref),
Title: fmt.Sprintf("%s %s", self.c.Tr.ResetTo, ref),
Items: menuItems,
})
}
+1 -1
View File
@@ -88,7 +88,7 @@ func (self *ReposHelper) getCurrentBranch(path string) string {
}
}
return self.c.Tr.LcBranchUnknown
return self.c.Tr.BranchUnknown
}
func (self *ReposHelper) CreateRecentReposMenu() error {
@@ -100,7 +100,7 @@ func (self *SuggestionsHelper) GetBranchNameSuggestionsFunc() func(string) []*ty
// Notably, unlike other suggestion functions we're not showing all the options
// if nothing has been typed because there'll be too much to display efficiently
func (self *SuggestionsHelper) GetFilePathSuggestionsFunc() func(string) []*types.Suggestion {
_ = self.c.WithWaitingStatus(self.c.Tr.LcLoadingFileSuggestions, func() error {
_ = self.c.WithWaitingStatus(self.c.Tr.LoadingFileSuggestions, func() error {
trie := patricia.NewTrie()
// load every non-gitignored file in the repo
ignore, err := gitignore.FromGit()

Some files were not shown because too many files have changed in this diff Show More