Compare commits
151 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2eeff1257b | ||
|
|
c878f34ff1 | ||
|
|
f8db3592e3 | ||
|
|
d073932cec | ||
|
|
a2f7fcd730 | ||
|
|
f96674b24b | ||
|
|
a553f7fb77 | ||
|
|
6c415d1341 | ||
|
|
617e8a05ee | ||
|
|
b21ac990ea | ||
|
|
0740409f43 | ||
|
|
37700908cc | ||
|
|
488c43aaa2 | ||
|
|
bb4fe2653b | ||
|
|
a2ee52142c | ||
|
|
66d735acb5 | ||
|
|
d51b065f2a | ||
|
|
a3a14e9ff4 | ||
|
|
e58376f9f7 | ||
|
|
e8e4fa5957 | ||
|
|
b5d8849c06 | ||
|
|
5d1a9639b6 | ||
|
|
ea136e4e77 | ||
|
|
dcd3b7c058 | ||
|
|
fd8cb6e6d7 | ||
|
|
906ec30cac | ||
|
|
46c146a8c1 | ||
|
|
a8ec044f0e | ||
|
|
d626bcac00 | ||
|
|
123d624141 | ||
|
|
e798aa4b15 | ||
|
|
04e474aa66 | ||
|
|
0662733ad9 | ||
|
|
3c78ba7ed3 | ||
|
|
550c0fd4dc | ||
|
|
0bc0e4ac88 | ||
|
|
117c0bd4f7 | ||
|
|
79848087bc | ||
|
|
a3b820fb5f | ||
|
|
de5133ff90 | ||
|
|
1183de151a | ||
|
|
bfc9881213 | ||
|
|
3db40a79fe | ||
|
|
62393cf28a | ||
|
|
ec82f8099c | ||
|
|
b81bac3d65 | ||
|
|
58ddbae4d1 | ||
|
|
3802b563b0 | ||
|
|
d1134daa53 | ||
|
|
63cb304a82 | ||
|
|
6e579dc6e4 | ||
|
|
d5ec0fdcd1 | ||
|
|
0a63f701e5 | ||
|
|
bccf203a18 | ||
|
|
b590397dce | ||
|
|
755cc9f8d8 | ||
|
|
0e6598adbd | ||
|
|
f2645da16a | ||
|
|
f8f596d097 | ||
|
|
028cb2be2f | ||
|
|
fb69bfd20d | ||
|
|
f4874bbb74 | ||
|
|
eec20b845d | ||
|
|
3a0a9ec33b | ||
|
|
9b57b73f41 | ||
|
|
4fca89bc52 | ||
|
|
fc76b44b45 | ||
|
|
9a087d04eb | ||
|
|
c005b0d92b | ||
|
|
713fae3e32 | ||
|
|
148bf2c070 | ||
|
|
edfb0a26b2 | ||
|
|
f70435a20f | ||
|
|
b92ff3ee3f | ||
|
|
f1ced5539a | ||
|
|
77e9ee64a4 | ||
|
|
9daa47fb2d | ||
|
|
d18c8c8dc3 | ||
|
|
1573a449f8 | ||
|
|
7b19c5ad95 | ||
|
|
b363b75534 | ||
|
|
fc066d2f2e | ||
|
|
53ea7df655 | ||
|
|
533817bda3 | ||
|
|
35f1ccdb1b | ||
|
|
3dc3174d85 | ||
|
|
ae2496cf80 | ||
|
|
2ac33bb83d | ||
|
|
2b4048ebff | ||
|
|
31bcd632c7 | ||
|
|
aa9ef12d43 | ||
|
|
b80fafef02 | ||
|
|
130480555f | ||
|
|
92cc6e883d | ||
|
|
107503c903 | ||
|
|
7ae106d4df | ||
|
|
16dcc8f4db | ||
|
|
eb10ddfccc | ||
|
|
22a6771e51 | ||
|
|
3f96537380 | ||
|
|
a9f04d3925 | ||
|
|
83834a2c2e | ||
|
|
0c3132c6f0 | ||
|
|
b28569a593 | ||
|
|
1aa45b0142 | ||
|
|
39c8577074 | ||
|
|
23285eab40 | ||
|
|
0c2d90a444 | ||
|
|
d65c018875 | ||
|
|
0c135515a5 | ||
|
|
2b9df0ea06 | ||
|
|
b7b30191f1 | ||
|
|
7d1b76a349 | ||
|
|
40f10c3388 | ||
|
|
01e4467d76 | ||
|
|
b4e6850f98 | ||
|
|
c57a0077d0 | ||
|
|
46e500dc28 | ||
|
|
d7865b3882 | ||
|
|
0aad68acf0 | ||
|
|
4969e9ce0a | ||
|
|
17770b9f9b | ||
|
|
3dd88d6138 | ||
|
|
ce7cbe58a0 | ||
|
|
7588d5290b | ||
|
|
9fdf92b226 | ||
|
|
93bf691fd6 | ||
|
|
82022615dd | ||
|
|
fb395bca6e | ||
|
|
f91adf026b | ||
|
|
6d91661d5e | ||
|
|
90983aae65 | ||
|
|
f71b23b890 | ||
|
|
05a23f0e1e | ||
|
|
fd38ad8096 | ||
|
|
d502c43ae8 | ||
|
|
0df02dacc2 | ||
|
|
3258c24fb3 | ||
|
|
e7c657fba0 | ||
|
|
60468d2e17 | ||
|
|
cb78cf7de4 | ||
|
|
94b52af661 | ||
|
|
472288c81b | ||
|
|
258eedb38c | ||
|
|
bc044c64b2 | ||
|
|
e478c254d4 | ||
|
|
44f7fc6f7c | ||
|
|
a13e919d3d | ||
|
|
f92fcfbb47 | ||
|
|
6ccf58c224 | ||
|
|
9190e9beac |
23
.github/workflows/cd.yml
vendored
23
.github/workflows/cd.yml
vendored
@@ -29,26 +29,3 @@ jobs:
|
||||
with:
|
||||
token: ${{secrets.GITHUB_API_TOKEN}}
|
||||
formula: lazygit
|
||||
ppa:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout PPA repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: dawidd6/lazygit-debian
|
||||
token: ${{secrets.GITHUB_API_TOKEN}}
|
||||
fetch-depth: 0
|
||||
- name: Setup git
|
||||
uses: dawidd6/action-git-user-config@v1
|
||||
- name: Update PPA repo
|
||||
run: |
|
||||
version="$(echo "$GITHUB_REF" | sed 's@refs/tags/v@@')"
|
||||
sudo apt update
|
||||
sudo apt install -y git-buildpackage
|
||||
git fetch --tags https://github.com/$GITHUB_REPOSITORY
|
||||
gbp import-ref -u "$version"
|
||||
gbp dch -D xenial -N "$version"-1
|
||||
git add debian/changelog
|
||||
git commit -m "d/changelog: dch $version"
|
||||
gbp tag
|
||||
git push --tags origin master
|
||||
|
||||
38
.github/workflows/ci.yml
vendored
38
.github/workflows/ci.yml
vendored
@@ -22,19 +22,37 @@ jobs:
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}
|
||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
|
||||
restore-keys: |
|
||||
${{runner.os}}-go-
|
||||
- name: Format code
|
||||
run: |
|
||||
if [ $(find . ! -path "./vendor/*" -name "*.go" -exec gofmt -s -d {} \;|wc -l) -gt 0 ]; then
|
||||
find . ! -path "./vendor/*" -name "*.go" -exec gofmt -s -d {} \;
|
||||
exit 1
|
||||
fi
|
||||
- name: Test code
|
||||
run: |
|
||||
./test.sh
|
||||
- name: Build binaries
|
||||
uses: goreleaser/goreleaser-action@v1
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GOFLAGS: -mod=vendor
|
||||
GOARCH: amd64
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
args: --skip-publish --snapshot
|
||||
go-version: 1.16.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/go-build
|
||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-build
|
||||
restore-keys: |
|
||||
${{runner.os}}-go-
|
||||
- name: Build linux binary
|
||||
run: |
|
||||
GOOS=linux go build
|
||||
- name: Build windows binary
|
||||
run: |
|
||||
GOOS=windows go build
|
||||
- name: Build darwin binary
|
||||
run: |
|
||||
GOOS=darwin go build
|
||||
|
||||
6
.github/workflows/lint.yml
vendored
6
.github/workflows/lint.yml
vendored
@@ -12,3 +12,9 @@ jobs:
|
||||
uses: golangci/golangci-lint-action@v2
|
||||
with:
|
||||
version: latest
|
||||
- name: Format code
|
||||
run: |
|
||||
if [ $(find . ! -path "./vendor/*" -name "*.go" -exec gofmt -s -d {} \;|wc -l) -gt 0 ]; then
|
||||
find . ! -path "./vendor/*" -name "*.go" -exec gofmt -s -d {} \;
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -21,7 +21,6 @@ If you're a mere mortal like me and you're tired of hearing how powerful git is
|
||||
- [Binary releases](#binary-releases)
|
||||
- [Homebrew](#homebrew)
|
||||
- [MacPorts](#macports)
|
||||
- [Ubuntu](#ubuntu)
|
||||
- [Void Linux](#void-linux)
|
||||
- [Scoop (Windows)](#scoop-windows)
|
||||
- [Arch Linux](#arch-linux)
|
||||
@@ -82,6 +81,8 @@ sudo port install lazygit
|
||||
|
||||
### Ubuntu
|
||||
|
||||
**Deprecated**: will no longer receive updates.
|
||||
|
||||
Packages for Ubuntu are available via [Launchpad PPA](https://launchpad.net/~lazygit-team).
|
||||
|
||||
```sh
|
||||
|
||||
@@ -41,6 +41,7 @@ gui:
|
||||
skipUnstageLineWarning: false
|
||||
skipStashWarning: true
|
||||
showFileTree: false # for rendering changes files in a tree format
|
||||
showListFooter: true # for seeing the '5 of 20' message in list panels
|
||||
showRandomTip: true
|
||||
showCommandLog: true
|
||||
commandLogSize: 8
|
||||
@@ -54,13 +55,17 @@ git:
|
||||
# extra args passed to `git merge`, e.g. --no-ff
|
||||
args: ''
|
||||
pull:
|
||||
mode: 'merge' # one of 'merge' | 'rebase' | 'ff-only'
|
||||
mode: 'auto' # one of 'auto' | 'merge' | 'rebase' | 'ff-only', auto reads from git configuration
|
||||
skipHookPrefix: WIP
|
||||
autoFetch: true
|
||||
branchLogCmd: 'git log --graph --color=always --abbrev-commit --decorate --date=relative --pretty=medium {{branchName}} --'
|
||||
allBranchesLogCmd: 'git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium'
|
||||
overrideGpg: false # prevents lazygit from spawning a separate process when using GPG
|
||||
disableForcePushing: false
|
||||
parseEmoji: false
|
||||
os:
|
||||
editCommand: '' # see 'Configuring File Editing' section
|
||||
openCommand: ''
|
||||
refresher:
|
||||
refreshInterval: 10 # file/submodule refresh interval in seconds
|
||||
fetchInterval: 60 # re-fetch interval in seconds
|
||||
@@ -98,6 +103,7 @@ keybinding:
|
||||
optionMenu-alt1: '?' # show help menu
|
||||
select: '<space>'
|
||||
goInto: '<enter>'
|
||||
openRecentRepos: '<c-r>'
|
||||
confirm: '<enter>'
|
||||
confirm-alt1: 'y'
|
||||
remove: 'd'
|
||||
@@ -127,7 +133,9 @@ keybinding:
|
||||
diffingMenu-alt: '<c-e>' # deprecated
|
||||
copyToClipboard: '<c-o>'
|
||||
submitEditorText: '<enter>'
|
||||
appendNewline: '<tab>'
|
||||
appendNewline: '<a-enter>'
|
||||
extrasMenu: '@'
|
||||
toggleWhitespaceInDiffView: '<c-w>'
|
||||
status:
|
||||
checkForUpdate: 'u'
|
||||
recentRepos: '<enter>'
|
||||
@@ -146,6 +154,7 @@ keybinding:
|
||||
toggleTreeView: '`'
|
||||
branches:
|
||||
createPullRequest: 'o'
|
||||
viewPullRequestOptions: 'O'
|
||||
checkoutBranchByName: 'c'
|
||||
forceCheckoutBranch: 'F'
|
||||
rebaseBranch: 'r'
|
||||
@@ -213,6 +222,25 @@ os:
|
||||
openCommand: 'open {{filename}}'
|
||||
```
|
||||
|
||||
### Configuring File Editing
|
||||
|
||||
Lazygit will edit a file with the first set editor in the following:
|
||||
|
||||
1. config.yaml
|
||||
|
||||
```yaml
|
||||
os:
|
||||
editCommand: 'vim' # as an example
|
||||
```
|
||||
|
||||
2. \$(git config core.editor)
|
||||
3. \$GIT_EDITOR
|
||||
4. \$VISUAL
|
||||
5. \$EDITOR
|
||||
6. \$(which vi)
|
||||
|
||||
Lazygit will log an error if none of these options are set.
|
||||
|
||||
### Recommended Config Values
|
||||
|
||||
for users of VSCode
|
||||
@@ -227,7 +255,8 @@ os:
|
||||
For color attributes you can choose an array of attributes (with max one color attribute)
|
||||
The available attributes are:
|
||||
|
||||
- default
|
||||
**Colors**
|
||||
|
||||
- black
|
||||
- red
|
||||
- green
|
||||
@@ -236,7 +265,12 @@ The available attributes are:
|
||||
- magenta
|
||||
- cyan
|
||||
- white
|
||||
- '#ff00ff'
|
||||
|
||||
**Modifiers**
|
||||
|
||||
- bold
|
||||
- default
|
||||
- reverse # useful for high-contrast
|
||||
- underline
|
||||
|
||||
|
||||
@@ -35,6 +35,20 @@ customCommands:
|
||||
command: "git flow {{index .PromptResponses 0}} start {{index .PromptResponses 1}}"
|
||||
context: 'localBranches'
|
||||
loadingText: 'creating branch'
|
||||
- key : 'r'
|
||||
description: 'Checkout a remote branch as FETCH_HEAD'
|
||||
command: "git fetch {{index .PromptResponses 0}} {{index .PromptResponses 1}} && git checkout FETCH_HEAD"
|
||||
context: 'remotes'
|
||||
prompts:
|
||||
- type: 'input'
|
||||
title: 'Remote:'
|
||||
initialValue: "{{index .SelectedRemote.Name }}"
|
||||
- type: 'menuFromCommand'
|
||||
title: 'Remote branch:'
|
||||
command: 'git branch -r --list {{index .PromptResponses 0}}/*'
|
||||
filter: '.*{{index .PromptResponses 0}}/(?P<branch>.*)'
|
||||
valueFormat: '{{ .branch }}'
|
||||
labelFormat: '{{ .branch | green }}'
|
||||
```
|
||||
|
||||
Looking at the command assigned to the 'n' key, here's what the result looks like:
|
||||
@@ -79,12 +93,28 @@ The permitted contexts are:
|
||||
|
||||
The permitted prompt fields are:
|
||||
|
||||
| _field_ | _description_ | _required_ |
|
||||
| ------------ | -------------------------------------------------------------------------------- | ---------- |
|
||||
| type | one of 'input' or 'menu' | yes |
|
||||
| title | the title to display in the popup panel | no |
|
||||
| initialValue | (only applicable to 'input' prompts) the initial value to appear in the text box | no |
|
||||
| options | (only applicable to 'menu' prompts) the options to display in the menu | no |
|
||||
| _field_ | _description_ | _required_ |
|
||||
| ------------ | -------------------------------------------------------------------------------- | ---------- |
|
||||
| type | one of 'input' or 'menu' | yes |
|
||||
| title | the title to display in the popup panel | no |
|
||||
| initialValue | (only applicable to 'input' prompts) the initial value to appear in the text box | no |
|
||||
| options | (only applicable to 'menu' prompts) the options to display in the menu | no |
|
||||
| command | (only applicable to 'menuFromCommand' prompts) the command to run to generate | yes |
|
||||
| | menu options | |
|
||||
| filter | (only applicable to 'menuFromCommand' prompts) the regexp to run specifying | yes |
|
||||
| | groups which are going to be kept from the command's output | |
|
||||
| valueFormat | (only applicable to 'menuFromCommand' prompts) how to format matched groups from | yes |
|
||||
| | the filter to construct a menu item's value (What gets appended to prompt | |
|
||||
| | responses when the item is selected). You can use named groups, | |
|
||||
| | or `{{ .group_GROUPID }}`. | |
|
||||
| | PS: named groups keep first match only | |
|
||||
| labelFormat | (only applicable to 'menuFromCommand' prompts) how to format matched groups from | no |
|
||||
| | the filter to construct the item's label (What's shown on screen). You can use | |
|
||||
| | named groups, or `{{ .group_GROUPID }}`. You can also color each match with | |
|
||||
| | `{{ .group_GROUPID | colorname }}` (Color names from | |
|
||||
| | [here](https://github.com/jesseduffield/lazygit/blob/master/docs/Config.md)) | |
|
||||
| | If `labelFormat` is not specified, `valueFormat` is shown instead. | |
|
||||
| | PS: named groups keep first match only | |
|
||||
|
||||
The permitted option fields are:
|
||||
| _field_ | _description_ | _required_ |
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
## Global Keybindings
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+r</kbd>: switch to a recent repo (<c-r>)
|
||||
<kbd>pgup</kbd>: scroll up main panel (fn+up)
|
||||
<kbd>pgdown</kbd>: scroll down main panel (fn+down)
|
||||
<kbd>m</kbd>: view merge/rebase options
|
||||
@@ -16,9 +17,10 @@
|
||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: prev screen mode
|
||||
<kbd>:</kbd>: execute custom command
|
||||
<kbd>|</kbd>: view filter-by-path options
|
||||
<kbd>ctrl+s</kbd>: view filter-by-path options
|
||||
<kbd>W</kbd>: open diff menu
|
||||
<kbd>ctrl+e</kbd>: open diff menu
|
||||
<kbd>@</kbd>: open command log menu
|
||||
</pre>
|
||||
|
||||
## List Panel Navigation
|
||||
@@ -38,6 +40,7 @@
|
||||
<pre>
|
||||
<kbd>space</kbd>: checkout
|
||||
<kbd>o</kbd>: create pull request
|
||||
<kbd>O</kbd>: create pull request options
|
||||
<kbd>ctrl+y</kbd>: copy pull request URL to clipboard
|
||||
<kbd>c</kbd>: checkout by name
|
||||
<kbd>F</kbd>: force checkout
|
||||
@@ -122,7 +125,7 @@
|
||||
<kbd>g</kbd>: reset to this commit
|
||||
<kbd>f</kbd>: fixup commit
|
||||
<kbd>F</kbd>: create fixup commit for this commit
|
||||
<kbd>S</kbd>: squash above commits
|
||||
<kbd>S</kbd>: squash all 'fixup!' commits above selected commit (autosquash)
|
||||
<kbd>d</kbd>: delete commit
|
||||
<kbd>ctrl+j</kbd>: move commit down one
|
||||
<kbd>ctrl+k</kbd>: move commit up one
|
||||
@@ -154,6 +157,12 @@
|
||||
<kbd>ctrl+o</kbd>: copy commit SHA to clipboard
|
||||
</pre>
|
||||
|
||||
## Extras Panel
|
||||
|
||||
<pre>
|
||||
<kbd>@</kbd>: open command log menu
|
||||
</pre>
|
||||
|
||||
## Files Panel (Files)
|
||||
|
||||
<pre>
|
||||
@@ -176,6 +185,8 @@
|
||||
<kbd>ctrl+o</kbd>: copy the file name to the clipboard
|
||||
<kbd>g</kbd>: view upstream reset options
|
||||
<kbd>`</kbd>: toggle file tree view
|
||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
||||
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||
</pre>
|
||||
|
||||
## Files Panel (Submodules)
|
||||
@@ -195,6 +206,7 @@
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: return to files panel
|
||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
||||
<kbd>space</kbd>: pick hunk
|
||||
<kbd>b</kbd>: pick both hunks
|
||||
<kbd>◄</kbd>: select previous conflict
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# Lazygit Sneltoetsen
|
||||
|
||||
## Globaale Sneltoetsen
|
||||
## Globale Sneltoetsen
|
||||
|
||||
<pre>
|
||||
<kbd>pgup</kbd>: scroll naar beneden vanaf hooft paneel (fn+up)
|
||||
<kbd>pgdown</kbd>: scroll naar beneden vabaf hooft paneel (fn+down)
|
||||
<kbd>ctrl+r</kbd>: wissel naar een recente repo (<c-r>)
|
||||
<kbd>pgup</kbd>: scroll naar beneden vanaf hoofdpaneel (fn+up)
|
||||
<kbd>pgdown</kbd>: scroll naar beneden vanaf hoofdpaneel (fn+down)
|
||||
<kbd>m</kbd>: bekijk merge/rebase opties
|
||||
<kbd>ctrl+p</kbd>: bekijk aangepaste patch opties
|
||||
<kbd>P</kbd>: push
|
||||
@@ -13,31 +14,33 @@
|
||||
<kbd>x</kbd>: open menu
|
||||
<kbd>z</kbd>: ongedaan maken (via reflog) (experimenteel)
|
||||
<kbd>ctrl+z</kbd>: redo (via reflog) (experimenteel)
|
||||
<kbd>+</kbd>: volgende schermmode (normaal/half/groot )
|
||||
<kbd>_</kbd>: vorige schermmode
|
||||
<kbd>:</kbd>: voor aangepast commando uit
|
||||
<kbd>|</kbd>: bekijk scoping opties
|
||||
<kbd>+</kbd>: volgende scherm modus (normaal/half/groot)
|
||||
<kbd>_</kbd>: vorige scherm modus
|
||||
<kbd>:</kbd>: voor aangepaste commando uit
|
||||
<kbd>ctrl+s</kbd>: bekijk scoping opties
|
||||
<kbd>W</kbd>: open diff menu
|
||||
<kbd>ctrl+e</kbd>: open diff menu
|
||||
<kbd>@</kbd>: open command log menu
|
||||
</pre>
|
||||
|
||||
## List Panel Navigation
|
||||
## Lijstpaneel Navigatie
|
||||
|
||||
<pre>
|
||||
<kbd>.</kbd>: volgende pagina
|
||||
<kbd>,</kbd>: vorige pagina
|
||||
<kbd><</kbd>: scroll naar boven
|
||||
<kbd>></kbd>: scroll naar beneden
|
||||
<kbd>/</kbd>: start met zoekken
|
||||
<kbd>]</kbd>: volgende tab
|
||||
<kbd>[</kbd>: vorige tab
|
||||
<kbd>/</kbd>: start met zoeken
|
||||
<kbd>]</kbd>: volgende tabblad
|
||||
<kbd>[</kbd>: vorige tabblad
|
||||
</pre>
|
||||
|
||||
## Branches Paneel (Branches Tab)
|
||||
## Branches Paneel (Branches Tabblad)
|
||||
|
||||
<pre>
|
||||
<kbd>space</kbd>: uitchecken
|
||||
<kbd>o</kbd>: maak een pull-aanvraag
|
||||
<kbd>o</kbd>: maak een pull-request
|
||||
<kbd>O</kbd>: bekijk opties voor pull-aanvraag
|
||||
<kbd>ctrl+y</kbd>: kopieer de URL van het pull-verzoek naar het klembord
|
||||
<kbd>c</kbd>: uitchecken bij naam
|
||||
<kbd>F</kbd>: forceer checkout
|
||||
@@ -49,16 +52,16 @@
|
||||
<kbd>f</kbd>: fast-forward deze branch vanaf zijn upstream
|
||||
<kbd>g</kbd>: bekijk reset opties
|
||||
<kbd>R</kbd>: hernoem branch
|
||||
<kbd>ctrl+o</kbd>: copieer branch name naar clipboard
|
||||
<kbd>enter</kbd>: view commits
|
||||
<kbd>ctrl+o</kbd>: kopieer branch name naar klembord
|
||||
<kbd>enter</kbd>: bekijk commits
|
||||
</pre>
|
||||
|
||||
## Branches Paneel (Remote Branches (in Remotes tab))
|
||||
## Branches Paneel (Remote Branches (in Remotes tabblad))
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: Ga terug naar remotes lijst
|
||||
<kbd>g</kbd>: bekijk reset opties
|
||||
<kbd>enter</kbd>: view commits
|
||||
<kbd>enter</kbd>: bekijk commits
|
||||
<kbd>space</kbd>: uitchecken
|
||||
<kbd>n</kbd>: nieuwe branch
|
||||
<kbd>M</kbd>: merge in met huidige checked out branch
|
||||
@@ -67,7 +70,7 @@
|
||||
<kbd>u</kbd>: stel in als upstream van uitgecheckte branch
|
||||
</pre>
|
||||
|
||||
## Branches Paneel (Remotes Tab)
|
||||
## Branches Paneel (Remotes Tabblad)
|
||||
|
||||
<pre>
|
||||
<kbd>f</kbd>: fetch remote
|
||||
@@ -83,13 +86,13 @@
|
||||
<kbd>space</kbd>: checkout commit
|
||||
<kbd>g</kbd>: bekijk reset opties
|
||||
<kbd>n</kbd>: nieuwe branch
|
||||
<kbd>c</kbd>: kopiëer commit (cherry-pick)
|
||||
<kbd>C</kbd>: kopiëer commit reeks (cherry-pick)
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (gecopieerde) commits selectie
|
||||
<kbd>ctrl+o</kbd>: copieer commit SHA naar clipboard
|
||||
<kbd>c</kbd>: kopieer commit (cherry-pick)
|
||||
<kbd>C</kbd>: kopieer commit reeks (cherry-pick)
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (gekopieerde) commits selectie
|
||||
<kbd>ctrl+o</kbd>: kopieer commit SHA naar klembord
|
||||
</pre>
|
||||
|
||||
## Branches Paneel (Tags Tab)
|
||||
## Branches Paneel (Tags Tabblad)
|
||||
|
||||
<pre>
|
||||
<kbd>space</kbd>: uitchecken
|
||||
@@ -97,7 +100,7 @@
|
||||
<kbd>P</kbd>: push tag
|
||||
<kbd>n</kbd>: creëer tag
|
||||
<kbd>g</kbd>: bekijk reset opties
|
||||
<kbd>enter</kbd>: view commits
|
||||
<kbd>enter</kbd>: bekijk commits
|
||||
</pre>
|
||||
|
||||
## Commit bestanden Paneel
|
||||
@@ -109,8 +112,8 @@
|
||||
<kbd>o</kbd>: open bestand
|
||||
<kbd>e</kbd>: verander bestand
|
||||
<kbd>space</kbd>: toggle bestand inbegrepen in patch
|
||||
<kbd>enter</kbd>: enter bestand to add selecteered lines to the patch
|
||||
<kbd>`</kbd>: toggle file tree view
|
||||
<kbd>enter</kbd>: enter bestand om geselecteerde regels toe te voegen aan de patch
|
||||
<kbd>`</kbd>: toggle bestandsboom weergave
|
||||
</pre>
|
||||
|
||||
## Commits Paneel (Commits)
|
||||
@@ -130,28 +133,34 @@
|
||||
<kbd>A</kbd>: wijzig commit met staged veranderingen
|
||||
<kbd>p</kbd>: kies commit (wanneer midden in rebase)
|
||||
<kbd>t</kbd>: commit ongedaan maken
|
||||
<kbd>c</kbd>: kopiëer commit (cherry-pick)
|
||||
<kbd>ctrl+o</kbd>: copieer commit SHA naar clipboard
|
||||
<kbd>C</kbd>: kopiëer commit reeks (cherry-pick)
|
||||
<kbd>c</kbd>: kopieer commit (cherry-pick)
|
||||
<kbd>ctrl+o</kbd>: kopieer commit SHA naar klembord
|
||||
<kbd>C</kbd>: kopieer commit reeks (cherry-pick)
|
||||
<kbd>v</kbd>: plak commits (cherry-pick)
|
||||
<kbd>enter</kbd>: bekijk gecommite bestanden
|
||||
<kbd>space</kbd>: checkout commit
|
||||
<kbd>n</kbd>: create new branch off of commit
|
||||
<kbd>n</kbd>: creëer nieuwe branch van commit
|
||||
<kbd>T</kbd>: tag commit
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (gecopieerde) commits selectie
|
||||
<kbd>ctrl+y</kbd>: copieer commit bericht naar clipboard
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (gekopieerde) commits selectie
|
||||
<kbd>ctrl+y</kbd>: kopieer commit bericht naar klembord
|
||||
</pre>
|
||||
|
||||
## Commits Paneel (Reflog Tab)
|
||||
## Commits Paneel (Reflog Tabblad)
|
||||
|
||||
<pre>
|
||||
<kbd>enter</kbd>: bekijk gecommite bestanden
|
||||
<kbd>space</kbd>: checkout commit
|
||||
<kbd>g</kbd>: bekijk reset opties
|
||||
<kbd>c</kbd>: kopiëer commit (cherry-pick)
|
||||
<kbd>C</kbd>: kopiëer commit reeks (cherry-pick)
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (gecopieerde) commits selectie
|
||||
<kbd>ctrl+o</kbd>: copieer commit SHA naar clipboard
|
||||
<kbd>c</kbd>: kopieer commit (cherry-pick)
|
||||
<kbd>C</kbd>: kopieer commit reeks (cherry-pick)
|
||||
<kbd>ctrl+r</kbd>: reset cherry-picked (gekopieerde) commits selectie
|
||||
<kbd>ctrl+o</kbd>: kopieer commit SHA naar klembord
|
||||
</pre>
|
||||
|
||||
## Extras Paneel
|
||||
|
||||
<pre>
|
||||
<kbd>@</kbd>: open command log menu
|
||||
</pre>
|
||||
|
||||
## Bestanden Paneel (Bestanden)
|
||||
@@ -175,26 +184,29 @@
|
||||
<kbd>f</kbd>: fetch
|
||||
<kbd>ctrl+o</kbd>: kopieer de bestandsnaam naar het klembord
|
||||
<kbd>g</kbd>: bekijk upstream reset opties
|
||||
<kbd>`</kbd>: toggle file tree view
|
||||
<kbd>`</kbd>: toggle bestandsboom weergave
|
||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
||||
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||
</pre>
|
||||
|
||||
## Bestanden Paneel (Submodules)
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+o</kbd>: copy submodule name to clipboard
|
||||
<kbd>ctrl+o</kbd>: kopieer submodule naam naar klembord
|
||||
<kbd>enter</kbd>: enter submodule
|
||||
<kbd>d</kbd>: view reset and remove submodule options
|
||||
<kbd>d</kbd>: bekijk reset en verwijder submodule opties
|
||||
<kbd>u</kbd>: update submodule
|
||||
<kbd>n</kbd>: add new submodule
|
||||
<kbd>n</kbd>: voeg nieuwe submodule toe
|
||||
<kbd>e</kbd>: update submodule URL
|
||||
<kbd>i</kbd>: initialize submodule
|
||||
<kbd>b</kbd>: view bulk submodule options
|
||||
<kbd>i</kbd>: initialiseer submodule
|
||||
<kbd>b</kbd>: bekijk bulk submodule opties
|
||||
</pre>
|
||||
|
||||
## Hooft Paneel (Merggen)
|
||||
## Hoofd Paneel (Mergen)
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: ga terug naar het bestanden paneel
|
||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
||||
<kbd>space</kbd>: kies hunk
|
||||
<kbd>b</kbd>: kies bijde hunks
|
||||
<kbd>◄</kbd>: selecteer voorgaand conflict
|
||||
@@ -204,29 +216,29 @@
|
||||
<kbd>z</kbd>: ongedaan maken
|
||||
</pre>
|
||||
|
||||
## Hooft Paneel (Normaal)
|
||||
## Hoofd Paneel (Normaal)
|
||||
|
||||
<pre>
|
||||
<kbd>Ő</kbd>: scroll omlaag (fn+up)
|
||||
<kbd>ő</kbd>: scroll omhoog (fn+down)
|
||||
</pre>
|
||||
|
||||
## Hooft Paneel (Patch Bouwen)
|
||||
## Hoofd Paneel (Patch Bouwen)
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: sluit lijn-bij-lijn mode
|
||||
<kbd>esc</kbd>: sluit lijn-bij-lijn modus
|
||||
<kbd>o</kbd>: open bestand
|
||||
<kbd>▲</kbd>: selecteer de vorige lijn
|
||||
<kbd>▼</kbd>: selecteer de volgende lijn
|
||||
<kbd>◄</kbd>: selecteer de vorige hunk
|
||||
<kbd>►</kbd>: selecteer de volgende hunk
|
||||
<kbd>space</kbd>: voeg toe/verwijder lijn(en) in patch
|
||||
<kbd>v</kbd>: toggle drag selecteer
|
||||
<kbd>V</kbd>: toggle drag selecteer
|
||||
<kbd>a</kbd>: toggle selecteer hunk
|
||||
<kbd>v</kbd>: toggle drag selecteer
|
||||
<kbd>V</kbd>: toggle drag selecteer
|
||||
<kbd>a</kbd>: toggle selecteer hunk
|
||||
</pre>
|
||||
|
||||
## Hooft Paneel (Staging)
|
||||
## Hoofd Paneel (Staging)
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: ga terug naar het bestanden paneel
|
||||
@@ -240,9 +252,9 @@
|
||||
<kbd>►</kbd>: selecteer de volgende hunk
|
||||
<kbd>e</kbd>: verander bestand
|
||||
<kbd>o</kbd>: open bestand
|
||||
<kbd>v</kbd>: toggle drag selecteer
|
||||
<kbd>V</kbd>: toggle drag selecteer
|
||||
<kbd>a</kbd>: toggle selecteer hunk
|
||||
<kbd>v</kbd>: toggle drag selecteer
|
||||
<kbd>V</kbd>: toggle drag selecteer
|
||||
<kbd>a</kbd>: toggle selecteer hunk
|
||||
<kbd>c</kbd>: Commit veranderingen
|
||||
<kbd>w</kbd>: commit veranderingen zonder pre-commit hook
|
||||
<kbd>C</kbd>: commit veranderingen met de git editor
|
||||
@@ -257,7 +269,7 @@
|
||||
## Stash Paneel
|
||||
|
||||
<pre>
|
||||
<kbd>enter</kbd>: view stash entry's files
|
||||
<kbd>enter</kbd>: bekijk bestanden van stash entry
|
||||
<kbd>space</kbd>: toepassen
|
||||
<kbd>g</kbd>: pop
|
||||
<kbd>d</kbd>: laten vallen
|
||||
@@ -271,5 +283,5 @@
|
||||
<kbd>o</kbd>: open config bestand
|
||||
<kbd>u</kbd>: check voor updates
|
||||
<kbd>enter</kbd>: wissel naar een recente repo
|
||||
<kbd>a</kbd>: alle takken van het houtblok laten zien
|
||||
<kbd>a</kbd>: alle logs van de branch laten zien
|
||||
</pre>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
## Globalne
|
||||
|
||||
<pre>
|
||||
<kbd>ctrl+r</kbd>: switch to a recent repo (<c-r>)
|
||||
<kbd>pgup</kbd>: scroll up main panel (fn+up)
|
||||
<kbd>pgdown</kbd>: scroll down main panel (fn+down)
|
||||
<kbd>m</kbd>: view merge/rebase options
|
||||
@@ -16,9 +17,10 @@
|
||||
<kbd>+</kbd>: next screen mode (normal/half/fullscreen)
|
||||
<kbd>_</kbd>: prev screen mode
|
||||
<kbd>:</kbd>: execute custom command
|
||||
<kbd>|</kbd>: view filter-by-path options
|
||||
<kbd>ctrl+s</kbd>: view filter-by-path options
|
||||
<kbd>W</kbd>: open diff menu
|
||||
<kbd>ctrl+e</kbd>: open diff menu
|
||||
<kbd>@</kbd>: open command log menu
|
||||
</pre>
|
||||
|
||||
## List Panel Navigation
|
||||
@@ -38,6 +40,7 @@
|
||||
<pre>
|
||||
<kbd>space</kbd>: przełącz
|
||||
<kbd>o</kbd>: utwórz żądanie wyciągnięcia
|
||||
<kbd>O</kbd>: utwórz opcje żądania ściągnięcia
|
||||
<kbd>ctrl+y</kbd>: skopiuj adres URL żądania ściągnięcia do schowka
|
||||
<kbd>c</kbd>: przełącz używając nazwy
|
||||
<kbd>F</kbd>: wymuś przełączenie
|
||||
@@ -122,7 +125,7 @@
|
||||
<kbd>g</kbd>: zresetuj do tego commita
|
||||
<kbd>f</kbd>: napraw commit
|
||||
<kbd>F</kbd>: create fixup commit for this commit
|
||||
<kbd>S</kbd>: squash above commits
|
||||
<kbd>S</kbd>: squash all 'fixup!' commits above selected commits (autosquash)
|
||||
<kbd>d</kbd>: delete commit
|
||||
<kbd>ctrl+j</kbd>: move commit down one
|
||||
<kbd>ctrl+k</kbd>: move commit up one
|
||||
@@ -154,6 +157,12 @@
|
||||
<kbd>ctrl+o</kbd>: copy commit SHA to clipboard
|
||||
</pre>
|
||||
|
||||
## Extras Panel
|
||||
|
||||
<pre>
|
||||
<kbd>@</kbd>: open command log menu
|
||||
</pre>
|
||||
|
||||
## Pliki Panel (Pliki)
|
||||
|
||||
<pre>
|
||||
@@ -176,6 +185,8 @@
|
||||
<kbd>ctrl+o</kbd>: copy the file name to the clipboard
|
||||
<kbd>g</kbd>: view upstream reset options
|
||||
<kbd>`</kbd>: toggle file tree view
|
||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
||||
<kbd>ctrl+w</kbd>: Toggle whether or not whitespace changes are shown in the diff view
|
||||
</pre>
|
||||
|
||||
## Pliki Panel (Submodules)
|
||||
@@ -195,6 +206,7 @@
|
||||
|
||||
<pre>
|
||||
<kbd>esc</kbd>: wróć do panelu plików
|
||||
<kbd>M</kbd>: open external merge tool (git mergetool)
|
||||
<kbd>space</kbd>: pick hunk
|
||||
<kbd>b</kbd>: pick both hunks
|
||||
<kbd>◄</kbd>: select previous conflict
|
||||
|
||||
21
go.mod
21
go.mod
@@ -9,38 +9,39 @@ require (
|
||||
github.com/cli/safeexec v1.0.0
|
||||
github.com/cloudfoundry/jibber_jabber v0.0.0-20151120183258-bcc4c8345a21
|
||||
github.com/creack/pty v1.1.11
|
||||
github.com/fatih/color v1.9.0
|
||||
github.com/fatih/color v1.9.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.7
|
||||
github.com/gdamore/tcell/v2 v2.2.0 // indirect
|
||||
github.com/go-errors/errors v1.1.1
|
||||
github.com/gdamore/tcell/v2 v2.3.11 // indirect
|
||||
github.com/go-errors/errors v1.4.0
|
||||
github.com/go-logfmt/logfmt v0.5.0 // indirect
|
||||
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3
|
||||
github.com/golang/protobuf v1.3.2 // indirect
|
||||
github.com/google/go-cmp v0.3.1 // indirect
|
||||
github.com/gookit/color v1.4.2
|
||||
github.com/imdario/mergo v0.3.11
|
||||
github.com/integrii/flaggy v1.4.0
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210417110745-37f79434200d
|
||||
github.com/jesseduffield/termbox-go v0.0.0-20200823212418-a2289ed6aafe // indirect
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210614081440-74b42ecad52b
|
||||
github.com/jesseduffield/yaml v2.1.0+incompatible
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/kyokomi/emoji/v2 v2.2.8
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.7 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.12
|
||||
github.com/mattn/go-runewidth v0.0.13
|
||||
github.com/mgutz/str v1.2.0
|
||||
github.com/onsi/ginkgo v1.10.3 // indirect
|
||||
github.com/onsi/gomega v1.7.1 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/sahilm/fuzzy v0.1.0
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/stretchr/testify v1.6.1
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 // indirect
|
||||
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c // indirect
|
||||
golang.org/x/sys v0.0.0-20210415045647-66c3f260301c // indirect
|
||||
golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72 // indirect
|
||||
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 // indirect
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 // indirect
|
||||
golang.org/x/text v0.3.6 // indirect
|
||||
)
|
||||
|
||||
|
||||
116
go.sum
116
go.sum
@@ -23,7 +23,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/fatih/color v1.7.1-0.20180516100307-2d684516a886 h1:NAFoy+QgUpERgK3y1xiVh5HcOvSeZHpXTTo5qnvnuK4=
|
||||
github.com/fatih/color v1.7.1-0.20180516100307-2d684516a886/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
@@ -33,24 +32,20 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
|
||||
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
|
||||
github.com/gdamore/tcell/v2 v2.0.0 h1:GRWG8aLfWAlekj9Q6W29bVvkHENc6hp79XOqG4AWDOs=
|
||||
github.com/gdamore/tcell/v2 v2.0.0/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
|
||||
github.com/gdamore/tcell/v2 v2.1.0 h1:UnSmozHgBkQi2PGsFr+rpdXuAPRRucMegpQp3Z3kDro=
|
||||
github.com/gdamore/tcell/v2 v2.1.0/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA=
|
||||
github.com/gdamore/tcell/v2 v2.2.0 h1:vSyEgKwraXPSOkvCk7IwOSyX+Pv3V2cV9CikJMXg4U4=
|
||||
github.com/gdamore/tcell/v2 v2.2.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
||||
github.com/gdamore/tcell/v2 v2.3.11 h1:ECO6WqHGbKZ3HrSL7bG/zArMCmLaNr5vcjjMVnLHpzc=
|
||||
github.com/gdamore/tcell/v2 v2.3.11/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-errors/errors v1.1.1 h1:ljK/pL5ltg3qoN+OtN6yCv9HWSfMwxSx90GJCZQxYNg=
|
||||
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-errors/errors v1.4.0 h1:2OA7MFw38+e9na72T1xgkomPb6GzZzzxvJ5U630FoRM=
|
||||
github.com/go-errors/errors v1.4.0/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/go-billy/v5 v5.0.0 h1:7NQHvd9FVid8VL4qVUMm8XifBK+2xCoZ2lSk0agRrHM=
|
||||
github.com/go-git/go-billy/v5 v5.0.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12 h1:PbKy9zOy4aAKrJ5pibIRpVO2BXnK1Tlcg+caKI7Ox5M=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.0.2-0.20200613231340-f56387b50c12/go.mod h1:m+ICp2rF3jDhFgEZ/8yziagdT1C+ZpZcrJjappBCDSw=
|
||||
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
@@ -62,9 +57,10 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk=
|
||||
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
|
||||
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
|
||||
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
@@ -74,54 +70,8 @@ github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOl
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4 h1:GOQrmaE8i+KEdB8NzAegKYd4tPn/inM0I1uo0NXFerg=
|
||||
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20161105104656-d666c9f652af h1:9ZI/QyVOerYYeqMt4svycU2Lz0WvxNHCpHHbsFsi/oA=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20161105104656-d666c9f652af/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20201010224802-8a6768078fd7 h1:K3MGrjmpPtIhfXmKh/zsIF0CdmNKOkjpIwcUfAa/J2A=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20201010224802-8a6768078fd7/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20201224041937-f5a9733d1860 h1:1xfQM6T5A4jqcVvUnYaKR6bGrOhDLWQsp79JFNJpzcQ=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20201224041937-f5a9733d1860/go.mod h1:9LmtJcK+Kwiuc2huslzS37uFJPdHka2Cs/cQ06JZdbk=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210208224444-2eecee85583d h1:Jto9W9w8CFwZiAYXa7LsHDEOb5cKCA1f5LOL1A3jva4=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210208224444-2eecee85583d/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210329125022-96b1d3106429 h1:Ih3UVczKRabZnQ7RisGi5uItC2QJxdqgef7AClJ2G9A=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210329125022-96b1d3106429/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210329125502-e830abf4b73a h1:RVYf2MA/RJbodE+S0e2z++JmB9A7hD1lUsI0euv1fmA=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210329125502-e830abf4b73a/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210329130738-e026850021e3 h1:UDiArPlzkg+8mmNjhUOamQoyiTSzQUGIpOsu5hCRJVI=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210329130738-e026850021e3/go.mod h1:2RtZznzYKt8RLRwvFiSkXjU0Ei8WwHdubgnlaYH47dw=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210329131148-bcc4dcd991ff h1:fTt3EzLtpsc7OA7A6Vd6JVnlxvcAy7cY9lmN9yzDwSs=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210329131148-bcc4dcd991ff/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210402033412-1238f910f001 h1:1WH+lTSK5YMr8emISHPA+VqYDDcLei6djuSxBCLIaiI=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210402033412-1238f910f001/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210402040718-77a1b9631715 h1:nELTdFJiZk3vv7j8nWoHvl7H2IqTr26EHKl6LaorRA8=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210402040718-77a1b9631715/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210402113210-6fd7ef27ce76 h1:miXVlortFNTlOX+KiKW3cVxOR6+Uhl4pnQRei2X26Y4=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210402113210-6fd7ef27ce76/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210403045716-a3be78c4ccf6 h1:nENhj0TKu+11RrPm9Ls5YtzkpbNHM0faXr9UECDhODQ=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210403045716-a3be78c4ccf6/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210405041826-439abd8b6e07 h1:BymGR28auSeuW0QELl0JomK0iFLPS/WRjFlc1iGZiOQ=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210405041826-439abd8b6e07/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210405093708-e79dab8f7772 h1:dg9krj10Udac4IcvlVCOAPktQkfggkgtqRmbDKk7Pzw=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210405093708-e79dab8f7772/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210406065811-95ef6e13779b h1:3+4+muhhikpls5FePXSRNFgcdoPx8dTdqaCy3AqLz98=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210406065811-95ef6e13779b/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210406065942-1b0c68414064 h1:Oe+QJuUIOd2TU+A3BW5sT1eXqceoBcOOfyoHlGf7F8Y=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210406065942-1b0c68414064/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210409121040-210802112d8a h1:ocrSuZxQIgWWt27b+rjiyIIPz6fzfFeoL5Q4cpa2cAo=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210409121040-210802112d8a/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210410011117-a2bb4baca390 h1:Es72JiUjt01TtvqCugdvOR91baB3DhuWF1DNuxA0frA=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210410011117-a2bb4baca390/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210412111008-6ef019af3724 h1:U70Do3/OSw5n/oLJGPWsQHnos2p0yq8yAeD2muioJhQ=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210412111008-6ef019af3724/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210412113212-ee65bd542c08 h1:d003y2GByfR3PqN/JvxNuqyo8vx4m0epwY2hW7sNU80=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210412113212-ee65bd542c08/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210412130453-de7bb5079f9f h1:JPpHlvSrKNxro+K9rM3nEHCdZ16qD0hnEedHPF07OtA=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210412130453-de7bb5079f9f/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210417105214-bdf37de5c917 h1:H4THGOdAJf61wByuq8EHF/NAgtqrTxpSIPsrCXU9HAY=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210417105214-bdf37de5c917/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210417110745-37f79434200d h1:2BPcc19W0j576hvhxtKma4jcD/+qAYvw1ln2HcIEZGU=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210417110745-37f79434200d/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/termbox-go v0.0.0-20200823212418-a2289ed6aafe h1:qsVhCf2RFyyKIUe/+gJblbCpXMUki9rZrHuEctg6M/E=
|
||||
github.com/jesseduffield/termbox-go v0.0.0-20200823212418-a2289ed6aafe/go.mod h1:anMibpZtqNxjDbxrcDEAwSdaJ37vyUeM1f/M4uekib4=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210614081440-74b42ecad52b h1:Wc2zx6xKLNaHc7/wIO6iYyDSjcFGN1Osd6tQvbgMmgo=
|
||||
github.com/jesseduffield/gocui v0.3.1-0.20210614081440-74b42ecad52b/go.mod h1:QWq79xplEoyhQO+dgpk3sojjTVRKjQklyTlzm5nC5Kg=
|
||||
github.com/jesseduffield/yaml v2.1.0+incompatible h1:HWQJ1gIv2zHKbDYNp0Jwjlj24K8aqpFHnMCynY1EpmE=
|
||||
github.com/jesseduffield/yaml v2.1.0+incompatible/go.mod h1:w0xGhOSIJCGYYW+hnFPTutCy5aACpkcwbmORt5axGqk=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
@@ -134,37 +84,32 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3 h1:QIbQXiugsb+q10B+MI+7DI1oQLdmnep86tWFlaaUAac=
|
||||
github.com/kyokomi/emoji/v2 v2.2.8 h1:jcofPxjHWEkJtkIbcLHvZhxKgCPl6C7MyjTrD4KDqUE=
|
||||
github.com/kyokomi/emoji/v2 v2.2.8/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
|
||||
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.7 h1:bQGKb3vps/j0E9GfJQ03JyhRuxsvdAanXlT9BTw3mdw=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRRpdGg=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
|
||||
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
@@ -180,7 +125,6 @@ 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 v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
@@ -196,21 +140,22 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
||||
github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/urfave/cli v1.20.1-0.20180226030253-8e01ec4cd3e2/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8=
|
||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs=
|
||||
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073 h1:xMPOj6Pz6UipU1wXLkrtqpHbR0AVFnyPEQq/wRWz9lM=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0 h1:hb9wdF1z5waM+dSIICn1l0DkLVDT3hqhhQsDNUmHPRE=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c h1:dk0ukUIHmGHqASjP0iue2261isepFCC6XRCSd1nHgDw=
|
||||
golang.org/x/net v0.0.0-20201002202402-0a1ea396d57c/go.mod h1:iQL9McJNjoIa5mjH6nYTCTZXUN6RP+XW3eib7Ya3XcI=
|
||||
@@ -226,38 +171,21 @@ golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210217105451-b926d437f341 h1:2/QtM1mL37YmcsT8HaDNHDgTqqFVw+zr8UzMiBVLzYU=
|
||||
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54 h1:rF3Ohx8DRyl8h2zw9qojyLHLhrJpEMgyPOImREEryf0=
|
||||
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210402192133-700132347e07 h1:4k6HsQjxj6hVMsI2Vf0yKlzt5lXxZsMW1q0zaq2k8zY=
|
||||
golang.org/x/sys v0.0.0-20210402192133-700132347e07/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210415045647-66c3f260301c h1:6L+uOeS3OQt/f4eFHXZcTxeZrGCuz+CLElgEBjbcTA4=
|
||||
golang.org/x/sys v0.0.0-20210415045647-66c3f260301c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 h1:faDu4veV+8pcThn4fewv6TVlNCezafGoC1gM/mxQLbQ=
|
||||
golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6 h1:EC6+IGYTjPpRfv9a2b/6Puw0W+hLtAhkV1tPsXhutqs=
|
||||
golang.org/x/term v0.0.0-20210317153231-de623e64d2a6/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72 h1:VqE9gduFZ4dbR7XoL77lHFp0/DyDUBKSXK7CMFkVcV0=
|
||||
golang.org/x/term v0.0.0-20210406210042-72f3dc4e9b72/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
|
||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -271,3 +199,5 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
@@ -4,6 +4,15 @@ import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/aybabtme/humanlog"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/env"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/updates"
|
||||
"github.com/sirupsen/logrus"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
@@ -12,17 +21,6 @@ import (
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/aybabtme/humanlog"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/env"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
"github.com/jesseduffield/lazygit/pkg/updates"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// App struct
|
||||
@@ -318,6 +316,9 @@ func TailLogs() {
|
||||
|
||||
fmt.Printf("Tailing log file %s\n\n", logFilePath)
|
||||
|
||||
opts := humanlog.DefaultOptions
|
||||
opts.Truncates = false
|
||||
|
||||
_, err = os.Stat(logFilePath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
@@ -326,22 +327,5 @@ func TailLogs() {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
cmd := secureexec.Command("tail", "-f", logFilePath)
|
||||
|
||||
stdout, _ := cmd.StdoutPipe()
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
opts := humanlog.DefaultOptions
|
||||
opts.Truncates = false
|
||||
if err := humanlog.Scanner(stdout, os.Stdout, opts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
TailLogsForPlatform(logFilePath, opts)
|
||||
}
|
||||
|
||||
29
pkg/app/logging.go
Normal file
29
pkg/app/logging.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// +build !windows
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"github.com/aybabtme/humanlog"
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func TailLogsForPlatform(logFilePath string, opts *humanlog.HandlerOptions) {
|
||||
cmd := secureexec.Command("tail", "-f", logFilePath)
|
||||
|
||||
stdout, _ := cmd.StdoutPipe()
|
||||
if err := cmd.Start(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := humanlog.Scanner(stdout, os.Stdout, opts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
71
pkg/app/logging_windows.go
Normal file
71
pkg/app/logging_windows.go
Normal file
@@ -0,0 +1,71 @@
|
||||
// +build windows
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"github.com/aybabtme/humanlog"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TailLogsForPlatform(logFilePath string, opts *humanlog.HandlerOptions) {
|
||||
var lastModified int64 = 0
|
||||
var lastOffset int64 = 0
|
||||
for {
|
||||
stat, err := os.Stat(logFilePath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if stat.ModTime().Unix() > lastModified {
|
||||
err = TailFrom(lastOffset, logFilePath, opts)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
lastOffset = stat.Size()
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func OpenAndSeek(filepath string, offset int64) (*os.File, error) {
|
||||
file, err := os.Open(filepath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = file.Seek(offset, 0)
|
||||
if err != nil {
|
||||
_ = file.Close()
|
||||
return nil, err
|
||||
}
|
||||
return file, nil
|
||||
}
|
||||
|
||||
func TailFrom(lastOffset int64, logFilePath string, opts *humanlog.HandlerOptions) error {
|
||||
file, err := OpenAndSeek(logFilePath, lastOffset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileScanner := bufio.NewScanner(file)
|
||||
var lines []string
|
||||
for fileScanner.Scan() {
|
||||
lines = append(lines, fileScanner.Text())
|
||||
}
|
||||
file.Close()
|
||||
lineCount := len(lines)
|
||||
lastTen := lines
|
||||
if lineCount > 10 {
|
||||
lastTen = lines[lineCount-10:]
|
||||
}
|
||||
for _, line := range lastTen {
|
||||
reader := strings.NewReader(line)
|
||||
if err := humanlog.Scanner(reader, os.Stdout, opts); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -47,6 +47,10 @@ func (c *GitCommand) GetCommitMessage(commitSha string) (string, error) {
|
||||
return strings.TrimSpace(message), err
|
||||
}
|
||||
|
||||
func (c *GitCommand) GetCommitMessageFirstLine(sha string) (string, error) {
|
||||
return c.RunCommandWithOutput("git show --no-patch --pretty=format:%%s %s", sha)
|
||||
}
|
||||
|
||||
// AmendHead amends HEAD with whatever is staged in your working tree
|
||||
func (c *GitCommand) AmendHead() error {
|
||||
return c.OSCommand.RunCommand(c.AmendHeadCmdStr())
|
||||
@@ -69,6 +73,10 @@ func (c *GitCommand) Revert(sha string) error {
|
||||
return c.RunCommand("git revert %s", sha)
|
||||
}
|
||||
|
||||
func (c *GitCommand) RevertMerge(sha string, parentNumber int) error {
|
||||
return c.RunCommand("git revert %s -m %d", sha, parentNumber)
|
||||
}
|
||||
|
||||
// CherryPickCommits begins an interactive rebase with the given shas being cherry picked onto HEAD
|
||||
func (c *GitCommand) CherryPickCommits(commits []*models.Commit) error {
|
||||
todo := ""
|
||||
|
||||
@@ -189,17 +189,18 @@ func (c *GitCommand) Ignore(filename string) error {
|
||||
}
|
||||
|
||||
// WorktreeFileDiff returns the diff of a file
|
||||
func (c *GitCommand) WorktreeFileDiff(file *models.File, plain bool, cached bool) string {
|
||||
func (c *GitCommand) WorktreeFileDiff(file *models.File, plain bool, cached bool, ignoreWhitespace bool) string {
|
||||
// for now we assume an error means the file was deleted
|
||||
s, _ := c.OSCommand.RunCommandWithOutput(c.WorktreeFileDiffCmdStr(file, plain, cached))
|
||||
s, _ := c.OSCommand.RunCommandWithOutput(c.WorktreeFileDiffCmdStr(file, plain, cached, ignoreWhitespace))
|
||||
return s
|
||||
}
|
||||
|
||||
func (c *GitCommand) WorktreeFileDiffCmdStr(node models.IFile, plain bool, cached bool) string {
|
||||
func (c *GitCommand) WorktreeFileDiffCmdStr(node models.IFile, plain bool, cached bool, ignoreWhitespace bool) string {
|
||||
cachedArg := ""
|
||||
trackedArg := "--"
|
||||
colorArg := c.colorArg()
|
||||
path := c.OSCommand.Quote(node.GetPath())
|
||||
ignoreWhitespaceArg := ""
|
||||
if cached {
|
||||
cachedArg = "--cached"
|
||||
}
|
||||
@@ -209,8 +210,11 @@ func (c *GitCommand) WorktreeFileDiffCmdStr(node models.IFile, plain bool, cache
|
||||
if plain {
|
||||
colorArg = "never"
|
||||
}
|
||||
if ignoreWhitespace {
|
||||
ignoreWhitespaceArg = "--ignore-all-space"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("git diff --submodule --no-ext-diff --color=%s %s %s %s", colorArg, cachedArg, trackedArg, path)
|
||||
return fmt.Sprintf("git diff --submodule --no-ext-diff --color=%s %s %s %s %s", colorArg, ignoreWhitespaceArg, cachedArg, trackedArg, path)
|
||||
}
|
||||
|
||||
func (c *GitCommand) ApplyPatch(patch string, flags ...string) error {
|
||||
@@ -246,12 +250,12 @@ func (c *GitCommand) ShowFileDiffCmdStr(from string, to string, reverse bool, fi
|
||||
reverseFlag = " -R "
|
||||
}
|
||||
|
||||
return fmt.Sprintf("git diff --submodule --no-ext-diff --no-renames --color=%s %s %s %s -- %s", colorArg, from, to, reverseFlag, fileName)
|
||||
return fmt.Sprintf("git diff --submodule --no-ext-diff --no-renames --color=%s %s %s %s -- %s", colorArg, from, to, reverseFlag, c.OSCommand.Quote(fileName))
|
||||
}
|
||||
|
||||
// CheckoutFile checks out the file for the given commit
|
||||
func (c *GitCommand) CheckoutFile(commitSha, fileName string) error {
|
||||
return c.RunCommand("git checkout %s %s", commitSha, fileName)
|
||||
return c.RunCommand("git checkout %s -- %s", commitSha, c.OSCommand.Quote(fileName))
|
||||
}
|
||||
|
||||
// DiscardOldFileChanges discards changes to a file from an old commit
|
||||
@@ -318,7 +322,11 @@ func (c *GitCommand) ResetAndClean() error {
|
||||
}
|
||||
|
||||
func (c *GitCommand) EditFileCmdStr(filename string) (string, error) {
|
||||
editor := c.GetConfigValue("core.editor")
|
||||
editor := c.Config.GetUserConfig().OS.EditCommand
|
||||
|
||||
if editor == "" {
|
||||
editor = c.GetConfigValue("core.editor")
|
||||
}
|
||||
|
||||
if editor == "" {
|
||||
editor = c.OSCommand.Getenv("GIT_EDITOR")
|
||||
@@ -335,7 +343,7 @@ func (c *GitCommand) EditFileCmdStr(filename string) (string, error) {
|
||||
}
|
||||
}
|
||||
if editor == "" {
|
||||
return "", errors.New("No editor defined in $GIT_EDITOR, $VISUAL, $EDITOR, or git config")
|
||||
return "", errors.New("No editor defined in config file, $GIT_EDITOR, $VISUAL, $EDITOR, or git config")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s %s", editor, c.OSCommand.Quote(filename)), nil
|
||||
|
||||
@@ -333,11 +333,12 @@ func TestGitCommandDiscardAllFileChanges(t *testing.T) {
|
||||
// TestGitCommandDiff is a function.
|
||||
func TestGitCommandDiff(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
command func(string, ...string) *exec.Cmd
|
||||
file *models.File
|
||||
plain bool
|
||||
cached bool
|
||||
testName string
|
||||
command func(string, ...string) *exec.Cmd
|
||||
file *models.File
|
||||
plain bool
|
||||
cached bool
|
||||
ignoreWhitespace bool
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
@@ -356,6 +357,7 @@ func TestGitCommandDiff(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"cached",
|
||||
@@ -372,6 +374,7 @@ func TestGitCommandDiff(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"plain",
|
||||
@@ -388,6 +391,7 @@ func TestGitCommandDiff(t *testing.T) {
|
||||
},
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"File not tracked and file has no staged changes",
|
||||
@@ -404,6 +408,24 @@ func TestGitCommandDiff(t *testing.T) {
|
||||
},
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
{
|
||||
"Default case (ignore whitespace)",
|
||||
func(cmd string, args ...string) *exec.Cmd {
|
||||
assert.EqualValues(t, "git", cmd)
|
||||
assert.EqualValues(t, []string{"diff", "--submodule", "--no-ext-diff", "--color=always", "--ignore-all-space", "--", "test.txt"}, args)
|
||||
|
||||
return secureexec.Command("echo")
|
||||
},
|
||||
&models.File{
|
||||
Name: "test.txt",
|
||||
HasStagedChanges: false,
|
||||
Tracked: true,
|
||||
},
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -411,7 +433,7 @@ func TestGitCommandDiff(t *testing.T) {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
gitCmd := NewDummyGitCommand()
|
||||
gitCmd.OSCommand.Command = s.command
|
||||
gitCmd.WorktreeFileDiff(s.file, s.plain, s.cached)
|
||||
gitCmd.WorktreeFileDiff(s.file, s.plain, s.cached, s.ignoreWhitespace)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -433,7 +455,7 @@ func TestGitCommandCheckoutFile(t *testing.T) {
|
||||
"test999.txt",
|
||||
test.CreateMockCommand(t, []*test.CommandSwapper{
|
||||
{
|
||||
Expect: "git checkout 11af912 test999.txt",
|
||||
Expect: "git checkout 11af912 -- test999.txt",
|
||||
Replace: "echo",
|
||||
},
|
||||
}),
|
||||
@@ -447,7 +469,7 @@ func TestGitCommandCheckoutFile(t *testing.T) {
|
||||
"test999.txt",
|
||||
test.CreateMockCommand(t, []*test.CommandSwapper{
|
||||
{
|
||||
Expect: "git checkout 11af912 test999.txt",
|
||||
Expect: "git checkout 11af912 -- test999.txt",
|
||||
Replace: "test",
|
||||
},
|
||||
}),
|
||||
@@ -584,7 +606,7 @@ func TestGitCommandDiscardOldFileChanges(t *testing.T) {
|
||||
Replace: "echo",
|
||||
},
|
||||
{
|
||||
Expect: "git checkout HEAD^ test999.txt",
|
||||
Expect: "git checkout HEAD^ -- test999.txt",
|
||||
Replace: "echo",
|
||||
},
|
||||
{
|
||||
@@ -720,6 +742,7 @@ func TestGitCommandRemoveUntrackedFiles(t *testing.T) {
|
||||
func TestEditFileCmdStr(t *testing.T) {
|
||||
type scenario struct {
|
||||
filename string
|
||||
configEditCommand string
|
||||
command func(string, ...string) *exec.Cmd
|
||||
getenv func(string) string
|
||||
getGitConfigValue func(string) (string, error)
|
||||
@@ -729,6 +752,7 @@ func TestEditFileCmdStr(t *testing.T) {
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"test",
|
||||
"",
|
||||
func(name string, arg ...string) *exec.Cmd {
|
||||
return secureexec.Command("exit", "1")
|
||||
},
|
||||
@@ -739,11 +763,30 @@ func TestEditFileCmdStr(t *testing.T) {
|
||||
return "", nil
|
||||
},
|
||||
func(cmdStr string, err error) {
|
||||
assert.EqualError(t, err, "No editor defined in $GIT_EDITOR, $VISUAL, $EDITOR, or git config")
|
||||
assert.EqualError(t, err, "No editor defined in config file, $GIT_EDITOR, $VISUAL, $EDITOR, or git config")
|
||||
},
|
||||
},
|
||||
{
|
||||
"test",
|
||||
"nano",
|
||||
func(name string, args ...string) *exec.Cmd {
|
||||
assert.Equal(t, "which", name)
|
||||
return secureexec.Command("echo")
|
||||
},
|
||||
func(env string) string {
|
||||
return ""
|
||||
},
|
||||
func(cf string) (string, error) {
|
||||
return "", nil
|
||||
},
|
||||
func(cmdStr string, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "nano \"test\"", cmdStr)
|
||||
},
|
||||
},
|
||||
{
|
||||
"test",
|
||||
"",
|
||||
func(name string, arg ...string) *exec.Cmd {
|
||||
assert.Equal(t, "which", name)
|
||||
return secureexec.Command("exit", "1")
|
||||
@@ -761,6 +804,7 @@ func TestEditFileCmdStr(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"test",
|
||||
"",
|
||||
func(name string, arg ...string) *exec.Cmd {
|
||||
assert.Equal(t, "which", name)
|
||||
return secureexec.Command("exit", "1")
|
||||
@@ -781,6 +825,7 @@ func TestEditFileCmdStr(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"test",
|
||||
"",
|
||||
func(name string, arg ...string) *exec.Cmd {
|
||||
assert.Equal(t, "which", name)
|
||||
return secureexec.Command("exit", "1")
|
||||
@@ -802,6 +847,7 @@ func TestEditFileCmdStr(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"test",
|
||||
"",
|
||||
func(name string, arg ...string) *exec.Cmd {
|
||||
assert.Equal(t, "which", name)
|
||||
return secureexec.Command("echo")
|
||||
@@ -819,6 +865,7 @@ func TestEditFileCmdStr(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"file/with space",
|
||||
"",
|
||||
func(name string, args ...string) *exec.Cmd {
|
||||
assert.Equal(t, "which", name)
|
||||
return secureexec.Command("echo")
|
||||
@@ -838,6 +885,7 @@ func TestEditFileCmdStr(t *testing.T) {
|
||||
|
||||
for _, s := range scenarios {
|
||||
gitCmd := NewDummyGitCommand()
|
||||
gitCmd.Config.GetUserConfig().OS.EditCommand = s.configEditCommand
|
||||
gitCmd.OSCommand.Command = s.command
|
||||
gitCmd.OSCommand.Getenv = s.getenv
|
||||
gitCmd.getGitConfigValue = s.getGitConfigValue
|
||||
|
||||
@@ -52,6 +52,12 @@ func (b *BranchListBuilder) obtainBranches() []*models.Branch {
|
||||
}
|
||||
|
||||
split := strings.Split(line, SEPARATION_CHAR)
|
||||
if len(split) != 4 {
|
||||
// Ignore line if it isn't separated into 4 parts
|
||||
// This is probably a warning message, for more info see:
|
||||
// https://github.com/jesseduffield/lazygit/issues/1385#issuecomment-885580439
|
||||
continue
|
||||
}
|
||||
|
||||
name := strings.TrimPrefix(split[1], "heads/")
|
||||
branch := &models.Branch{
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -72,10 +72,6 @@ func (c *CommitListBuilder) extractCommitFromLine(line string) *models.Commit {
|
||||
|
||||
unitTimestampInt, _ := strconv.Atoi(unixTimestamp)
|
||||
|
||||
// Any commit with multiple parents is a merge commit.
|
||||
// If there's a space then it means there must be more than one parent hash
|
||||
isMerge := strings.Contains(parentHashes, " ")
|
||||
|
||||
return &models.Commit{
|
||||
Sha: sha,
|
||||
Name: message,
|
||||
@@ -83,7 +79,7 @@ func (c *CommitListBuilder) extractCommitFromLine(line string) *models.Commit {
|
||||
ExtraInfo: extraInfo,
|
||||
UnixTimestamp: int64(unitTimestampInt),
|
||||
Author: author,
|
||||
IsMerge: isMerge,
|
||||
Parents: strings.Split(parentHashes, " "),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,8 +165,7 @@ func (c *CommitListBuilder) GetCommits(opts GetCommitsOptions) ([]*models.Commit
|
||||
|
||||
if rebaseMode != "" {
|
||||
currentCommit := commits[len(rebasingCommits)]
|
||||
blue := color.New(color.FgYellow)
|
||||
youAreHere := blue.Sprintf("<-- %s ---", c.Tr.YouAreHere)
|
||||
youAreHere := style.FgYellow.Sprintf("<-- %s ---", c.Tr.YouAreHere)
|
||||
currentCommit.Name = fmt.Sprintf("%s %s", youAreHere, currentCommit.Name)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,8 +8,6 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
const RENAME_SEPARATOR = " -> "
|
||||
|
||||
// GetStatusFiles git status files
|
||||
type GetStatusFileOptions struct {
|
||||
NoRenames bool
|
||||
@@ -24,37 +22,29 @@ func (c *GitCommand) GetStatusFiles(opts GetStatusFileOptions) []*models.File {
|
||||
}
|
||||
untrackedFilesArg := fmt.Sprintf("--untracked-files=%s", untrackedFilesSetting)
|
||||
|
||||
statusOutput, err := c.GitStatus(GitStatusOptions{NoRenames: opts.NoRenames, UntrackedFilesArg: untrackedFilesArg})
|
||||
statuses, err := c.GitStatus(GitStatusOptions{NoRenames: opts.NoRenames, UntrackedFilesArg: untrackedFilesArg})
|
||||
if err != nil {
|
||||
c.Log.Error(err)
|
||||
}
|
||||
statusStrings := utils.SplitLines(statusOutput)
|
||||
files := []*models.File{}
|
||||
|
||||
for _, statusString := range statusStrings {
|
||||
if strings.HasPrefix(statusString, "warning") {
|
||||
c.Log.Warningf("warning when calling git status: %s", statusString)
|
||||
for _, status := range statuses {
|
||||
if strings.HasPrefix(status.StatusString, "warning") {
|
||||
c.Log.Warningf("warning when calling git status: %s", status.StatusString)
|
||||
continue
|
||||
}
|
||||
change := statusString[0:2]
|
||||
change := status.Change
|
||||
stagedChange := change[0:1]
|
||||
unstagedChange := statusString[1:2]
|
||||
name := statusString[3:]
|
||||
unstagedChange := change[1:2]
|
||||
untracked := utils.IncludesString([]string{"??", "A ", "AM"}, change)
|
||||
hasNoStagedChanges := utils.IncludesString([]string{" ", "U", "?"}, stagedChange)
|
||||
hasMergeConflicts := utils.IncludesString([]string{"DD", "AA", "UU", "AU", "UA", "UD", "DU"}, change)
|
||||
hasInlineMergeConflicts := utils.IncludesString([]string{"UU", "AA"}, change)
|
||||
previousName := ""
|
||||
if strings.Contains(name, RENAME_SEPARATOR) {
|
||||
split := strings.Split(name, RENAME_SEPARATOR)
|
||||
name = split[1]
|
||||
previousName = split[0]
|
||||
}
|
||||
|
||||
file := &models.File{
|
||||
Name: name,
|
||||
PreviousName: previousName,
|
||||
DisplayString: statusString,
|
||||
Name: status.Name,
|
||||
PreviousName: status.PreviousName,
|
||||
DisplayString: status.StatusString,
|
||||
HasStagedChanges: !hasNoStagedChanges,
|
||||
HasUnstagedChanges: unstagedChange != " ",
|
||||
Tracked: !untracked,
|
||||
@@ -62,7 +52,7 @@ func (c *GitCommand) GetStatusFiles(opts GetStatusFileOptions) []*models.File {
|
||||
Added: unstagedChange == "A" || untracked,
|
||||
HasMergeConflicts: hasMergeConflicts,
|
||||
HasInlineMergeConflicts: hasInlineMergeConflicts,
|
||||
Type: c.OSCommand.FileType(name),
|
||||
Type: c.OSCommand.FileType(status.Name),
|
||||
ShortStatus: change,
|
||||
}
|
||||
files = append(files, file)
|
||||
@@ -71,13 +61,20 @@ func (c *GitCommand) GetStatusFiles(opts GetStatusFileOptions) []*models.File {
|
||||
return files
|
||||
}
|
||||
|
||||
// GitStatus returns the plaintext short status of the repo
|
||||
// GitStatus returns the file status of the repo
|
||||
type GitStatusOptions struct {
|
||||
NoRenames bool
|
||||
UntrackedFilesArg string
|
||||
}
|
||||
|
||||
func (c *GitCommand) GitStatus(opts GitStatusOptions) (string, error) {
|
||||
type FileStatus struct {
|
||||
StatusString string
|
||||
Change string // ??, MM, AM, ...
|
||||
Name string
|
||||
PreviousName string
|
||||
}
|
||||
|
||||
func (c *GitCommand) GitStatus(opts GitStatusOptions) ([]FileStatus, error) {
|
||||
noRenamesFlag := ""
|
||||
if opts.NoRenames {
|
||||
noRenamesFlag = "--no-renames"
|
||||
@@ -85,20 +82,35 @@ func (c *GitCommand) GitStatus(opts GitStatusOptions) (string, error) {
|
||||
|
||||
statusLines, err := c.RunCommandWithOutput("git status %s --porcelain -z %s", opts.UntrackedFilesArg, noRenamesFlag)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return []FileStatus{}, err
|
||||
}
|
||||
|
||||
splitLines := strings.Split(statusLines, "\x00")
|
||||
// if a line starts with 'R' then the next line is the original file.
|
||||
for i := 0; i < len(splitLines)-1; i++ {
|
||||
response := []FileStatus{}
|
||||
|
||||
for i := 0; i < len(splitLines); i++ {
|
||||
original := splitLines[i]
|
||||
if strings.HasPrefix(original, "R ") {
|
||||
next := splitLines[i+1]
|
||||
updated := "R " + next + RENAME_SEPARATOR + strings.TrimPrefix(original, "R ")
|
||||
splitLines[i] = updated
|
||||
splitLines = append(splitLines[0:i+1], splitLines[i+2:]...)
|
||||
|
||||
if len(original) < 3 {
|
||||
continue
|
||||
}
|
||||
|
||||
status := FileStatus{
|
||||
StatusString: original,
|
||||
Change: original[:2],
|
||||
Name: original[3:],
|
||||
PreviousName: "",
|
||||
}
|
||||
|
||||
if strings.HasPrefix(status.Change, "R") {
|
||||
// if a line starts with 'R' then the next line is the original file.
|
||||
status.PreviousName = strings.TrimSpace(splitLines[i+1])
|
||||
status.StatusString = fmt.Sprintf("%s %s -> %s", status.Change, status.PreviousName, status.Name)
|
||||
i++
|
||||
}
|
||||
|
||||
response = append(response, status)
|
||||
}
|
||||
|
||||
return strings.Join(splitLines, "\n"), nil
|
||||
return response, nil
|
||||
}
|
||||
|
||||
@@ -31,8 +31,8 @@ func TestGitCommandGetStatusFiles(t *testing.T) {
|
||||
"Several files found",
|
||||
func(cmd string, args ...string) *exec.Cmd {
|
||||
return secureexec.Command(
|
||||
"echo",
|
||||
"MM file1.txt\nA file3.txt\nAM file2.txt\n?? file4.txt\nUU file5.txt",
|
||||
"printf",
|
||||
`MM file1.txt\0A file3.txt\0AM file2.txt\0?? file4.txt\0UU file5.txt`,
|
||||
)
|
||||
},
|
||||
func(files []*models.File) {
|
||||
@@ -106,6 +106,111 @@ func TestGitCommandGetStatusFiles(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
assert.EqualValues(t, expected, files)
|
||||
},
|
||||
},
|
||||
{
|
||||
"File with new line char",
|
||||
func(cmd string, args ...string) *exec.Cmd {
|
||||
return secureexec.Command(
|
||||
"printf",
|
||||
`MM a\nb.txt`,
|
||||
)
|
||||
},
|
||||
func(files []*models.File) {
|
||||
assert.Len(t, files, 1)
|
||||
|
||||
expected := []*models.File{
|
||||
{
|
||||
Name: "a\nb.txt",
|
||||
HasStagedChanges: true,
|
||||
HasUnstagedChanges: true,
|
||||
Tracked: true,
|
||||
Added: false,
|
||||
Deleted: false,
|
||||
HasMergeConflicts: false,
|
||||
HasInlineMergeConflicts: false,
|
||||
DisplayString: "MM a\nb.txt",
|
||||
Type: "other",
|
||||
ShortStatus: "MM",
|
||||
},
|
||||
}
|
||||
|
||||
assert.EqualValues(t, expected, files)
|
||||
},
|
||||
},
|
||||
{
|
||||
"Renamed files",
|
||||
func(cmd string, args ...string) *exec.Cmd {
|
||||
return secureexec.Command(
|
||||
"printf",
|
||||
`R after1.txt\0before1.txt\0RM after2.txt\0before2.txt`,
|
||||
)
|
||||
},
|
||||
func(files []*models.File) {
|
||||
assert.Len(t, files, 2)
|
||||
|
||||
expected := []*models.File{
|
||||
{
|
||||
Name: "after1.txt",
|
||||
PreviousName: "before1.txt",
|
||||
HasStagedChanges: true,
|
||||
HasUnstagedChanges: false,
|
||||
Tracked: true,
|
||||
Added: false,
|
||||
Deleted: false,
|
||||
HasMergeConflicts: false,
|
||||
HasInlineMergeConflicts: false,
|
||||
DisplayString: "R before1.txt -> after1.txt",
|
||||
Type: "other",
|
||||
ShortStatus: "R ",
|
||||
},
|
||||
{
|
||||
Name: "after2.txt",
|
||||
PreviousName: "before2.txt",
|
||||
HasStagedChanges: true,
|
||||
HasUnstagedChanges: true,
|
||||
Tracked: true,
|
||||
Added: false,
|
||||
Deleted: false,
|
||||
HasMergeConflicts: false,
|
||||
HasInlineMergeConflicts: false,
|
||||
DisplayString: "RM before2.txt -> after2.txt",
|
||||
Type: "other",
|
||||
ShortStatus: "RM",
|
||||
},
|
||||
}
|
||||
|
||||
assert.EqualValues(t, expected, files)
|
||||
},
|
||||
},
|
||||
{
|
||||
"File with arrow in name",
|
||||
func(cmd string, args ...string) *exec.Cmd {
|
||||
return secureexec.Command(
|
||||
"printf",
|
||||
`?? a -> b.txt`,
|
||||
)
|
||||
},
|
||||
func(files []*models.File) {
|
||||
assert.Len(t, files, 1)
|
||||
|
||||
expected := []*models.File{
|
||||
{
|
||||
Name: "a -> b.txt",
|
||||
HasStagedChanges: false,
|
||||
HasUnstagedChanges: true,
|
||||
Tracked: false,
|
||||
Added: true,
|
||||
Deleted: false,
|
||||
HasMergeConflicts: false,
|
||||
HasInlineMergeConflicts: false,
|
||||
DisplayString: "?? a -> b.txt",
|
||||
Type: "other",
|
||||
ShortStatus: "??",
|
||||
},
|
||||
}
|
||||
|
||||
assert.EqualValues(t, expected, files)
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,28 +1,16 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
const semverRegex = `v?((\d+\.?)+)([^\d]?.*)`
|
||||
|
||||
func convertToInt(s string) int {
|
||||
i, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
func (c *GitCommand) GetTags() ([]*models.Tag, error) {
|
||||
// get remote branches
|
||||
remoteBranchesStr, err := c.OSCommand.RunCommandWithOutput(`git tag --list`)
|
||||
// get remote branches, sorted by creation date (descending)
|
||||
// see: https://git-scm.com/docs/git-tag#Documentation/git-tag.txt---sortltkeygt
|
||||
remoteBranchesStr, err := c.OSCommand.RunCommandWithOutput(`git tag --list --sort=-creatordate`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -37,52 +25,10 @@ func (c *GitCommand) GetTags() ([]*models.Tag, error) {
|
||||
// first step is to get our remotes from go-git
|
||||
tags := make([]*models.Tag, len(split))
|
||||
for i, tagName := range split {
|
||||
|
||||
tags[i] = &models.Tag{
|
||||
Name: tagName,
|
||||
}
|
||||
}
|
||||
|
||||
// now lets sort our tags by name numerically
|
||||
re := regexp.MustCompile(semverRegex)
|
||||
|
||||
// the reason this is complicated is because we're both sorting alphabetically
|
||||
// and when we're dealing with semver strings
|
||||
sort.Slice(tags, func(i, j int) bool {
|
||||
a := tags[i].Name
|
||||
b := tags[j].Name
|
||||
|
||||
matchA := re.FindStringSubmatch(a)
|
||||
matchB := re.FindStringSubmatch(b)
|
||||
|
||||
if len(matchA) > 0 && len(matchB) > 0 {
|
||||
numbersA := strings.Split(matchA[1], ".")
|
||||
numbersB := strings.Split(matchB[1], ".")
|
||||
k := 0
|
||||
for {
|
||||
if len(numbersA) == k && len(numbersB) == k {
|
||||
break
|
||||
}
|
||||
if len(numbersA) == k {
|
||||
return true
|
||||
}
|
||||
if len(numbersB) == k {
|
||||
return false
|
||||
}
|
||||
if convertToInt(numbersA[k]) < convertToInt(numbersB[k]) {
|
||||
return true
|
||||
}
|
||||
if convertToInt(numbersA[k]) > convertToInt(numbersB[k]) {
|
||||
return false
|
||||
}
|
||||
k++
|
||||
}
|
||||
|
||||
return strings.ToLower(matchA[3]) < strings.ToLower(matchB[3])
|
||||
}
|
||||
|
||||
return strings.ToLower(a) < strings.ToLower(b)
|
||||
})
|
||||
|
||||
return tags, nil
|
||||
}
|
||||
|
||||
@@ -24,3 +24,26 @@ func (b *Branch) ID() string {
|
||||
func (b *Branch) Description() string {
|
||||
return b.RefName()
|
||||
}
|
||||
|
||||
// this method does not consider the case where the git config states that a branch is tracking the config.
|
||||
// The Pullables value here is based on whether or not we saw an upstream when doing `git branch`
|
||||
func (b *Branch) IsTrackingRemote() bool {
|
||||
return b.IsRealBranch() && b.Pullables != "?"
|
||||
}
|
||||
|
||||
func (b *Branch) MatchesUpstream() bool {
|
||||
return b.IsRealBranch() && b.Pushables == "0" && b.Pullables == "0"
|
||||
}
|
||||
|
||||
func (b *Branch) HasCommitsToPush() bool {
|
||||
return b.IsRealBranch() && b.Pushables != "0"
|
||||
}
|
||||
|
||||
func (b *Branch) HasCommitsToPull() bool {
|
||||
return b.IsRealBranch() && b.Pullables != "0"
|
||||
}
|
||||
|
||||
// for when we're in a detached head state
|
||||
func (b *Branch) IsRealBranch() bool {
|
||||
return b.Pushables != "" && b.Pullables != ""
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ type Commit struct {
|
||||
Author string
|
||||
UnixTimestamp int64
|
||||
|
||||
// IsMerge tells us whether we're dealing with a merge commit i.e. a commit with two parents
|
||||
IsMerge bool
|
||||
// SHAs of parent commits (will be multiple if it's a merge commit)
|
||||
Parents []string
|
||||
}
|
||||
|
||||
func (c *Commit) ShortSha() string {
|
||||
@@ -35,3 +35,7 @@ func (c *Commit) ID() string {
|
||||
func (c *Commit) Description() string {
|
||||
return fmt.Sprintf("%s %s", c.Sha[:7], c.Name)
|
||||
}
|
||||
|
||||
func (c *Commit) IsMerge() bool {
|
||||
return len(c.Parents) > 1
|
||||
}
|
||||
|
||||
@@ -29,8 +29,6 @@ type IFile interface {
|
||||
GetPath() string
|
||||
}
|
||||
|
||||
const RENAME_SEPARATOR = " -> "
|
||||
|
||||
func (f *File) IsRename() bool {
|
||||
return f.PreviousName != ""
|
||||
}
|
||||
@@ -63,7 +61,7 @@ func (f *File) IsSubmodule(configs []*SubmoduleConfig) bool {
|
||||
|
||||
func (f *File) SubmoduleConfig(configs []*SubmoduleConfig) *SubmoduleConfig {
|
||||
for _, config := range configs {
|
||||
if f.Name == config.Name {
|
||||
if f.Name == config.Path {
|
||||
return config
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,10 +301,15 @@ func sanitisedCommandOutput(output []byte, err error) (string, error) {
|
||||
// OpenFile opens a file with the given
|
||||
func (c *OSCommand) OpenFile(filename string) error {
|
||||
commandTemplate := c.Config.GetUserConfig().OS.OpenCommand
|
||||
templateValues := map[string]string{
|
||||
"filename": c.Quote(filename),
|
||||
quoted := c.Quote(filename)
|
||||
if c.Platform.OS == "linux" {
|
||||
// Add extra quoting to avoid issues with shell command string
|
||||
quoted = c.Quote(quoted)
|
||||
quoted = quoted[1 : len(quoted)-1]
|
||||
}
|
||||
templateValues := map[string]string{
|
||||
"filename": quoted,
|
||||
}
|
||||
|
||||
command := utils.ResolvePlaceholderString(commandTemplate, templateValues)
|
||||
err := c.RunCommand(command)
|
||||
return err
|
||||
|
||||
@@ -103,6 +103,7 @@ func TestOSCommandOpenFile(t *testing.T) {
|
||||
|
||||
for _, s := range scenarios {
|
||||
OSCmd := NewDummyOSCommand()
|
||||
OSCmd.Platform.OS = "darwin"
|
||||
OSCmd.Command = s.command
|
||||
OSCmd.Config.GetUserConfig().OS.OpenCommand = "open {{filename}}"
|
||||
|
||||
@@ -110,6 +111,80 @@ func TestOSCommandOpenFile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestOSCommandOpenFile tests the OpenFile command on Linux
|
||||
func TestOSCommandOpenFileLinux(t *testing.T) {
|
||||
type scenario struct {
|
||||
filename string
|
||||
command func(string, ...string) *exec.Cmd
|
||||
test func(error)
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"test",
|
||||
func(name string, arg ...string) *exec.Cmd {
|
||||
return secureexec.Command("exit", "1")
|
||||
},
|
||||
func(err error) {
|
||||
assert.Error(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
"test",
|
||||
func(name string, arg ...string) *exec.Cmd {
|
||||
assert.Equal(t, "sh", name)
|
||||
assert.Equal(t, []string{"-c", "xdg-open \"test\" > /dev/null"}, arg)
|
||||
return secureexec.Command("echo")
|
||||
},
|
||||
func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
"filename with spaces",
|
||||
func(name string, arg ...string) *exec.Cmd {
|
||||
assert.Equal(t, "sh", name)
|
||||
assert.Equal(t, []string{"-c", "xdg-open \"filename with spaces\" > /dev/null"}, arg)
|
||||
return secureexec.Command("echo")
|
||||
},
|
||||
func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
"let's_test_with_single_quote",
|
||||
func(name string, arg ...string) *exec.Cmd {
|
||||
assert.Equal(t, "sh", name)
|
||||
assert.Equal(t, []string{"-c", "xdg-open \"let's_test_with_single_quote\" > /dev/null"}, arg)
|
||||
return secureexec.Command("echo")
|
||||
},
|
||||
func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
"$USER.txt",
|
||||
func(name string, arg ...string) *exec.Cmd {
|
||||
assert.Equal(t, "sh", name)
|
||||
assert.Equal(t, []string{"-c", "xdg-open \"\\$USER.txt\" > /dev/null"}, arg)
|
||||
return secureexec.Command("echo")
|
||||
},
|
||||
func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
OSCmd := NewDummyOSCommand()
|
||||
OSCmd.Command = s.command
|
||||
OSCmd.Platform.OS = "linux"
|
||||
OSCmd.Config.GetUserConfig().OS.OpenCommand = `sh -c "xdg-open {{filename}} > /dev/null"`
|
||||
|
||||
s.test(OSCmd.OpenFile(s.filename))
|
||||
}
|
||||
}
|
||||
|
||||
// TestOSCommandQuote is a function.
|
||||
func TestOSCommandQuote(t *testing.T) {
|
||||
osCommand := NewDummyOSCommand()
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
@@ -95,45 +95,39 @@ func (l *PatchLine) render(selected bool, included bool) string {
|
||||
if l.Kind == HUNK_HEADER {
|
||||
re := regexp.MustCompile("(@@.*?@@)(.*)")
|
||||
match := re.FindStringSubmatch(content)
|
||||
return coloredString(color.FgCyan, match[1], selected, included) + coloredString(theme.DefaultTextColor, match[2], selected, false)
|
||||
return coloredString(style.FgCyan, match[1], selected, included) + coloredString(theme.DefaultTextColor, match[2], selected, false)
|
||||
}
|
||||
|
||||
var colorAttr color.Attribute
|
||||
textStyle := theme.DefaultTextColor
|
||||
switch l.Kind {
|
||||
case PATCH_HEADER:
|
||||
colorAttr = color.Bold
|
||||
textStyle = textStyle.SetBold()
|
||||
case ADDITION:
|
||||
colorAttr = color.FgGreen
|
||||
textStyle = style.FgGreen
|
||||
case DELETION:
|
||||
colorAttr = color.FgRed
|
||||
textStyle = style.FgRed
|
||||
case COMMIT_SHA:
|
||||
colorAttr = color.FgYellow
|
||||
default:
|
||||
colorAttr = theme.DefaultTextColor
|
||||
textStyle = style.FgYellow
|
||||
}
|
||||
|
||||
return coloredString(colorAttr, content, selected, included)
|
||||
return coloredString(textStyle, content, selected, included)
|
||||
}
|
||||
|
||||
func coloredString(colorAttr color.Attribute, str string, selected bool, included bool) string {
|
||||
var cl *color.Color
|
||||
attributes := []color.Attribute{colorAttr}
|
||||
func coloredString(textStyle style.TextStyle, str string, selected bool, included bool) string {
|
||||
if selected {
|
||||
attributes = append(attributes, theme.SelectedRangeBgColor)
|
||||
textStyle = textStyle.MergeStyle(theme.SelectedRangeBgColor)
|
||||
}
|
||||
cl = color.New(attributes...)
|
||||
var clIncluded *color.Color
|
||||
|
||||
firstCharStyle := textStyle
|
||||
if included {
|
||||
clIncluded = color.New(append(attributes, color.BgGreen)...)
|
||||
} else {
|
||||
clIncluded = color.New(attributes...)
|
||||
firstCharStyle = firstCharStyle.MergeStyle(style.BgGreen)
|
||||
}
|
||||
|
||||
if len(str) < 2 {
|
||||
return utils.ColoredStringDirect(str, clIncluded)
|
||||
return firstCharStyle.Sprint(str)
|
||||
}
|
||||
|
||||
return utils.ColoredStringDirect(str[:1], clIncluded) + utils.ColoredStringDirect(str[1:], cl)
|
||||
return firstCharStyle.Sprint(str[:1]) + textStyle.Sprint(str[1:])
|
||||
}
|
||||
|
||||
func parsePatch(patch string) ([]int, []int, []*PatchLine) {
|
||||
|
||||
@@ -5,14 +5,62 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-errors/errors"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
)
|
||||
|
||||
// Service is a service that repository is on (Github, Bitbucket, ...)
|
||||
type Service struct {
|
||||
Name string
|
||||
PullRequestURL string
|
||||
Name string
|
||||
pullRequestURLIntoDefaultBranch func(owner string, repository string, from string) string
|
||||
pullRequestURLIntoTargetBranch func(owner string, repository string, from string, to string) string
|
||||
}
|
||||
|
||||
// NewService builds a Service based on the host type
|
||||
func NewService(typeName string, repositoryDomain string, siteDomain string) *Service {
|
||||
var service *Service
|
||||
|
||||
switch typeName {
|
||||
case "github":
|
||||
service = &Service{
|
||||
Name: repositoryDomain,
|
||||
pullRequestURLIntoDefaultBranch: func(owner string, repository string, from string) string {
|
||||
return fmt.Sprintf("https://%s/%s/%s/compare/%s?expand=1", siteDomain, owner, repository, from)
|
||||
},
|
||||
pullRequestURLIntoTargetBranch: func(owner string, repository string, from string, to string) string {
|
||||
return fmt.Sprintf("https://%s/%s/%s/compare/%s...%s?expand=1", siteDomain, owner, repository, to, from)
|
||||
},
|
||||
}
|
||||
case "bitbucket":
|
||||
service = &Service{
|
||||
Name: repositoryDomain,
|
||||
pullRequestURLIntoDefaultBranch: func(owner string, repository string, from string) string {
|
||||
return fmt.Sprintf("https://%s/%s/%s/pull-requests/new?source=%s&t=1", siteDomain, owner, repository, from)
|
||||
},
|
||||
pullRequestURLIntoTargetBranch: func(owner string, repository string, from string, to string) string {
|
||||
return fmt.Sprintf("https://%s/%s/%s/pull-requests/new?source=%s&dest=%s&t=1", siteDomain, owner, repository, from, to)
|
||||
},
|
||||
}
|
||||
case "gitlab":
|
||||
service = &Service{
|
||||
Name: repositoryDomain,
|
||||
pullRequestURLIntoDefaultBranch: func(owner string, repository string, from string) string {
|
||||
return fmt.Sprintf("https://%s/%s/%s/merge_requests/new?merge_request[source_branch]=%s", siteDomain, owner, repository, from)
|
||||
},
|
||||
pullRequestURLIntoTargetBranch: func(owner string, repository string, from string, to string) string {
|
||||
return fmt.Sprintf("https://%s/%s/%s/merge_requests/new?merge_request[source_branch]=%s&merge_request[target_branch]=%s", siteDomain, owner, repository, from, to)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return service
|
||||
}
|
||||
|
||||
func (s *Service) PullRequestURL(owner string, repository string, from string, to string) string {
|
||||
if to == "" {
|
||||
return s.pullRequestURLIntoDefaultBranch(owner, repository, from)
|
||||
} else {
|
||||
return s.pullRequestURLIntoTargetBranch(owner, repository, from, to)
|
||||
}
|
||||
}
|
||||
|
||||
// PullRequest opens a link in browser to create new pull request
|
||||
@@ -28,31 +76,6 @@ type RepoInformation struct {
|
||||
Repository string
|
||||
}
|
||||
|
||||
// NewService builds a Service based on the host type
|
||||
func NewService(typeName string, repositoryDomain string, siteDomain string) *Service {
|
||||
var service *Service
|
||||
|
||||
switch typeName {
|
||||
case "github":
|
||||
service = &Service{
|
||||
Name: repositoryDomain,
|
||||
PullRequestURL: fmt.Sprintf("https://%s%s", siteDomain, "/%s/%s/compare/%s?expand=1"),
|
||||
}
|
||||
case "bitbucket":
|
||||
service = &Service{
|
||||
Name: repositoryDomain,
|
||||
PullRequestURL: fmt.Sprintf("https://%s%s", siteDomain, "/%s/%s/pull-requests/new?source=%s&t=1"),
|
||||
}
|
||||
case "gitlab":
|
||||
service = &Service{
|
||||
Name: repositoryDomain,
|
||||
PullRequestURL: fmt.Sprintf("https://%s%s", siteDomain, "/%s/%s/merge_requests/new?merge_request[source_branch]=%s"),
|
||||
}
|
||||
}
|
||||
|
||||
return service
|
||||
}
|
||||
|
||||
func getServices(config config.AppConfigurer) []*Service {
|
||||
services := []*Service{
|
||||
NewService("github", "github.com", "github.com"),
|
||||
@@ -90,8 +113,8 @@ func NewPullRequest(gitCommand *GitCommand) *PullRequest {
|
||||
}
|
||||
|
||||
// Create opens link to new pull request in browser
|
||||
func (pr *PullRequest) Create(branch *models.Branch) (string, error) {
|
||||
pullRequestURL, err := pr.getPullRequestURL(branch)
|
||||
func (pr *PullRequest) Create(from string, to string) (string, error) {
|
||||
pullRequestURL, err := pr.getPullRequestURL(from, to)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -100,8 +123,8 @@ func (pr *PullRequest) Create(branch *models.Branch) (string, error) {
|
||||
}
|
||||
|
||||
// CopyURL copies the pull request URL to the clipboard
|
||||
func (pr *PullRequest) CopyURL(branch *models.Branch) (string, error) {
|
||||
pullRequestURL, err := pr.getPullRequestURL(branch)
|
||||
func (pr *PullRequest) CopyURL(from string, to string) (string, error) {
|
||||
pullRequestURL, err := pr.getPullRequestURL(from, to)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -109,8 +132,8 @@ func (pr *PullRequest) CopyURL(branch *models.Branch) (string, error) {
|
||||
return pullRequestURL, pr.GitCommand.OSCommand.CopyToClipboard(pullRequestURL)
|
||||
}
|
||||
|
||||
func (pr *PullRequest) getPullRequestURL(branch *models.Branch) (string, error) {
|
||||
branchExistsOnRemote := pr.GitCommand.CheckRemoteBranchExists(branch)
|
||||
func (pr *PullRequest) getPullRequestURL(from string, to string) (string, error) {
|
||||
branchExistsOnRemote := pr.GitCommand.CheckRemoteBranchExists(from)
|
||||
|
||||
if !branchExistsOnRemote {
|
||||
return "", errors.New(pr.GitCommand.Tr.NoBranchOnRemote)
|
||||
@@ -131,9 +154,8 @@ func (pr *PullRequest) getPullRequestURL(branch *models.Branch) (string, error)
|
||||
}
|
||||
|
||||
repoInfo := getRepoInfoFromURL(repoURL)
|
||||
pullRequestURL := fmt.Sprintf(
|
||||
gitService.PullRequestURL, repoInfo.Owner, repoInfo.Repository, branch.Name,
|
||||
)
|
||||
|
||||
pullRequestURL := gitService.PullRequestURL(repoInfo.Owner, repoInfo.Repository, from, to)
|
||||
|
||||
return pullRequestURL, nil
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -48,7 +47,8 @@ func TestGetRepoInfoFromURL(t *testing.T) {
|
||||
func TestCreatePullRequest(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
branch *models.Branch
|
||||
from string
|
||||
to string
|
||||
remoteUrl string
|
||||
command func(string, ...string) *exec.Cmd
|
||||
test func(url string, err error)
|
||||
@@ -56,10 +56,8 @@ func TestCreatePullRequest(t *testing.T) {
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "Opens a link to new pull request on bitbucket",
|
||||
branch: &models.Branch{
|
||||
Name: "feature/profile-page",
|
||||
},
|
||||
testName: "Opens a link to new pull request on bitbucket",
|
||||
from: "feature/profile-page",
|
||||
remoteUrl: "git@bitbucket.org:johndoe/social_network.git",
|
||||
command: func(cmd string, args ...string) *exec.Cmd {
|
||||
// Handle git remote url call
|
||||
@@ -77,10 +75,8 @@ func TestCreatePullRequest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Opens a link to new pull request on bitbucket with http remote url",
|
||||
branch: &models.Branch{
|
||||
Name: "feature/events",
|
||||
},
|
||||
testName: "Opens a link to new pull request on bitbucket with http remote url",
|
||||
from: "feature/events",
|
||||
remoteUrl: "https://my_username@bitbucket.org/johndoe/social_network.git",
|
||||
command: func(cmd string, args ...string) *exec.Cmd {
|
||||
// Handle git remote url call
|
||||
@@ -98,10 +94,8 @@ func TestCreatePullRequest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Opens a link to new pull request on github",
|
||||
branch: &models.Branch{
|
||||
Name: "feature/sum-operation",
|
||||
},
|
||||
testName: "Opens a link to new pull request on github",
|
||||
from: "feature/sum-operation",
|
||||
remoteUrl: "git@github.com:peter/calculator.git",
|
||||
command: func(cmd string, args ...string) *exec.Cmd {
|
||||
// Handle git remote url call
|
||||
@@ -119,10 +113,68 @@ func TestCreatePullRequest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Opens a link to new pull request on gitlab",
|
||||
branch: &models.Branch{
|
||||
Name: "feature/ui",
|
||||
testName: "Opens a link to new pull request on bitbucket with specific target branch",
|
||||
from: "feature/profile-page/avatar",
|
||||
to: "feature/profile-page",
|
||||
remoteUrl: "git@bitbucket.org:johndoe/social_network.git",
|
||||
command: func(cmd string, args ...string) *exec.Cmd {
|
||||
// Handle git remote url call
|
||||
if strings.HasPrefix(cmd, "git") {
|
||||
return secureexec.Command("echo", "git@bitbucket.org:johndoe/social_network.git")
|
||||
}
|
||||
|
||||
assert.Equal(t, cmd, "open")
|
||||
assert.Equal(t, args, []string{"https://bitbucket.org/johndoe/social_network/pull-requests/new?source=feature/profile-page/avatar&dest=feature/profile-page&t=1"})
|
||||
return secureexec.Command("echo")
|
||||
},
|
||||
test: func(url string, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "https://bitbucket.org/johndoe/social_network/pull-requests/new?source=feature/profile-page/avatar&dest=feature/profile-page&t=1", url)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Opens a link to new pull request on bitbucket with http remote url with specified target branch",
|
||||
from: "feature/remote-events",
|
||||
to: "feature/events",
|
||||
remoteUrl: "https://my_username@bitbucket.org/johndoe/social_network.git",
|
||||
command: func(cmd string, args ...string) *exec.Cmd {
|
||||
// Handle git remote url call
|
||||
if strings.HasPrefix(cmd, "git") {
|
||||
return secureexec.Command("echo", "https://my_username@bitbucket.org/johndoe/social_network.git")
|
||||
}
|
||||
|
||||
assert.Equal(t, cmd, "open")
|
||||
assert.Equal(t, args, []string{"https://bitbucket.org/johndoe/social_network/pull-requests/new?source=feature/remote-events&dest=feature/events&t=1"})
|
||||
return secureexec.Command("echo")
|
||||
},
|
||||
test: func(url string, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "https://bitbucket.org/johndoe/social_network/pull-requests/new?source=feature/remote-events&dest=feature/events&t=1", url)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Opens a link to new pull request on github with specific target branch",
|
||||
from: "feature/sum-operation",
|
||||
to: "feature/operations",
|
||||
remoteUrl: "git@github.com:peter/calculator.git",
|
||||
command: func(cmd string, args ...string) *exec.Cmd {
|
||||
// Handle git remote url call
|
||||
if strings.HasPrefix(cmd, "git") {
|
||||
return secureexec.Command("echo", "git@github.com:peter/calculator.git")
|
||||
}
|
||||
|
||||
assert.Equal(t, cmd, "open")
|
||||
assert.Equal(t, args, []string{"https://github.com/peter/calculator/compare/feature/operations...feature/sum-operation?expand=1"})
|
||||
return secureexec.Command("echo")
|
||||
},
|
||||
test: func(url string, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "https://github.com/peter/calculator/compare/feature/operations...feature/sum-operation?expand=1", url)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Opens a link to new pull request on gitlab",
|
||||
from: "feature/ui",
|
||||
remoteUrl: "git@gitlab.com:peter/calculator.git",
|
||||
command: func(cmd string, args ...string) *exec.Cmd {
|
||||
// Handle git remote url call
|
||||
@@ -140,10 +192,67 @@ func TestCreatePullRequest(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Throws an error if git service is unsupported",
|
||||
branch: &models.Branch{
|
||||
Name: "feature/divide-operation",
|
||||
testName: "Opens a link to new pull request on gitlab in nested groups",
|
||||
from: "feature/ui",
|
||||
remoteUrl: "git@gitlab.com:peter/public/calculator.git",
|
||||
command: func(cmd string, args ...string) *exec.Cmd {
|
||||
// Handle git remote url call
|
||||
if strings.HasPrefix(cmd, "git") {
|
||||
return secureexec.Command("echo", "git@gitlab.com:peter/calculator.git")
|
||||
}
|
||||
|
||||
assert.Equal(t, cmd, "open")
|
||||
assert.Equal(t, args, []string{"https://gitlab.com/peter/public/calculator/merge_requests/new?merge_request[source_branch]=feature/ui"})
|
||||
return secureexec.Command("echo")
|
||||
},
|
||||
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/ui", url)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Opens a link to new pull request on gitlab with specific target branch",
|
||||
from: "feature/commit-ui",
|
||||
to: "epic/ui",
|
||||
remoteUrl: "git@gitlab.com:peter/calculator.git",
|
||||
command: func(cmd string, args ...string) *exec.Cmd {
|
||||
// Handle git remote url call
|
||||
if strings.HasPrefix(cmd, "git") {
|
||||
return secureexec.Command("echo", "git@gitlab.com:peter/calculator.git")
|
||||
}
|
||||
|
||||
assert.Equal(t, cmd, "open")
|
||||
assert.Equal(t, args, []string{"https://gitlab.com/peter/calculator/merge_requests/new?merge_request[source_branch]=feature/commit-ui&merge_request[target_branch]=epic/ui"})
|
||||
return secureexec.Command("echo")
|
||||
},
|
||||
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/commit-ui&merge_request[target_branch]=epic/ui", url)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Opens a link to new pull request on gitlab with specific target branch in nested groups",
|
||||
from: "feature/commit-ui",
|
||||
to: "epic/ui",
|
||||
remoteUrl: "git@gitlab.com:peter/public/calculator.git",
|
||||
command: func(cmd string, args ...string) *exec.Cmd {
|
||||
// Handle git remote url call
|
||||
if strings.HasPrefix(cmd, "git") {
|
||||
return secureexec.Command("echo", "git@gitlab.com:peter/calculator.git")
|
||||
}
|
||||
|
||||
assert.Equal(t, cmd, "open")
|
||||
assert.Equal(t, args, []string{"https://gitlab.com/peter/public/calculator/merge_requests/new?merge_request[source_branch]=feature/commit-ui&merge_request[target_branch]=epic/ui"})
|
||||
return secureexec.Command("echo")
|
||||
},
|
||||
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/commit-ui&merge_request[target_branch]=epic/ui", url)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "Throws an error if git service is unsupported",
|
||||
from: "feature/divide-operation",
|
||||
remoteUrl: "git@something.com:peter/calculator.git",
|
||||
command: func(cmd string, args ...string) *exec.Cmd {
|
||||
return secureexec.Command("echo")
|
||||
@@ -171,7 +280,7 @@ func TestCreatePullRequest(t *testing.T) {
|
||||
return s.remoteUrl, nil
|
||||
}
|
||||
dummyPullRequest := NewPullRequest(gitCommand)
|
||||
s.test(dummyPullRequest.Create(s.branch))
|
||||
s.test(dummyPullRequest.Create(s.from, s.to))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ func (c *GitCommand) GenerateGenericRebaseTodo(commits []*models.Commit, actionI
|
||||
var commitAction string
|
||||
if i == actionIndex {
|
||||
commitAction = action
|
||||
} else if commit.IsMerge {
|
||||
} else if commit.IsMerge() {
|
||||
// your typical interactive rebase will actually drop merge commits by default. Damn git CLI, you scary!
|
||||
// doing this means we don't need to worry about rebasing over merges which always causes problems.
|
||||
// you typically shouldn't be doing rebases that pass over merge commits anyway.
|
||||
|
||||
@@ -2,8 +2,6 @@ package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
)
|
||||
|
||||
func (c *GitCommand) AddRemote(name string, url string) error {
|
||||
@@ -28,10 +26,10 @@ func (c *GitCommand) DeleteRemoteBranch(remoteName string, branchName string, pr
|
||||
}
|
||||
|
||||
// CheckRemoteBranchExists Returns remote branch
|
||||
func (c *GitCommand) CheckRemoteBranchExists(branch *models.Branch) bool {
|
||||
func (c *GitCommand) CheckRemoteBranchExists(branchName string) bool {
|
||||
_, err := c.OSCommand.RunCommandWithOutput(
|
||||
"git show-ref --verify -- refs/remotes/origin/%s",
|
||||
branch.Name,
|
||||
branchName,
|
||||
)
|
||||
|
||||
return err == nil
|
||||
|
||||
@@ -69,11 +69,11 @@ func (c *GitCommand) SubmoduleStash(submodule *models.SubmoduleConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return c.RunCommand("git -C %s stash --include-untracked", submodule.Path)
|
||||
return c.RunCommand("git -C %s stash --include-untracked", c.OSCommand.Quote(submodule.Path))
|
||||
}
|
||||
|
||||
func (c *GitCommand) SubmoduleReset(submodule *models.SubmoduleConfig) error {
|
||||
return c.RunCommand("git submodule update --init --force %s", submodule.Path)
|
||||
return c.RunCommand("git submodule update --init --force -- %s", c.OSCommand.Quote(submodule.Path))
|
||||
}
|
||||
|
||||
func (c *GitCommand) SubmoduleUpdateAll() error {
|
||||
@@ -84,13 +84,13 @@ func (c *GitCommand) SubmoduleUpdateAll() error {
|
||||
func (c *GitCommand) SubmoduleDelete(submodule *models.SubmoduleConfig) error {
|
||||
// based on https://gist.github.com/myusuf3/7f645819ded92bda6677
|
||||
|
||||
if err := c.RunCommand("git submodule deinit --force %s", submodule.Path); err != nil {
|
||||
if err := c.RunCommand("git submodule deinit --force -- %s", c.OSCommand.Quote(submodule.Path)); err != nil {
|
||||
if strings.Contains(err.Error(), "did not match any file(s) known to git") {
|
||||
if err := c.RunCommand("git config --file .gitmodules --remove-section submodule.%s", submodule.Name); err != nil {
|
||||
if err := c.RunCommand("git config --file .gitmodules --remove-section submodule.%s", c.OSCommand.Quote(submodule.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.RunCommand("git config --remove-section submodule.%s", submodule.Name); err != nil {
|
||||
if err := c.RunCommand("git config --remove-section submodule.%s", c.OSCommand.Quote(submodule.Name)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -119,11 +119,11 @@ func (c *GitCommand) SubmoduleAdd(name string, path string, url string) error {
|
||||
|
||||
func (c *GitCommand) SubmoduleUpdateUrl(name string, path string, newUrl string) error {
|
||||
// the set-url command is only for later git versions so we're doing it manually here
|
||||
if err := c.RunCommand("git config --file .gitmodules submodule.%s.url %s", name, newUrl); err != nil {
|
||||
if err := c.RunCommand("git config --file .gitmodules submodule.%s.url %s", c.OSCommand.Quote(name), newUrl); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.RunCommand("git submodule sync %s", path); err != nil {
|
||||
if err := c.RunCommand("git submodule sync -- %s", c.OSCommand.Quote(path)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -131,11 +131,11 @@ func (c *GitCommand) SubmoduleUpdateUrl(name string, path string, newUrl string)
|
||||
}
|
||||
|
||||
func (c *GitCommand) SubmoduleInit(path string) error {
|
||||
return c.RunCommand("git submodule init %s", path)
|
||||
return c.RunCommand("git submodule init -- %s", c.OSCommand.Quote(path))
|
||||
}
|
||||
|
||||
func (c *GitCommand) SubmoduleUpdate(path string) error {
|
||||
return c.RunCommand("git submodule update --init %s", path)
|
||||
return c.RunCommand("git submodule update --init -- %s", c.OSCommand.Quote(path))
|
||||
}
|
||||
|
||||
func (c *GitCommand) SubmoduleBulkInitCmdStr() string {
|
||||
|
||||
@@ -2,6 +2,7 @@ package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Push pushes to a branch
|
||||
@@ -59,3 +60,32 @@ func (c *GitCommand) FetchRemote(remoteName string, promptUserForCredential func
|
||||
command := fmt.Sprintf("git fetch %s", remoteName)
|
||||
return c.OSCommand.DetectUnamePass(command, promptUserForCredential)
|
||||
}
|
||||
|
||||
func (c *GitCommand) GetPullMode(mode string) string {
|
||||
if mode != "auto" {
|
||||
return mode
|
||||
}
|
||||
|
||||
var isRebase bool
|
||||
var isFf bool
|
||||
var wg sync.WaitGroup
|
||||
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
isRebase = c.GetConfigValue("pull.rebase") == "true"
|
||||
wg.Done()
|
||||
}()
|
||||
go func() {
|
||||
isFf = c.GetConfigValue("pull.ff") == "only"
|
||||
wg.Done()
|
||||
}()
|
||||
wg.Wait()
|
||||
|
||||
if isRebase {
|
||||
return "rebase"
|
||||
} else if isFf {
|
||||
return "ff-only"
|
||||
} else {
|
||||
return "merge"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,3 +96,128 @@ func TestGitCommandPush(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type getPullModeScenario struct {
|
||||
testName string
|
||||
getGitConfigValueMock func(string) (string, error)
|
||||
configPullModeValue string
|
||||
test func(string)
|
||||
}
|
||||
|
||||
func TestGetPullMode(t *testing.T) {
|
||||
|
||||
scenarios := getPullModeScenarios(t)
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
gitCmd := NewDummyGitCommand()
|
||||
gitCmd.getGitConfigValue = s.getGitConfigValueMock
|
||||
s.test(gitCmd.GetPullMode(s.configPullModeValue))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func getPullModeScenarios(t *testing.T) []getPullModeScenario {
|
||||
return []getPullModeScenario{
|
||||
{
|
||||
testName: "Merge is default",
|
||||
getGitConfigValueMock: func(s string) (string, error) {
|
||||
return "", nil
|
||||
},
|
||||
configPullModeValue: "auto",
|
||||
test: func(actual string) {
|
||||
assert.Equal(t, "merge", actual)
|
||||
},
|
||||
}, {
|
||||
testName: "Reads rebase when pull.rebase is true",
|
||||
getGitConfigValueMock: func(s string) (string, error) {
|
||||
if s == "pull.rebase" {
|
||||
return "true", nil
|
||||
}
|
||||
return "", nil
|
||||
},
|
||||
configPullModeValue: "auto",
|
||||
test: func(actual string) {
|
||||
assert.Equal(t, "rebase", actual)
|
||||
},
|
||||
}, {
|
||||
testName: "Reads ff-only when pull.ff is only",
|
||||
getGitConfigValueMock: func(s string) (string, error) {
|
||||
if s == "pull.ff" {
|
||||
return "only", nil
|
||||
}
|
||||
return "", nil
|
||||
},
|
||||
configPullModeValue: "auto",
|
||||
test: func(actual string) {
|
||||
assert.Equal(t, "ff-only", actual)
|
||||
},
|
||||
}, {
|
||||
testName: "Reads rebase when rebase is true and ff is only",
|
||||
getGitConfigValueMock: func(s string) (string, error) {
|
||||
if s == "pull.rebase" {
|
||||
return "true", nil
|
||||
}
|
||||
if s == "pull.ff" {
|
||||
return "only", nil
|
||||
}
|
||||
return "", nil
|
||||
},
|
||||
configPullModeValue: "auto",
|
||||
test: func(actual string) {
|
||||
assert.Equal(t, "rebase", actual)
|
||||
},
|
||||
}, {
|
||||
testName: "Reads rebase when pull.rebase is true",
|
||||
getGitConfigValueMock: func(s string) (string, error) {
|
||||
if s == "pull.rebase" {
|
||||
return "true", nil
|
||||
}
|
||||
return "", nil
|
||||
},
|
||||
configPullModeValue: "auto",
|
||||
test: func(actual string) {
|
||||
assert.Equal(t, "rebase", actual)
|
||||
},
|
||||
}, {
|
||||
testName: "Reads ff-only when pull.ff is only",
|
||||
getGitConfigValueMock: func(s string) (string, error) {
|
||||
if s == "pull.ff" {
|
||||
return "only", nil
|
||||
}
|
||||
return "", nil
|
||||
},
|
||||
configPullModeValue: "auto",
|
||||
test: func(actual string) {
|
||||
assert.Equal(t, "ff-only", actual)
|
||||
},
|
||||
}, {
|
||||
testName: "Respects merge config",
|
||||
getGitConfigValueMock: func(s string) (string, error) {
|
||||
return "", nil
|
||||
},
|
||||
configPullModeValue: "merge",
|
||||
test: func(actual string) {
|
||||
assert.Equal(t, "merge", actual)
|
||||
},
|
||||
}, {
|
||||
testName: "Respects rebase config",
|
||||
getGitConfigValueMock: func(s string) (string, error) {
|
||||
return "", nil
|
||||
},
|
||||
configPullModeValue: "rebase",
|
||||
test: func(actual string) {
|
||||
assert.Equal(t, "rebase", actual)
|
||||
},
|
||||
}, {
|
||||
testName: "Respects ff-only config",
|
||||
getGitConfigValueMock: func(s string) (string, error) {
|
||||
return "", nil
|
||||
},
|
||||
configPullModeValue: "ff-only",
|
||||
test: func(actual string) {
|
||||
assert.Equal(t, "ff-only", actual)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package config
|
||||
// GetPlatformDefaultConfig gets the defaults for the platform
|
||||
func GetPlatformDefaultConfig() OSConfig {
|
||||
return OSConfig{
|
||||
EditCommand: ``,
|
||||
OpenCommand: "open {{filename}}",
|
||||
OpenLinkCommand: "open {{link}}",
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package config
|
||||
// GetPlatformDefaultConfig gets the defaults for the platform
|
||||
func GetPlatformDefaultConfig() OSConfig {
|
||||
return OSConfig{
|
||||
EditCommand: ``,
|
||||
OpenCommand: `sh -c "xdg-open {{filename}} >/dev/null"`,
|
||||
OpenLinkCommand: `sh -c "xdg-open {{link}} >/dev/null"`,
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package config
|
||||
// GetPlatformDefaultConfig gets the defaults for the platform
|
||||
func GetPlatformDefaultConfig() OSConfig {
|
||||
return OSConfig{
|
||||
EditCommand: ``,
|
||||
OpenCommand: `cmd /c "start "" {{filename}}"`,
|
||||
OpenLinkCommand: `cmd /c "start "" {{link}}"`,
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ type GuiConfig struct {
|
||||
Theme ThemeConfig `yaml:"theme"`
|
||||
CommitLength CommitLengthConfig `yaml:"commitLength"`
|
||||
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
|
||||
ShowListFooter bool `yaml:"showListFooter"`
|
||||
ShowFileTree bool `yaml:"showFileTree"`
|
||||
ShowRandomTip bool `yaml:"showRandomTip"`
|
||||
ShowCommandLog bool `yaml:"showCommandLog"`
|
||||
@@ -65,6 +66,7 @@ type GitConfig struct {
|
||||
OverrideGpg bool `yaml:"overrideGpg"`
|
||||
DisableForcePushing bool `yaml:"disableForcePushing"`
|
||||
CommitPrefixes map[string]CommitPrefixConfig `yaml:"commitPrefixes"`
|
||||
ParseEmoji bool `yaml:"parseEmoji"`
|
||||
}
|
||||
|
||||
type PagingConfig struct {
|
||||
@@ -160,9 +162,11 @@ type KeybindingUniversalConfig struct {
|
||||
DiffingMenu string `yaml:"diffingMenu"`
|
||||
DiffingMenuAlt string `yaml:"diffingMenu-alt"`
|
||||
CopyToClipboard string `yaml:"copyToClipboard"`
|
||||
OpenRecentRepos string `yaml:"openRecentRepos"`
|
||||
SubmitEditorText string `yaml:"submitEditorText"`
|
||||
AppendNewline string `yaml:"appendNewline"`
|
||||
ExtrasMenu string `yaml:"extrasMenu"`
|
||||
ToggleWhitespaceInDiffView string `yaml:"toggleWhitespaceInDiffView"`
|
||||
}
|
||||
|
||||
type KeybindingStatusConfig struct {
|
||||
@@ -189,6 +193,7 @@ type KeybindingFilesConfig struct {
|
||||
|
||||
type KeybindingBranchesConfig struct {
|
||||
CreatePullRequest string `yaml:"createPullRequest"`
|
||||
ViewPullRequestOptions string `yaml:"viewPullRequestOptions"`
|
||||
CopyPullRequestURL string `yaml:"copyPullRequestURL"`
|
||||
CheckoutBranchByName string `yaml:"checkoutBranchByName"`
|
||||
ForceCheckoutBranch string `yaml:"forceCheckoutBranch"`
|
||||
@@ -247,6 +252,9 @@ type KeybindingSubmodulesConfig struct {
|
||||
|
||||
// OSConfig contains config on the level of the os
|
||||
type OSConfig struct {
|
||||
// EditCommand is the command for editing a file
|
||||
EditCommand string `yaml:"editCommand,omitempty"`
|
||||
|
||||
// OpenCommand is the command for opening a file
|
||||
OpenCommand string `yaml:"openCommand,omitempty"`
|
||||
|
||||
@@ -273,6 +281,12 @@ type CustomCommandPrompt struct {
|
||||
|
||||
// this only applies to menus
|
||||
Options []CustomCommandMenuOption
|
||||
|
||||
// this only applies to menuFromCommand
|
||||
Command string `yaml:"command"`
|
||||
Filter string `yaml:"filter"`
|
||||
ValueFormat string `yaml:"valueFormat"`
|
||||
LabelFormat string `yaml:"labelFormat"`
|
||||
}
|
||||
|
||||
type CustomCommandMenuOption struct {
|
||||
@@ -302,6 +316,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
},
|
||||
CommitLength: CommitLengthConfig{Show: true},
|
||||
SkipNoStagedFilesWarning: false,
|
||||
ShowListFooter: true,
|
||||
ShowCommandLog: true,
|
||||
ShowFileTree: false,
|
||||
ShowRandomTip: true,
|
||||
@@ -317,7 +332,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
Args: "",
|
||||
},
|
||||
Pull: PullConfig{
|
||||
Mode: "merge",
|
||||
Mode: "auto",
|
||||
},
|
||||
SkipHookPrefix: "WIP",
|
||||
AutoFetch: true,
|
||||
@@ -325,6 +340,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
AllBranchesLogCmd: "git log --graph --all --color=always --abbrev-commit --decorate --date=relative --pretty=medium",
|
||||
DisableForcePushing: false,
|
||||
CommitPrefixes: map[string]CommitPrefixConfig(nil),
|
||||
ParseEmoji: false,
|
||||
},
|
||||
Refresher: RefresherConfig{
|
||||
RefreshInterval: 10,
|
||||
@@ -372,6 +388,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
New: "n",
|
||||
Edit: "e",
|
||||
OpenFile: "o",
|
||||
OpenRecentRepos: "<c-r>",
|
||||
ScrollUpMain: "<pgup>",
|
||||
ScrollDownMain: "<pgdown>",
|
||||
ScrollUpMainAlt1: "K",
|
||||
@@ -397,6 +414,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
SubmitEditorText: "<enter>",
|
||||
AppendNewline: "<a-enter>",
|
||||
ExtrasMenu: "@",
|
||||
ToggleWhitespaceInDiffView: "<c-w>",
|
||||
},
|
||||
Status: KeybindingStatusConfig{
|
||||
CheckForUpdate: "u",
|
||||
@@ -421,6 +439,7 @@ func GetDefaultConfig() *UserConfig {
|
||||
Branches: KeybindingBranchesConfig{
|
||||
CopyPullRequestURL: "<c-y>",
|
||||
CreatePullRequest: "o",
|
||||
ViewPullRequestOptions: "O",
|
||||
CheckoutBranchByName: "c",
|
||||
ForceCheckoutBranch: "F",
|
||||
RebaseBranch: "r",
|
||||
|
||||
@@ -91,23 +91,25 @@ func (gui *Gui) handleBranchPress() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCreatePullRequestPress() error {
|
||||
pullRequest := commands.NewPullRequest(gui.GitCommand)
|
||||
|
||||
branch := gui.getSelectedBranch()
|
||||
url, err := pullRequest.Create(branch)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
gui.OnRunCommand(oscommands.NewCmdLogEntry(fmt.Sprintf("Creating pull request at URL: %s", url), "Create pull request", false))
|
||||
return gui.createPullRequest(branch.Name, "")
|
||||
}
|
||||
|
||||
return nil
|
||||
func (gui *Gui) handleCreatePullRequestMenu() error {
|
||||
selectedBranch := gui.getSelectedBranch()
|
||||
if selectedBranch == nil {
|
||||
return nil
|
||||
}
|
||||
checkedOutBranch := gui.getCheckedOutBranch()
|
||||
|
||||
return gui.createPullRequestMenu(selectedBranch, checkedOutBranch)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCopyPullRequestURLPress() error {
|
||||
pullRequest := commands.NewPullRequest(gui.GitCommand)
|
||||
|
||||
branch := gui.getSelectedBranch()
|
||||
url, err := pullRequest.CopyURL(branch)
|
||||
url, err := pullRequest.CopyURL(branch.Name, "")
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
@@ -379,16 +381,14 @@ func (gui *Gui) handleRebaseOntoBranch(selectedBranchName string) error {
|
||||
|
||||
func (gui *Gui) handleFastForward() error {
|
||||
branch := gui.getSelectedBranch()
|
||||
if branch == nil {
|
||||
if branch == nil || !branch.IsRealBranch() {
|
||||
return nil
|
||||
}
|
||||
if branch.Pushables == "" {
|
||||
return nil
|
||||
}
|
||||
if branch.Pushables == "?" {
|
||||
|
||||
if !branch.IsTrackingRemote() {
|
||||
return gui.createErrorPanel(gui.Tr.FwdNoUpstream)
|
||||
}
|
||||
if branch.Pushables != "0" {
|
||||
if branch.HasCommitsToPush() {
|
||||
return gui.createErrorPanel(gui.Tr.FwdCommitsToPush)
|
||||
}
|
||||
|
||||
@@ -435,7 +435,7 @@ func (gui *Gui) handleCreateResetToBranchMenu() error {
|
||||
|
||||
func (gui *Gui) handleRenameBranch() error {
|
||||
branch := gui.getSelectedBranch()
|
||||
if branch == nil {
|
||||
if branch == nil || !branch.IsRealBranch() {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -469,8 +469,7 @@ func (gui *Gui) handleRenameBranch() error {
|
||||
// I could do an explicit check here for whether the branch is tracking a remote branch
|
||||
// but if we've selected it we'll already know that via Pullables and Pullables.
|
||||
// Bit of a hack but I'm lazy.
|
||||
notTrackingRemote := branch.Pullables == "?"
|
||||
if notTrackingRemote {
|
||||
if !branch.IsTrackingRemote() {
|
||||
return promptForNewName()
|
||||
}
|
||||
|
||||
@@ -505,8 +504,8 @@ func (gui *Gui) handleNewBranchOffCurrentItem() error {
|
||||
|
||||
prefilledName := ""
|
||||
if context.GetKey() == REMOTE_BRANCHES_CONTEXT_KEY {
|
||||
// will set to the remote's existing name
|
||||
prefilledName = item.ID()
|
||||
// will set to the remote's branch name without the remote name
|
||||
prefilledName = strings.SplitAfterN(item.ID(), "/", 2)[1]
|
||||
}
|
||||
|
||||
return gui.prompt(promptOpts{
|
||||
@@ -555,7 +554,7 @@ func (gui *Gui) findBranchNameSuggestions(input string) []*types.Suggestion {
|
||||
for i, branchName := range matchingBranchNames {
|
||||
suggestions[i] = &types.Suggestion{
|
||||
Value: branchName,
|
||||
Label: utils.ColoredString(branchName, presentation.GetBranchColor(branchName)),
|
||||
Label: presentation.GetBranchTextStyle(branchName).Sprint(branchName),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ import "github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
// you can only copy from one context at a time, because the order and position of commits matter
|
||||
|
||||
func (gui *Gui) resetCherryPickingIfNecessary(context Context) error {
|
||||
oldContextKey := gui.State.Modes.CherryPicking.ContextKey
|
||||
oldContextKey := ContextKey(gui.State.Modes.CherryPicking.ContextKey)
|
||||
|
||||
if oldContextKey != context.GetKey() {
|
||||
// need to reset the cherry picking mode
|
||||
gui.State.Modes.CherryPicking.ContextKey = context.GetKey()
|
||||
gui.State.Modes.CherryPicking.ContextKey = string(context.GetKey())
|
||||
gui.State.Modes.CherryPicking.CherryPickedCommits = make([]*models.Commit, 0)
|
||||
|
||||
return gui.rerenderContextViewIfPresent(oldContextKey)
|
||||
@@ -156,7 +156,7 @@ func (gui *Gui) HandlePasteCommits() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) exitCherryPickingMode() error {
|
||||
contextKey := gui.State.Modes.CherryPicking.ContextKey
|
||||
contextKey := ContextKey(gui.State.Modes.CherryPicking.ContextKey)
|
||||
|
||||
gui.State.Modes.CherryPicking.ContextKey = ""
|
||||
gui.State.Modes.CherryPicking.CherryPickedCommits = nil
|
||||
|
||||
@@ -6,11 +6,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/constants"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func (gui *Gui) GetOnRunCommand() func(entry oscommands.CmdLogEntry) {
|
||||
@@ -25,17 +24,17 @@ func (gui *Gui) GetOnRunCommand() func(entry oscommands.CmdLogEntry) {
|
||||
gui.Views.Extras.Autoscroll = true
|
||||
|
||||
if entry.GetSpan() != currentSpan {
|
||||
fmt.Fprint(gui.Views.Extras, "\n"+utils.ColoredString(entry.GetSpan(), color.FgYellow))
|
||||
fmt.Fprint(gui.Views.Extras, "\n"+style.FgYellow.Sprint(entry.GetSpan()))
|
||||
currentSpan = entry.GetSpan()
|
||||
}
|
||||
|
||||
clrAttr := theme.DefaultTextColor
|
||||
textStyle := theme.DefaultTextColor
|
||||
if !entry.GetCommandLine() {
|
||||
clrAttr = color.FgMagenta
|
||||
textStyle = style.FgMagenta
|
||||
}
|
||||
gui.CmdLog = append(gui.CmdLog, entry.GetCmdStr())
|
||||
indentedCmdStr := " " + strings.Replace(entry.GetCmdStr(), "\n", "\n ", -1)
|
||||
fmt.Fprint(gui.Views.Extras, "\n"+utils.ColoredString(indentedCmdStr, clrAttr))
|
||||
fmt.Fprint(gui.Views.Extras, "\n"+textStyle.Sprint(indentedCmdStr))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,14 +43,14 @@ func (gui *Gui) printCommandLogHeader() {
|
||||
gui.Tr.CommandLogHeader,
|
||||
gui.getKeyDisplay(gui.Config.GetUserConfig().Keybinding.Universal.ExtrasMenu),
|
||||
)
|
||||
fmt.Fprintln(gui.Views.Extras, utils.ColoredString(introStr, color.FgCyan))
|
||||
fmt.Fprintln(gui.Views.Extras, style.FgCyan.Sprint(introStr))
|
||||
|
||||
if gui.Config.GetUserConfig().Gui.ShowRandomTip {
|
||||
fmt.Fprintf(
|
||||
gui.Views.Extras,
|
||||
"%s: %s",
|
||||
utils.ColoredString(gui.Tr.RandomTip, color.FgYellow),
|
||||
utils.ColoredString(gui.getRandomTip(), color.FgGreen),
|
||||
style.FgYellow.Sprint(gui.Tr.RandomTip),
|
||||
style.FgGreen.Sprint(gui.getRandomTip()),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -102,7 +101,7 @@ func (gui *Gui) getRandomTip() string {
|
||||
formattedKey(config.Universal.GoInto),
|
||||
),
|
||||
fmt.Sprintf(
|
||||
"You can diff two commits by pressing '%s' one one commit and then navigating to the other. You can then press '%s' to view the files of the diff",
|
||||
"You can diff two commits by pressing '%s' on one commit and then navigating to the other. You can then press '%s' to view the files of the diff",
|
||||
formattedKey(config.Universal.DiffingMenu),
|
||||
formattedKey(config.Universal.GoInto),
|
||||
),
|
||||
|
||||
@@ -461,9 +461,43 @@ func (gui *Gui) handleCommitRevert() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := gui.GitCommand.WithSpan(gui.Tr.Spans.RevertCommit).Revert(gui.State.Commits[gui.State.Panels.Commits.SelectedLineIdx].Sha); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
commit := gui.getSelectedLocalCommit()
|
||||
|
||||
if commit.IsMerge() {
|
||||
return gui.createRevertMergeCommitMenu(commit)
|
||||
} else {
|
||||
if err := gui.GitCommand.WithSpan(gui.Tr.Spans.RevertCommit).Revert(commit.Sha); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
return gui.afterRevertCommit()
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) createRevertMergeCommitMenu(commit *models.Commit) error {
|
||||
menuItems := make([]*menuItem, len(commit.Parents))
|
||||
for i, parentSha := range commit.Parents {
|
||||
i := i
|
||||
message, err := gui.GitCommand.GetCommitMessageFirstLine(parentSha)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
menuItems[i] = &menuItem{
|
||||
displayString: fmt.Sprintf("%s: %s", utils.SafeTruncate(parentSha, 8), message),
|
||||
onPress: func() error {
|
||||
parentNumber := i + 1
|
||||
if err := gui.GitCommand.WithSpan(gui.Tr.Spans.RevertCommit).RevertMerge(commit.Sha, parentNumber); err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
return gui.afterRevertCommit()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return gui.createMenu(gui.Tr.SelectParentCommitForMerge, menuItems, createMenuOptions{showCancel: true})
|
||||
}
|
||||
|
||||
func (gui *Gui) afterRevertCommit() error {
|
||||
gui.State.Panels.Commits.SelectedLineIdx++
|
||||
return gui.refreshSidePanels(refreshOptions{mode: BLOCK_UI, scope: []RefreshableView{COMMITS, BRANCHES}})
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ package gui
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
@@ -316,8 +316,7 @@ func (gui *Gui) wrappedHandler(f func() error) func(g *gocui.Gui, v *gocui.View)
|
||||
}
|
||||
|
||||
func (gui *Gui) createErrorPanel(message string) error {
|
||||
colorFunction := color.New(color.FgRed).SprintFunc()
|
||||
coloredMessage := colorFunction(strings.TrimSpace(message))
|
||||
coloredMessage := style.FgRed.Sprint(strings.TrimSpace(message))
|
||||
if err := gui.refreshSidePanels(refreshOptions{mode: ASYNC}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@@ -28,6 +33,11 @@ type CustomCommandObjects struct {
|
||||
PromptResponses []string
|
||||
}
|
||||
|
||||
type commandMenuEntry struct {
|
||||
label string
|
||||
value string
|
||||
}
|
||||
|
||||
func (gui *Gui) resolveTemplate(templateStr string, promptResponses []string) (string, error) {
|
||||
objects := CustomCommandObjects{
|
||||
SelectedFile: gui.getSelectedFile(),
|
||||
@@ -49,6 +59,180 @@ func (gui *Gui) resolveTemplate(templateStr string, promptResponses []string) (s
|
||||
return utils.ResolveTemplate(templateStr, objects)
|
||||
}
|
||||
|
||||
func (gui *Gui) inputPrompt(prompt config.CustomCommandPrompt, promptResponses []string, responseIdx int, wrappedF func() error) error {
|
||||
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
initialValue, err := gui.resolveTemplate(prompt.InitialValue, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.prompt(promptOpts{
|
||||
title: title,
|
||||
initialContent: initialValue,
|
||||
handleConfirm: func(str string) error {
|
||||
promptResponses[responseIdx] = str
|
||||
return wrappedF()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) menuPrompt(prompt config.CustomCommandPrompt, promptResponses []string, responseIdx int, wrappedF func() error) error {
|
||||
// need to make a menu here some how
|
||||
menuItems := make([]*menuItem, len(prompt.Options))
|
||||
for i, option := range prompt.Options {
|
||||
option := option
|
||||
|
||||
nameTemplate := option.Name
|
||||
if nameTemplate == "" {
|
||||
// this allows you to only pass values rather than bother with names/descriptions
|
||||
nameTemplate = option.Value
|
||||
}
|
||||
name, err := gui.resolveTemplate(nameTemplate, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
description, err := gui.resolveTemplate(option.Description, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
value, err := gui.resolveTemplate(option.Value, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
menuItems[i] = &menuItem{
|
||||
displayStrings: []string{name, style.FgYellow.Sprint(description)},
|
||||
onPress: func() error {
|
||||
promptResponses[responseIdx] = value
|
||||
return wrappedF()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.createMenu(title, menuItems, createMenuOptions{showCancel: true})
|
||||
}
|
||||
|
||||
func (gui *Gui) GenerateMenuCandidates(commandOutput, filter, valueFormat, labelFormat string) ([]commandMenuEntry, error) {
|
||||
reg, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
return nil, gui.surfaceError(errors.New("unable to parse filter regex, error: " + err.Error()))
|
||||
}
|
||||
|
||||
buff := bytes.NewBuffer(nil)
|
||||
|
||||
valueTemp, err := template.New("format").Parse(valueFormat)
|
||||
if err != nil {
|
||||
return nil, gui.surfaceError(errors.New("unable to parse value format, error: " + err.Error()))
|
||||
}
|
||||
|
||||
colorFuncMap := style.TemplateFuncMapAddColors(template.FuncMap{})
|
||||
|
||||
descTemp, err := template.New("format").Funcs(colorFuncMap).Parse(labelFormat)
|
||||
if err != nil {
|
||||
return nil, gui.surfaceError(errors.New("unable to parse label format, error: " + err.Error()))
|
||||
}
|
||||
|
||||
candidates := []commandMenuEntry{}
|
||||
for _, str := range strings.Split(string(commandOutput), "\n") {
|
||||
if str == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
tmplData := map[string]string{}
|
||||
out := reg.FindAllStringSubmatch(str, -1)
|
||||
if len(out) > 0 {
|
||||
for groupIdx, group := range reg.SubexpNames() {
|
||||
// Record matched group with group ids
|
||||
matchName := "group_" + strconv.Itoa(groupIdx)
|
||||
tmplData[matchName] = out[0][groupIdx]
|
||||
// Record last named group non-empty matches as group matches
|
||||
if group != "" {
|
||||
tmplData[group] = out[0][groupIdx]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = valueTemp.Execute(buff, tmplData)
|
||||
if err != nil {
|
||||
return candidates, gui.surfaceError(err)
|
||||
}
|
||||
entry := commandMenuEntry{
|
||||
value: strings.TrimSpace(buff.String()),
|
||||
}
|
||||
|
||||
if labelFormat != "" {
|
||||
buff.Reset()
|
||||
err = descTemp.Execute(buff, tmplData)
|
||||
if err != nil {
|
||||
return candidates, gui.surfaceError(err)
|
||||
}
|
||||
entry.label = strings.TrimSpace(buff.String())
|
||||
} else {
|
||||
entry.label = entry.value
|
||||
}
|
||||
|
||||
candidates = append(candidates, entry)
|
||||
|
||||
buff.Reset()
|
||||
}
|
||||
return candidates, err
|
||||
}
|
||||
|
||||
func (gui *Gui) menuPromptFromCommand(prompt config.CustomCommandPrompt, promptResponses []string, responseIdx int, wrappedF func() error) error {
|
||||
// Collect cmd to run from config
|
||||
cmdStr, err := gui.resolveTemplate(prompt.Command, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
// Collect Filter regexp
|
||||
filter, err := gui.resolveTemplate(prompt.Filter, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
// Run and save output
|
||||
message, err := gui.GitCommand.RunCommandWithOutput(cmdStr)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
// Need to make a menu out of what the cmd has displayed
|
||||
candidates, err := gui.GenerateMenuCandidates(message, filter, prompt.ValueFormat, prompt.LabelFormat)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
menuItems := make([]*menuItem, len(candidates))
|
||||
for i := range candidates {
|
||||
menuItems[i] = &menuItem{
|
||||
displayStrings: []string{candidates[i].label},
|
||||
onPress: func() error {
|
||||
promptResponses[responseIdx] = candidates[i].value
|
||||
return wrappedF()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.createMenu(title, menuItems, createMenuOptions{showCancel: true})
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand) func() error {
|
||||
return func() error {
|
||||
promptResponses := make([]string, len(customCommand.Prompts))
|
||||
@@ -89,72 +273,18 @@ func (gui *Gui) handleCustomCommandKeybinding(customCommand config.CustomCommand
|
||||
switch prompt.Type {
|
||||
case "input":
|
||||
f = func() error {
|
||||
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
initialValue, err := gui.resolveTemplate(prompt.InitialValue, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.prompt(promptOpts{
|
||||
title: title,
|
||||
initialContent: initialValue,
|
||||
handleConfirm: func(str string) error {
|
||||
promptResponses[idx] = str
|
||||
|
||||
return wrappedF()
|
||||
},
|
||||
})
|
||||
return gui.inputPrompt(prompt, promptResponses, idx, wrappedF)
|
||||
}
|
||||
case "menu":
|
||||
f = func() error {
|
||||
// need to make a menu here some how
|
||||
menuItems := make([]*menuItem, len(prompt.Options))
|
||||
for i, option := range prompt.Options {
|
||||
option := option
|
||||
|
||||
nameTemplate := option.Name
|
||||
if nameTemplate == "" {
|
||||
// this allows you to only pass values rather than bother with names/descriptions
|
||||
nameTemplate = option.Value
|
||||
}
|
||||
name, err := gui.resolveTemplate(nameTemplate, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
description, err := gui.resolveTemplate(option.Description, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
value, err := gui.resolveTemplate(option.Value, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
menuItems[i] = &menuItem{
|
||||
displayStrings: []string{name, utils.ColoredString(description, color.FgYellow)},
|
||||
onPress: func() error {
|
||||
promptResponses[idx] = value
|
||||
|
||||
return wrappedF()
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
title, err := gui.resolveTemplate(prompt.Title, promptResponses)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
|
||||
return gui.createMenu(title, menuItems, createMenuOptions{showCancel: true})
|
||||
return gui.menuPrompt(prompt, promptResponses, idx, wrappedF)
|
||||
}
|
||||
case "menuFromCommand":
|
||||
f = func() error {
|
||||
return gui.menuPromptFromCommand(prompt, promptResponses, idx, wrappedF)
|
||||
}
|
||||
default:
|
||||
return gui.createErrorPanel("custom command prompt must have a type of 'input' or 'menu'")
|
||||
return gui.createErrorPanel("custom command prompt must have a type of 'input', 'menu' or 'menuFromCommand'")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@ package gui
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/modes/diffing"
|
||||
)
|
||||
|
||||
func (gui *Gui) exitDiffMode() error {
|
||||
gui.State.Modes.Diffing = Diffing{}
|
||||
gui.State.Modes.Diffing = diffing.New()
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
}
|
||||
|
||||
@@ -145,7 +147,7 @@ func (gui *Gui) handleCreateDiffingMenuPanel() error {
|
||||
{
|
||||
displayString: gui.Tr.LcExitDiffMode,
|
||||
onPress: func() error {
|
||||
gui.State.Modes.Diffing = Diffing{}
|
||||
gui.State.Modes.Diffing = diffing.New()
|
||||
return gui.refreshSidePanels(refreshOptions{mode: ASYNC})
|
||||
},
|
||||
},
|
||||
|
||||
21
pkg/gui/dummies.go
Normal file
21
pkg/gui/dummies.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/updates"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// NewDummyGui creates a new dummy GUI for testing
|
||||
func NewDummyUpdater() *updates.Updater {
|
||||
DummyUpdater, _ := updates.NewUpdater(utils.NewDummyLog(), config.NewDummyAppConfig(), oscommands.NewDummyOSCommand(), i18n.NewTranslationSet(utils.NewDummyLog()))
|
||||
return DummyUpdater
|
||||
}
|
||||
|
||||
func NewDummyGui() *Gui {
|
||||
DummyGui, _ := NewGui(utils.NewDummyLog(), commands.NewDummyGitCommand(), oscommands.NewDummyOSCommand(), i18n.NewTranslationSet(utils.NewDummyLog()), config.NewDummyAppConfig(), NewDummyUpdater(), "", false)
|
||||
return DummyGui
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
|
||||
return gui.refreshMergePanelWithLock()
|
||||
}
|
||||
|
||||
cmdStr := gui.GitCommand.WorktreeFileDiffCmdStr(node, false, !node.GetHasUnstagedChanges() && node.GetHasStagedChanges())
|
||||
cmdStr := gui.GitCommand.WorktreeFileDiffCmdStr(node, false, !node.GetHasUnstagedChanges() && node.GetHasStagedChanges(), gui.State.IgnoreWhitespaceInDiffView)
|
||||
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
|
||||
|
||||
refreshOpts := refreshMainOpts{main: &viewUpdateOpts{
|
||||
@@ -81,7 +81,7 @@ func (gui *Gui) selectFile(alreadySelected bool) error {
|
||||
|
||||
if node.GetHasUnstagedChanges() {
|
||||
if node.GetHasStagedChanges() {
|
||||
cmdStr := gui.GitCommand.WorktreeFileDiffCmdStr(node, false, true)
|
||||
cmdStr := gui.GitCommand.WorktreeFileDiffCmdStr(node, false, true, gui.State.IgnoreWhitespaceInDiffView)
|
||||
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
|
||||
|
||||
refreshOpts.secondary = &viewUpdateOpts{
|
||||
@@ -617,7 +617,7 @@ func (gui *Gui) handlePullFiles() error {
|
||||
}
|
||||
|
||||
// if we have no upstream branch we need to set that first
|
||||
if currentBranch.Pullables == "?" {
|
||||
if !currentBranch.IsTrackingRemote() {
|
||||
// see if we have this branch in our config with an upstream
|
||||
conf, err := gui.GitCommand.Repo.Config()
|
||||
if err != nil {
|
||||
@@ -659,10 +659,11 @@ func (gui *Gui) pullFiles(opts PullFilesOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
mode := gui.Config.GetUserConfig().Git.Pull.Mode
|
||||
mode := &gui.Config.GetUserConfig().Git.Pull.Mode
|
||||
*mode = gui.GitCommand.GetPullMode(*mode)
|
||||
|
||||
// TODO: this doesn't look like a good idea. Why the goroutine?
|
||||
go utils.Safe(func() { _ = gui.pullWithMode(mode, opts) })
|
||||
go utils.Safe(func() { _ = gui.pullWithMode(*mode, opts) })
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -740,16 +741,21 @@ func (gui *Gui) pushFiles() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if currentBranch.Pullables == "?" {
|
||||
// see if we have this branch in our config with an upstream
|
||||
conf, err := gui.GitCommand.Repo.Config()
|
||||
if currentBranch.IsTrackingRemote() {
|
||||
if currentBranch.HasCommitsToPull() {
|
||||
return gui.requestToForcePush()
|
||||
} else {
|
||||
return gui.pushWithForceFlag(false, "", "")
|
||||
}
|
||||
} else {
|
||||
// see if we have an upstream for this branch in our config
|
||||
upstream, err := gui.upstreamForBranchInConfig(currentBranch.Name)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
for branchName, branch := range conf.Branches {
|
||||
if branchName == currentBranch.Name {
|
||||
return gui.pushWithForceFlag(false, "", fmt.Sprintf("%s %s", branch.Remote, branchName))
|
||||
}
|
||||
|
||||
if upstream != "" {
|
||||
return gui.pushWithForceFlag(false, "", upstream)
|
||||
}
|
||||
|
||||
if gui.GitCommand.PushToCurrent {
|
||||
@@ -758,15 +764,15 @@ func (gui *Gui) pushFiles() error {
|
||||
return gui.prompt(promptOpts{
|
||||
title: gui.Tr.EnterUpstream,
|
||||
initialContent: "origin " + currentBranch.Name,
|
||||
handleConfirm: func(response string) error {
|
||||
return gui.pushWithForceFlag(false, response, "")
|
||||
handleConfirm: func(upstream string) error {
|
||||
return gui.pushWithForceFlag(false, upstream, "")
|
||||
},
|
||||
})
|
||||
}
|
||||
} else if currentBranch.Pullables == "0" {
|
||||
return gui.pushWithForceFlag(false, "", "")
|
||||
}
|
||||
}
|
||||
|
||||
func (gui *Gui) requestToForcePush() error {
|
||||
forcePushDisabled := gui.Config.GetUserConfig().Git.DisableForcePushing
|
||||
if forcePushDisabled {
|
||||
return gui.createErrorPanel(gui.Tr.ForcePushDisabled)
|
||||
@@ -781,6 +787,21 @@ func (gui *Gui) pushFiles() error {
|
||||
})
|
||||
}
|
||||
|
||||
func (gui *Gui) upstreamForBranchInConfig(branchName string) (string, error) {
|
||||
conf, err := gui.GitCommand.Repo.Config()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for configBranchName, configBranch := range conf.Branches {
|
||||
if configBranchName == branchName {
|
||||
return fmt.Sprintf("%s %s", configBranch.Remote, configBranchName), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (gui *Gui) handleSwitchToMerge() error {
|
||||
file := gui.getSelectedFile()
|
||||
if file == nil {
|
||||
|
||||
@@ -54,23 +54,33 @@ func TestBuildTreeFromFiles(t *testing.T) {
|
||||
name: "paths that can be compressed",
|
||||
files: []*models.File{
|
||||
{
|
||||
Name: "dir1/a",
|
||||
Name: "dir1/dir3/a",
|
||||
},
|
||||
{
|
||||
Name: "dir2/b",
|
||||
Name: "dir2/dir4/b",
|
||||
},
|
||||
},
|
||||
expected: &FileNode{
|
||||
Path: "",
|
||||
Children: []*FileNode{
|
||||
{
|
||||
File: &models.File{Name: "dir1/a"},
|
||||
Path: "dir1/a",
|
||||
Path: "dir1/dir3",
|
||||
Children: []*FileNode{
|
||||
{
|
||||
File: &models.File{Name: "dir1/dir3/a"},
|
||||
Path: "dir1/dir3/a",
|
||||
},
|
||||
},
|
||||
CompressionLevel: 1,
|
||||
},
|
||||
{
|
||||
File: &models.File{Name: "dir2/b"},
|
||||
Path: "dir2/b",
|
||||
Path: "dir2/dir4",
|
||||
Children: []*FileNode{
|
||||
{
|
||||
File: &models.File{Name: "dir2/dir4/b"},
|
||||
Path: "dir2/dir4/b",
|
||||
},
|
||||
},
|
||||
CompressionLevel: 1,
|
||||
},
|
||||
},
|
||||
@@ -201,12 +211,12 @@ func TestBuildFlatTreeFromFiles(t *testing.T) {
|
||||
{
|
||||
File: &models.File{Name: "dir1/a"},
|
||||
Path: "dir1/a",
|
||||
CompressionLevel: 1,
|
||||
CompressionLevel: 0,
|
||||
},
|
||||
{
|
||||
File: &models.File{Name: "dir2/b"},
|
||||
Path: "dir2/b",
|
||||
CompressionLevel: 1,
|
||||
CompressionLevel: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -351,23 +361,33 @@ func TestBuildTreeFromCommitFiles(t *testing.T) {
|
||||
name: "paths that can be compressed",
|
||||
files: []*models.CommitFile{
|
||||
{
|
||||
Name: "dir1/a",
|
||||
Name: "dir1/dir3/a",
|
||||
},
|
||||
{
|
||||
Name: "dir2/b",
|
||||
Name: "dir2/dir4/b",
|
||||
},
|
||||
},
|
||||
expected: &CommitFileNode{
|
||||
Path: "",
|
||||
Children: []*CommitFileNode{
|
||||
{
|
||||
File: &models.CommitFile{Name: "dir1/a"},
|
||||
Path: "dir1/a",
|
||||
Path: "dir1/dir3",
|
||||
Children: []*CommitFileNode{
|
||||
{
|
||||
File: &models.CommitFile{Name: "dir1/dir3/a"},
|
||||
Path: "dir1/dir3/a",
|
||||
},
|
||||
},
|
||||
CompressionLevel: 1,
|
||||
},
|
||||
{
|
||||
File: &models.CommitFile{Name: "dir2/b"},
|
||||
Path: "dir2/b",
|
||||
Path: "dir2/dir4",
|
||||
Children: []*CommitFileNode{
|
||||
{
|
||||
File: &models.CommitFile{Name: "dir2/dir4/b"},
|
||||
Path: "dir2/dir4/b",
|
||||
},
|
||||
},
|
||||
CompressionLevel: 1,
|
||||
},
|
||||
},
|
||||
@@ -464,12 +484,12 @@ func TestBuildFlatTreeFromCommitFiles(t *testing.T) {
|
||||
{
|
||||
File: &models.CommitFile{Name: "dir1/a"},
|
||||
Path: "dir1/a",
|
||||
CompressionLevel: 1,
|
||||
CompressionLevel: 0,
|
||||
},
|
||||
{
|
||||
File: &models.CommitFile{Name: "dir2/b"},
|
||||
Path: "dir2/b",
|
||||
CompressionLevel: 1,
|
||||
CompressionLevel: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -3,11 +3,15 @@ package filetree
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/xo/terminfo"
|
||||
)
|
||||
|
||||
func TestRender(t *testing.T) {
|
||||
color.ForceSetColorLevel(terminfo.ColorLevelNone)
|
||||
|
||||
scenarios := []struct {
|
||||
name string
|
||||
root *FileNode
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package filetree
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
)
|
||||
|
||||
@@ -182,7 +180,7 @@ func (s *FileNode) NameAtDepth(depth int) string {
|
||||
prevName = join(splitPrevName[depth:])
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s%s%s", prevName, " → ", name)
|
||||
return prevName + " → " + name
|
||||
}
|
||||
|
||||
return name
|
||||
|
||||
@@ -84,9 +84,13 @@ func TestCompress(t *testing.T) {
|
||||
Path: "",
|
||||
Children: []*FileNode{
|
||||
{
|
||||
Path: "dir1/file2",
|
||||
File: &models.File{Name: "file2", ShortStatus: "M ", HasUnstagedChanges: true},
|
||||
CompressionLevel: 1,
|
||||
Path: "dir1",
|
||||
Children: []*FileNode{
|
||||
{
|
||||
File: &models.File{Name: "file2", ShortStatus: "M ", HasUnstagedChanges: true},
|
||||
Path: "dir1/file2",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "dir2",
|
||||
@@ -102,9 +106,14 @@ func TestCompress(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "dir3/dir3-1/file5",
|
||||
File: &models.File{Name: "file5", ShortStatus: "M ", HasUnstagedChanges: true},
|
||||
CompressionLevel: 2,
|
||||
Path: "dir3/dir3-1",
|
||||
CompressionLevel: 1,
|
||||
Children: []*FileNode{
|
||||
{
|
||||
File: &models.File{Name: "file5", ShortStatus: "M ", HasUnstagedChanges: true},
|
||||
Path: "dir3/dir3-1/file5",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
File: &models.File{Name: "file1", ShortStatus: "M ", HasUnstagedChanges: true},
|
||||
|
||||
@@ -170,7 +170,7 @@ func compressAux(node INode) INode {
|
||||
children := node.GetChildren()
|
||||
for i := range children {
|
||||
grandchildren := children[i].GetChildren()
|
||||
for len(grandchildren) == 1 {
|
||||
for len(grandchildren) == 1 && !grandchildren[0].IsLeaf() {
|
||||
grandchildren[0].SetCompressionLevel(children[i].GetCompressionLevel() + 1)
|
||||
children[i] = grandchildren[0]
|
||||
grandchildren = children[i].GetChildren()
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
@@ -21,7 +20,10 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/filetree"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/lbl"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/mergeconflicts"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/modes/cherrypicking"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/modes/diffing"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/modes/filtering"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/tasks"
|
||||
@@ -269,31 +271,10 @@ const (
|
||||
COMPLETE
|
||||
)
|
||||
|
||||
// if ref is blank we're not diffing anything
|
||||
type Diffing struct {
|
||||
Ref string
|
||||
Reverse bool
|
||||
}
|
||||
|
||||
func (m *Diffing) Active() bool {
|
||||
return m.Ref != ""
|
||||
}
|
||||
|
||||
type CherryPicking struct {
|
||||
CherryPickedCommits []*models.Commit
|
||||
|
||||
// we only allow cherry picking from one context at a time, so you can't copy a commit from the local commits context and then also copy a commit in the reflog context
|
||||
ContextKey ContextKey
|
||||
}
|
||||
|
||||
func (m *CherryPicking) Active() bool {
|
||||
return len(m.CherryPickedCommits) > 0
|
||||
}
|
||||
|
||||
type Modes struct {
|
||||
Filtering filtering.Filtering
|
||||
CherryPicking CherryPicking
|
||||
Diffing Diffing
|
||||
CherryPicking cherrypicking.CherryPicking
|
||||
Diffing diffing.Diffing
|
||||
}
|
||||
|
||||
type guiMutexes struct {
|
||||
@@ -358,6 +339,9 @@ type guiState struct {
|
||||
// do this whenever we switch back and forth between repos to get the views
|
||||
// back in sync with the repo state
|
||||
ViewsSetup bool
|
||||
|
||||
// flag as to whether or not the diff view should ignore whitespace
|
||||
IgnoreWhitespaceInDiffView bool
|
||||
}
|
||||
|
||||
// reuseState determines if we pull the repo state from our repo state map or
|
||||
@@ -424,12 +408,9 @@ func (gui *Gui) resetState(filterPath string, reuseState bool) {
|
||||
},
|
||||
Ptmx: nil,
|
||||
Modes: Modes{
|
||||
Filtering: filtering.NewFiltering(filterPath),
|
||||
CherryPicking: CherryPicking{
|
||||
CherryPickedCommits: make([]*models.Commit, 0),
|
||||
ContextKey: "",
|
||||
},
|
||||
Diffing: Diffing{},
|
||||
Filtering: filtering.New(filterPath),
|
||||
CherryPicking: cherrypicking.New(),
|
||||
Diffing: diffing.New(),
|
||||
},
|
||||
ViewContextMap: contexts.initialViewContextMap(),
|
||||
ViewTabContextMap: contexts.initialViewTabContextMap(),
|
||||
@@ -518,6 +499,8 @@ func (gui *Gui) Run() error {
|
||||
|
||||
g.ASCII = runtime.GOOS == "windows" && runewidth.IsEastAsian()
|
||||
|
||||
g.ShowListFooter = userConfig.Gui.ShowListFooter
|
||||
|
||||
if userConfig.Gui.MouseEvents {
|
||||
g.Mouse = true
|
||||
}
|
||||
@@ -628,7 +611,7 @@ func (gui *Gui) runSubprocess(subprocess *exec.Cmd) error {
|
||||
subprocess.Stderr = os.Stdout
|
||||
subprocess.Stdin = os.Stdin
|
||||
|
||||
fmt.Fprintf(os.Stdout, "\n%s\n\n", utils.ColoredString("+ "+strings.Join(subprocess.Args, " "), color.FgBlue))
|
||||
fmt.Fprintf(os.Stdout, "\n%s\n\n", style.FgBlue.Sprint("+ "+strings.Join(subprocess.Args, " ")))
|
||||
|
||||
if err := subprocess.Run(); err != nil {
|
||||
// not handling the error explicitly because usually we're going to see it
|
||||
@@ -640,7 +623,7 @@ func (gui *Gui) runSubprocess(subprocess *exec.Cmd) error {
|
||||
subprocess.Stderr = ioutil.Discard
|
||||
subprocess.Stdin = nil
|
||||
|
||||
fmt.Fprintf(os.Stdout, "\n%s", utils.ColoredString(gui.Tr.PressEnterToReturn, color.FgGreen))
|
||||
fmt.Fprintf(os.Stdout, "\n%s", style.FgGreen.Sprint(gui.Tr.PressEnterToReturn))
|
||||
fmt.Scanln() // wait for enter press
|
||||
|
||||
return nil
|
||||
|
||||
@@ -80,3 +80,59 @@ func runCmdHeadless(cmd *exec.Cmd) error {
|
||||
|
||||
return f.Close()
|
||||
}
|
||||
|
||||
func TestGuiGenerateMenuCandidates(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
cmdOut string
|
||||
filter string
|
||||
valueFormat string
|
||||
labelFormat string
|
||||
test func([]commandMenuEntry, error)
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"Extract remote branch name",
|
||||
"upstream/pr-1",
|
||||
"(?P<remote>[a-z_]+)/(?P<branch>.*)",
|
||||
"{{ .branch }}",
|
||||
"Remote: {{ .remote }}",
|
||||
func(actualEntry []commandMenuEntry, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "pr-1", actualEntry[0].value)
|
||||
assert.EqualValues(t, "Remote: upstream", actualEntry[0].label)
|
||||
},
|
||||
},
|
||||
{
|
||||
"Multiple named groups with empty labelFormat",
|
||||
"upstream/pr-1",
|
||||
"(?P<remote>[a-z]*)/(?P<branch>.*)",
|
||||
"{{ .branch }}|{{ .remote }}",
|
||||
"",
|
||||
func(actualEntry []commandMenuEntry, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "pr-1|upstream", actualEntry[0].value)
|
||||
assert.EqualValues(t, "pr-1|upstream", actualEntry[0].label)
|
||||
},
|
||||
},
|
||||
{
|
||||
"Multiple named groups with group ids",
|
||||
"upstream/pr-1",
|
||||
"(?P<remote>[a-z]*)/(?P<branch>.*)",
|
||||
"{{ .group_2 }}|{{ .group_1 }}",
|
||||
"Remote: {{ .group_1 }}",
|
||||
func(actualEntry []commandMenuEntry, err error) {
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, "pr-1|upstream", actualEntry[0].value)
|
||||
assert.EqualValues(t, "Remote: upstream", actualEntry[0].label)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
s.test(NewDummyGui().GenerateMenuCandidates(s.cmdOut, s.filter, s.valueFormat, s.labelFormat))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ package gui
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/constants"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
)
|
||||
|
||||
func (gui *Gui) informationStr() string {
|
||||
@@ -15,8 +15,8 @@ func (gui *Gui) informationStr() string {
|
||||
}
|
||||
|
||||
if gui.g.Mouse {
|
||||
donate := color.New(color.FgMagenta, color.Underline).Sprint(gui.Tr.Donate)
|
||||
askQuestion := color.New(color.FgYellow, color.Underline).Sprint(gui.Tr.AskQuestion)
|
||||
donate := style.FgMagenta.SetUnderline().Sprint(gui.Tr.Donate)
|
||||
askQuestion := style.FgYellow.SetUnderline().Sprint(gui.Tr.AskQuestion)
|
||||
return fmt.Sprintf("%s %s %s", donate, askQuestion, gui.Config.GetVersion())
|
||||
} else {
|
||||
return gui.Config.GetVersion()
|
||||
|
||||
@@ -231,6 +231,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
Modifier: gocui.ModNone,
|
||||
Handler: gui.handleTopLevelReturn,
|
||||
},
|
||||
{
|
||||
ViewName: "",
|
||||
Key: gui.getKey(config.Universal.OpenRecentRepos),
|
||||
Handler: gui.handleCreateRecentReposMenu,
|
||||
Alternative: "<c-r>",
|
||||
Description: gui.Tr.SwitchRepo,
|
||||
},
|
||||
{
|
||||
ViewName: "",
|
||||
Key: gui.getKey(config.Universal.ScrollUpMain),
|
||||
@@ -538,6 +545,14 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
Handler: gui.handleCreatePullRequestPress,
|
||||
Description: gui.Tr.LcCreatePullRequest,
|
||||
},
|
||||
{
|
||||
ViewName: "branches",
|
||||
Contexts: []string{string(LOCAL_BRANCHES_CONTEXT_KEY)},
|
||||
Key: gui.getKey(config.Branches.ViewPullRequestOptions),
|
||||
Handler: gui.handleCreatePullRequestMenu,
|
||||
Description: gui.Tr.LcCreatePullRequestOptions,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
ViewName: "branches",
|
||||
Contexts: []string{string(LOCAL_BRANCHES_CONTEXT_KEY)},
|
||||
@@ -1712,6 +1727,13 @@ func (gui *Gui) GetInitialKeybindings() []*Binding {
|
||||
Description: gui.Tr.LcViewBulkSubmoduleOptions,
|
||||
OpensMenu: true,
|
||||
},
|
||||
{
|
||||
ViewName: "files",
|
||||
Contexts: []string{string(FILES_CONTEXT_KEY)},
|
||||
Key: gui.getKey(config.Universal.ToggleWhitespaceInDiffView),
|
||||
Handler: gui.toggleWhitespaceInDiffView,
|
||||
Description: gui.Tr.ToggleWhitespaceInDiffView,
|
||||
},
|
||||
{
|
||||
ViewName: "extras",
|
||||
Key: gocui.MouseWheelUp,
|
||||
|
||||
52
pkg/gui/lbl/focus.go
Normal file
52
pkg/gui/lbl/focus.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package lbl
|
||||
|
||||
import "github.com/jesseduffield/lazygit/pkg/utils"
|
||||
|
||||
func calculateOrigin(currentOrigin int, bufferHeight int, firstLineIdx int, lastLineIdx int, selectedLineIdx int, mode selectMode) int {
|
||||
needToSeeIdx, wantToSeeIdx := getNeedAndWantLineIdx(firstLineIdx, lastLineIdx, selectedLineIdx, mode)
|
||||
|
||||
return calculateNewOriginWithNeededAndWantedIdx(currentOrigin, bufferHeight, needToSeeIdx, wantToSeeIdx)
|
||||
}
|
||||
|
||||
// we want to scroll our origin so that the index we need to see is in view
|
||||
// and the other index we want to see (e.g. the other side of a line range)
|
||||
// is in as close to being in view as possible.
|
||||
func calculateNewOriginWithNeededAndWantedIdx(currentOrigin int, bufferHeight int, needToSeeIdx int, wantToSeeIdx int) int {
|
||||
origin := currentOrigin
|
||||
if needToSeeIdx < currentOrigin {
|
||||
origin = needToSeeIdx
|
||||
} else if needToSeeIdx > currentOrigin+bufferHeight {
|
||||
origin = needToSeeIdx - bufferHeight
|
||||
}
|
||||
|
||||
bottom := origin + bufferHeight
|
||||
|
||||
if wantToSeeIdx < origin {
|
||||
requiredChange := origin - wantToSeeIdx
|
||||
allowedChange := bottom - needToSeeIdx
|
||||
return origin - utils.Min(requiredChange, allowedChange)
|
||||
} else if wantToSeeIdx > origin+bufferHeight {
|
||||
requiredChange := wantToSeeIdx - bottom
|
||||
allowedChange := needToSeeIdx - origin
|
||||
return origin + utils.Min(requiredChange, allowedChange)
|
||||
} else {
|
||||
return origin
|
||||
}
|
||||
}
|
||||
|
||||
func getNeedAndWantLineIdx(firstLineIdx int, lastLineIdx int, selectedLineIdx int, mode selectMode) (int, int) {
|
||||
switch mode {
|
||||
case LINE:
|
||||
return selectedLineIdx, selectedLineIdx
|
||||
case RANGE:
|
||||
if selectedLineIdx == firstLineIdx {
|
||||
return firstLineIdx, lastLineIdx
|
||||
} else {
|
||||
return lastLineIdx, firstLineIdx
|
||||
}
|
||||
case HUNK:
|
||||
return firstLineIdx, lastLineIdx
|
||||
default:
|
||||
panic("unknown mode")
|
||||
}
|
||||
}
|
||||
100
pkg/gui/lbl/focus_test.go
Normal file
100
pkg/gui/lbl/focus_test.go
Normal file
@@ -0,0 +1,100 @@
|
||||
package lbl
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewOrigin(t *testing.T) {
|
||||
type scenario struct {
|
||||
name string
|
||||
origin int
|
||||
bufferHeight int
|
||||
firstLineIdx int
|
||||
lastLineIdx int
|
||||
selectedLineIdx int
|
||||
selectMode selectMode
|
||||
expected int
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
name: "selection above scroll window",
|
||||
origin: 50,
|
||||
bufferHeight: 100,
|
||||
firstLineIdx: 10,
|
||||
lastLineIdx: 10,
|
||||
selectedLineIdx: 10,
|
||||
selectMode: LINE,
|
||||
expected: 10,
|
||||
},
|
||||
{
|
||||
name: "selection below scroll window",
|
||||
origin: 0,
|
||||
bufferHeight: 100,
|
||||
firstLineIdx: 150,
|
||||
lastLineIdx: 150,
|
||||
selectedLineIdx: 150,
|
||||
selectMode: LINE,
|
||||
expected: 50,
|
||||
},
|
||||
{
|
||||
name: "selection within scroll window",
|
||||
origin: 0,
|
||||
bufferHeight: 100,
|
||||
firstLineIdx: 50,
|
||||
lastLineIdx: 50,
|
||||
selectedLineIdx: 50,
|
||||
selectMode: LINE,
|
||||
expected: 0,
|
||||
},
|
||||
{
|
||||
name: "range ending below scroll window with selection at end of range",
|
||||
origin: 0,
|
||||
bufferHeight: 100,
|
||||
firstLineIdx: 40,
|
||||
lastLineIdx: 150,
|
||||
selectedLineIdx: 150,
|
||||
selectMode: RANGE,
|
||||
expected: 50,
|
||||
},
|
||||
{
|
||||
name: "range ending below scroll window with selection at beginning of range",
|
||||
origin: 0,
|
||||
bufferHeight: 100,
|
||||
firstLineIdx: 40,
|
||||
lastLineIdx: 150,
|
||||
selectedLineIdx: 40,
|
||||
selectMode: RANGE,
|
||||
expected: 40,
|
||||
},
|
||||
{
|
||||
name: "range starting above scroll window with selection at beginning of range",
|
||||
origin: 50,
|
||||
bufferHeight: 100,
|
||||
firstLineIdx: 40,
|
||||
lastLineIdx: 150,
|
||||
selectedLineIdx: 40,
|
||||
selectMode: RANGE,
|
||||
expected: 40,
|
||||
},
|
||||
{
|
||||
name: "hunk extending beyond both bounds of scroll window",
|
||||
origin: 50,
|
||||
bufferHeight: 100,
|
||||
firstLineIdx: 40,
|
||||
lastLineIdx: 200,
|
||||
selectedLineIdx: 70,
|
||||
selectMode: HUNK,
|
||||
expected: 40,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
assert.EqualValues(t, s.expected, calculateOrigin(s.origin, s.bufferHeight, s.firstLineIdx, s.lastLineIdx, s.selectedLineIdx, s.selectMode))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -189,3 +189,9 @@ func (s *State) SelectTop() {
|
||||
s.SetLineSelectMode()
|
||||
s.SelectLine(0)
|
||||
}
|
||||
|
||||
func (s *State) CalculateOrigin(currentOrigin int, bufferHeight int) int {
|
||||
firstLineIdx, lastLineIdx := s.SelectedRange()
|
||||
|
||||
return calculateOrigin(currentOrigin, bufferHeight, firstLineIdx, lastLineIdx, s.GetSelectedLineIdx(), s.selectMode)
|
||||
}
|
||||
|
||||
@@ -155,25 +155,16 @@ func (gui *Gui) focusSelection(state *LblPanelState) error {
|
||||
bufferHeight := viewHeight - 1
|
||||
_, origin := stagingView.Origin()
|
||||
|
||||
firstLineIdx, lastLineIdx := state.SelectedRange()
|
||||
selectedLineIdx := state.GetSelectedLineIdx()
|
||||
|
||||
margin := 0 // we may want to have a margin in place to show context but right now I'm thinking we keep this at zero
|
||||
|
||||
var newOrigin int
|
||||
if firstLineIdx-origin < margin {
|
||||
newOrigin = firstLineIdx - margin
|
||||
} else if lastLineIdx-origin > bufferHeight-margin {
|
||||
newOrigin = lastLineIdx - bufferHeight + margin
|
||||
} else {
|
||||
newOrigin = origin
|
||||
}
|
||||
newOrigin := state.CalculateOrigin(origin, bufferHeight)
|
||||
|
||||
gui.g.Update(func(*gocui.Gui) error {
|
||||
if err := stagingView.SetOrigin(0, newOrigin); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return stagingView.SetCursor(0, state.GetSelectedLineIdx()-newOrigin)
|
||||
return stagingView.SetCursor(0, selectedLineIdx-newOrigin)
|
||||
})
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
)
|
||||
|
||||
func (gui *Gui) menuListContext() *ListContext {
|
||||
@@ -150,6 +149,7 @@ func (gui *Gui) tagsListContext() *ListContext {
|
||||
}
|
||||
|
||||
func (gui *Gui) branchCommitsListContext() *ListContext {
|
||||
parseEmoji := gui.Config.GetUserConfig().Git.ParseEmoji
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
ViewName: "commits",
|
||||
@@ -164,7 +164,13 @@ func (gui *Gui) branchCommitsListContext() *ListContext {
|
||||
Gui: gui,
|
||||
ResetMainViewOriginOnFocus: true,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
return presentation.GetCommitListDisplayStrings(gui.State.Commits, gui.State.ScreenMode != SCREEN_NORMAL, gui.cherryPickedCommitShaMap(), gui.State.Modes.Diffing.Ref)
|
||||
return presentation.GetCommitListDisplayStrings(
|
||||
gui.State.Commits,
|
||||
gui.State.ScreenMode != SCREEN_NORMAL,
|
||||
gui.cherryPickedCommitShaMap(),
|
||||
gui.State.Modes.Diffing.Ref,
|
||||
parseEmoji,
|
||||
)
|
||||
},
|
||||
SelectedItem: func() (ListItem, bool) {
|
||||
item := gui.getSelectedLocalCommit()
|
||||
@@ -174,6 +180,7 @@ func (gui *Gui) branchCommitsListContext() *ListContext {
|
||||
}
|
||||
|
||||
func (gui *Gui) reflogCommitsListContext() *ListContext {
|
||||
parseEmoji := gui.Config.GetUserConfig().Git.ParseEmoji
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
ViewName: "commits",
|
||||
@@ -187,7 +194,13 @@ func (gui *Gui) reflogCommitsListContext() *ListContext {
|
||||
Gui: gui,
|
||||
ResetMainViewOriginOnFocus: true,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
return presentation.GetReflogCommitListDisplayStrings(gui.State.FilteredReflogCommits, gui.State.ScreenMode != SCREEN_NORMAL, gui.cherryPickedCommitShaMap(), gui.State.Modes.Diffing.Ref)
|
||||
return presentation.GetReflogCommitListDisplayStrings(
|
||||
gui.State.FilteredReflogCommits,
|
||||
gui.State.ScreenMode != SCREEN_NORMAL,
|
||||
gui.cherryPickedCommitShaMap(),
|
||||
gui.State.Modes.Diffing.Ref,
|
||||
parseEmoji,
|
||||
)
|
||||
},
|
||||
SelectedItem: func() (ListItem, bool) {
|
||||
item := gui.getSelectedReflogCommit()
|
||||
@@ -197,6 +210,7 @@ func (gui *Gui) reflogCommitsListContext() *ListContext {
|
||||
}
|
||||
|
||||
func (gui *Gui) subCommitsListContext() *ListContext {
|
||||
parseEmoji := gui.Config.GetUserConfig().Git.ParseEmoji
|
||||
return &ListContext{
|
||||
BasicContext: &BasicContext{
|
||||
ViewName: "branches",
|
||||
@@ -210,7 +224,13 @@ func (gui *Gui) subCommitsListContext() *ListContext {
|
||||
Gui: gui,
|
||||
ResetMainViewOriginOnFocus: true,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
return presentation.GetCommitListDisplayStrings(gui.State.SubCommits, gui.State.ScreenMode != SCREEN_NORMAL, gui.cherryPickedCommitShaMap(), gui.State.Modes.Diffing.Ref)
|
||||
return presentation.GetCommitListDisplayStrings(
|
||||
gui.State.SubCommits,
|
||||
gui.State.ScreenMode != SCREEN_NORMAL,
|
||||
gui.cherryPickedCommitShaMap(),
|
||||
gui.State.Modes.Diffing.Ref,
|
||||
parseEmoji,
|
||||
)
|
||||
},
|
||||
SelectedItem: func() (ListItem, bool) {
|
||||
item := gui.getSelectedSubCommit()
|
||||
@@ -257,7 +277,7 @@ func (gui *Gui) commitFilesListContext() *ListContext {
|
||||
ResetMainViewOriginOnFocus: true,
|
||||
GetDisplayStrings: func() [][]string {
|
||||
if gui.State.CommitFileManager.GetItemsLength() == 0 {
|
||||
return [][]string{{utils.ColoredString("(none)", color.FgRed)}}
|
||||
return [][]string{{style.FgRed.Sprint("(none)")}}
|
||||
}
|
||||
|
||||
lines := gui.State.CommitFileManager.Render(gui.State.Modes.Diffing.Ref, gui.GitCommand.PatchManager)
|
||||
|
||||
60
pkg/gui/mergeconflicts/find_conflicts.go
Normal file
60
pkg/gui/mergeconflicts/find_conflicts.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package mergeconflicts
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// LineType tells us whether a given line is a start/middle/end marker of a conflict,
|
||||
// or if it's not a marker at all
|
||||
type LineType int
|
||||
|
||||
const (
|
||||
START LineType = iota
|
||||
MIDDLE
|
||||
END
|
||||
NOT_A_MARKER
|
||||
)
|
||||
|
||||
func findConflicts(content string) []*mergeConflict {
|
||||
conflicts := make([]*mergeConflict, 0)
|
||||
|
||||
if content == "" {
|
||||
return conflicts
|
||||
}
|
||||
|
||||
var newConflict *mergeConflict
|
||||
for i, line := range utils.SplitLines(content) {
|
||||
switch determineLineType(line) {
|
||||
case START:
|
||||
newConflict = &mergeConflict{start: i}
|
||||
case MIDDLE:
|
||||
newConflict.middle = i
|
||||
case END:
|
||||
newConflict.end = i
|
||||
conflicts = append(conflicts, newConflict)
|
||||
// reset value to avoid any possible silent mutations in further iterations
|
||||
newConflict = nil
|
||||
default:
|
||||
// line isn't a merge conflict marker so we just continue
|
||||
}
|
||||
}
|
||||
|
||||
return conflicts
|
||||
}
|
||||
|
||||
func determineLineType(line string) LineType {
|
||||
trimmedLine := strings.TrimPrefix(line, "++")
|
||||
|
||||
switch {
|
||||
case strings.HasPrefix(trimmedLine, "<<<<<<< "):
|
||||
return START
|
||||
case trimmedLine == "=======":
|
||||
return MIDDLE
|
||||
case strings.HasPrefix(trimmedLine, ">>>>>>> "):
|
||||
return END
|
||||
default:
|
||||
return NOT_A_MARKER
|
||||
}
|
||||
}
|
||||
57
pkg/gui/mergeconflicts/find_conflicts_test.go
Normal file
57
pkg/gui/mergeconflicts/find_conflicts_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package mergeconflicts
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDetermineLineType(t *testing.T) {
|
||||
type scenario struct {
|
||||
line string
|
||||
expected LineType
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
line: "",
|
||||
expected: NOT_A_MARKER,
|
||||
},
|
||||
{
|
||||
line: "blah",
|
||||
expected: NOT_A_MARKER,
|
||||
},
|
||||
{
|
||||
line: "<<<<<<< HEAD",
|
||||
expected: START,
|
||||
},
|
||||
{
|
||||
line: "<<<<<<< HEAD:my_branch",
|
||||
expected: START,
|
||||
},
|
||||
{
|
||||
line: "<<<<<<< MERGE_HEAD:my_branch",
|
||||
expected: START,
|
||||
},
|
||||
{
|
||||
line: "<<<<<<< Updated upstream:my_branch",
|
||||
expected: START,
|
||||
},
|
||||
{
|
||||
line: "<<<<<<< ours:my_branch",
|
||||
expected: START,
|
||||
},
|
||||
{
|
||||
line: "=======",
|
||||
expected: MIDDLE,
|
||||
},
|
||||
{
|
||||
line: ">>>>>>> blah",
|
||||
expected: END,
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
assert.EqualValues(t, s.expected, determineLineType(s.line))
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ package mergeconflicts
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
@@ -15,19 +15,18 @@ func ColoredConflictFile(content string, state *State, hasFocus bool) string {
|
||||
conflict, remainingConflicts := shiftConflict(state.conflicts)
|
||||
var outputBuffer bytes.Buffer
|
||||
for i, line := range utils.SplitLines(content) {
|
||||
colourAttr := theme.DefaultTextColor
|
||||
textStyle := theme.DefaultTextColor
|
||||
if i == conflict.start || i == conflict.middle || i == conflict.end {
|
||||
colourAttr = color.FgRed
|
||||
textStyle = style.FgRed
|
||||
}
|
||||
colour := color.New(colourAttr)
|
||||
|
||||
if hasFocus && state.conflictIndex < len(state.conflicts) && *state.conflicts[state.conflictIndex] == *conflict && shouldHighlightLine(i, conflict, state.conflictTop) {
|
||||
colour.Add(color.Bold)
|
||||
colour.Add(theme.SelectedRangeBgColor)
|
||||
textStyle = textStyle.MergeStyle(theme.SelectedRangeBgColor).SetBold()
|
||||
}
|
||||
if i == conflict.end && len(remainingConflicts) > 0 {
|
||||
conflict, remainingConflicts = shiftConflict(remainingConflicts)
|
||||
}
|
||||
outputBuffer.WriteString(utils.ColoredStringDirect(line, colour) + "\n")
|
||||
outputBuffer.WriteString(textStyle.Sprint(line) + "\n")
|
||||
}
|
||||
return outputBuffer.String()
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package mergeconflicts
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/golang-collections/collections/stack"
|
||||
@@ -19,7 +16,7 @@ const (
|
||||
)
|
||||
|
||||
// mergeConflict : A git conflict with a start middle and end corresponding to line
|
||||
// numbers in the file where the conflict bars appear
|
||||
// numbers in the file where the conflict markers appear
|
||||
type mergeConflict struct {
|
||||
start int
|
||||
middle int
|
||||
@@ -88,32 +85,6 @@ func (s *State) SetConflictsFromCat(cat string) {
|
||||
s.setConflicts(findConflicts(cat))
|
||||
}
|
||||
|
||||
func findConflicts(content string) []*mergeConflict {
|
||||
conflicts := make([]*mergeConflict, 0)
|
||||
|
||||
if content == "" {
|
||||
return conflicts
|
||||
}
|
||||
|
||||
var newConflict *mergeConflict
|
||||
for i, line := range utils.SplitLines(content) {
|
||||
trimmedLine := strings.TrimPrefix(line, "++")
|
||||
switch trimmedLine {
|
||||
case "<<<<<<< HEAD", "<<<<<<< MERGE_HEAD", "<<<<<<< Updated upstream", "<<<<<<< ours":
|
||||
newConflict = &mergeConflict{start: i}
|
||||
case "=======":
|
||||
newConflict.middle = i
|
||||
default:
|
||||
if strings.HasPrefix(trimmedLine, ">>>>>>> ") {
|
||||
newConflict.end = i
|
||||
conflicts = append(conflicts, newConflict)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return conflicts
|
||||
}
|
||||
|
||||
func (s *State) setConflicts(conflicts []*mergeConflict) {
|
||||
s.conflicts = conflicts
|
||||
|
||||
@@ -154,32 +125,29 @@ func (s *State) ContentAfterConflictResolve(path string, selection Selection) (b
|
||||
return false, "", nil
|
||||
}
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
reader := bufio.NewReader(file)
|
||||
content := ""
|
||||
for i := 0; true; i++ {
|
||||
line, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
err := utils.ForEachLineInFile(path, func(line string, i int) {
|
||||
if !isIndexToDelete(i, conflict, selection) {
|
||||
content += line
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
return true, content, nil
|
||||
}
|
||||
|
||||
func isIndexToDelete(i int, conflict *mergeConflict, selection Selection) bool {
|
||||
return i == conflict.middle ||
|
||||
i == conflict.start ||
|
||||
i == conflict.end ||
|
||||
selection != BOTH &&
|
||||
(selection == BOTTOM && i > conflict.start && i < conflict.middle) ||
|
||||
(selection == TOP && i > conflict.middle && i < conflict.end)
|
||||
isMarkerLine :=
|
||||
i == conflict.middle ||
|
||||
i == conflict.start ||
|
||||
i == conflict.end
|
||||
|
||||
isUnwantedContent :=
|
||||
(selection == BOTTOM && conflict.start < i && i < conflict.middle) ||
|
||||
(selection == TOP && conflict.middle < i && i < conflict.end)
|
||||
|
||||
return isMarkerLine || isUnwantedContent
|
||||
}
|
||||
|
||||
103
pkg/gui/mergeconflicts/state_test.go
Normal file
103
pkg/gui/mergeconflicts/state_test.go
Normal file
@@ -0,0 +1,103 @@
|
||||
package mergeconflicts
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFindConflicts(t *testing.T) {
|
||||
type scenario struct {
|
||||
name string
|
||||
content string
|
||||
expected []*mergeConflict
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
name: "empty",
|
||||
content: "",
|
||||
expected: []*mergeConflict{},
|
||||
},
|
||||
{
|
||||
name: "various conflicts",
|
||||
content: `++<<<<<<< HEAD
|
||||
foo
|
||||
++=======
|
||||
bar
|
||||
++>>>>>>> branch
|
||||
|
||||
<<<<<<< HEAD: foo/bar/baz.go
|
||||
foo
|
||||
bar
|
||||
=======
|
||||
baz
|
||||
>>>>>>> branch
|
||||
|
||||
++<<<<<<< MERGE_HEAD
|
||||
foo
|
||||
++=======
|
||||
bar
|
||||
++>>>>>>> branch
|
||||
|
||||
++<<<<<<< Updated upstream
|
||||
foo
|
||||
++=======
|
||||
bar
|
||||
++>>>>>>> branch
|
||||
|
||||
++<<<<<<< ours
|
||||
foo
|
||||
++=======
|
||||
bar
|
||||
++>>>>>>> branch
|
||||
|
||||
<<<<<<< Updated upstream: foo/bar/baz.go
|
||||
foo
|
||||
bar
|
||||
=======
|
||||
baz
|
||||
>>>>>>> branch
|
||||
`,
|
||||
expected: []*mergeConflict{
|
||||
{
|
||||
start: 0,
|
||||
middle: 2,
|
||||
end: 4,
|
||||
},
|
||||
{
|
||||
start: 6,
|
||||
middle: 9,
|
||||
end: 11,
|
||||
},
|
||||
{
|
||||
start: 13,
|
||||
middle: 15,
|
||||
end: 17,
|
||||
},
|
||||
{
|
||||
start: 19,
|
||||
middle: 21,
|
||||
end: 23,
|
||||
},
|
||||
{
|
||||
start: 25,
|
||||
middle: 27,
|
||||
end: 29,
|
||||
},
|
||||
{
|
||||
start: 31,
|
||||
middle: 34,
|
||||
end: 36,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
assert.EqualValues(t, s.expected, findConflicts(s.content))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
)
|
||||
|
||||
type modeStatus struct {
|
||||
@@ -18,9 +15,11 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
{
|
||||
isActive: gui.State.Modes.Diffing.Active,
|
||||
description: func() string {
|
||||
return utils.ColoredString(
|
||||
fmt.Sprintf("%s %s %s", gui.Tr.LcShowingGitDiff, "git diff "+gui.diffStr(), utils.ColoredString(gui.Tr.ResetInParentheses, color.Underline)),
|
||||
color.FgMagenta,
|
||||
return style.FgMagenta.Sprintf(
|
||||
"%s %s %s",
|
||||
gui.Tr.LcShowingGitDiff,
|
||||
"git diff "+gui.diffStr(),
|
||||
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
|
||||
)
|
||||
},
|
||||
reset: gui.exitDiffMode,
|
||||
@@ -28,10 +27,10 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
{
|
||||
isActive: gui.GitCommand.PatchManager.Active,
|
||||
description: func() string {
|
||||
return utils.ColoredString(
|
||||
fmt.Sprintf("%s %s", gui.Tr.LcBuildingPatch, utils.ColoredString(gui.Tr.ResetInParentheses, color.Underline)),
|
||||
color.FgYellow,
|
||||
color.Bold,
|
||||
return style.FgYellow.SetBold().Sprintf(
|
||||
"%s %s",
|
||||
gui.Tr.LcBuildingPatch,
|
||||
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
|
||||
)
|
||||
},
|
||||
reset: gui.handleResetPatch,
|
||||
@@ -39,10 +38,11 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
{
|
||||
isActive: gui.State.Modes.Filtering.Active,
|
||||
description: func() string {
|
||||
return utils.ColoredString(
|
||||
fmt.Sprintf("%s '%s' %s", gui.Tr.LcFilteringBy, gui.State.Modes.Filtering.GetPath(), utils.ColoredString(gui.Tr.ResetInParentheses, color.Underline)),
|
||||
color.FgRed,
|
||||
color.Bold,
|
||||
return style.FgRed.SetBold().Sprintf(
|
||||
"%s '%s' %s",
|
||||
gui.Tr.LcFilteringBy,
|
||||
gui.State.Modes.Filtering.GetPath(),
|
||||
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
|
||||
)
|
||||
},
|
||||
reset: gui.exitFilterMode,
|
||||
@@ -50,9 +50,10 @@ func (gui *Gui) modeStatuses() []modeStatus {
|
||||
{
|
||||
isActive: gui.State.Modes.CherryPicking.Active,
|
||||
description: func() string {
|
||||
return utils.ColoredString(
|
||||
fmt.Sprintf("%d commits copied %s", len(gui.State.Modes.CherryPicking.CherryPickedCommits), utils.ColoredString(gui.Tr.ResetInParentheses, color.Underline)),
|
||||
color.FgCyan,
|
||||
return style.FgCyan.Sprintf(
|
||||
"%d commits copied %s",
|
||||
len(gui.State.Modes.CherryPicking.CherryPickedCommits),
|
||||
style.AttrUnderline.Sprint(gui.Tr.ResetInParentheses),
|
||||
)
|
||||
},
|
||||
reset: gui.exitCherryPickingMode,
|
||||
|
||||
23
pkg/gui/modes/cherrypicking/cherry_picking.go
Normal file
23
pkg/gui/modes/cherrypicking/cherry_picking.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package cherrypicking
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
)
|
||||
|
||||
type CherryPicking struct {
|
||||
CherryPickedCommits []*models.Commit
|
||||
|
||||
// we only allow cherry picking from one context at a time, so you can't copy a commit from the local commits context and then also copy a commit in the reflog context
|
||||
ContextKey string
|
||||
}
|
||||
|
||||
func New() CherryPicking {
|
||||
return CherryPicking{
|
||||
CherryPickedCommits: make([]*models.Commit, 0),
|
||||
ContextKey: "",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *CherryPicking) Active() bool {
|
||||
return len(m.CherryPickedCommits) > 0
|
||||
}
|
||||
15
pkg/gui/modes/diffing/diffing.go
Normal file
15
pkg/gui/modes/diffing/diffing.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package diffing
|
||||
|
||||
// if ref is blank we're not diffing anything
|
||||
type Diffing struct {
|
||||
Ref string
|
||||
Reverse bool
|
||||
}
|
||||
|
||||
func New() Diffing {
|
||||
return Diffing{}
|
||||
}
|
||||
|
||||
func (m *Diffing) Active() bool {
|
||||
return m.Ref != ""
|
||||
}
|
||||
@@ -4,7 +4,7 @@ type Filtering struct {
|
||||
path string // the filename that gets passed to git log
|
||||
}
|
||||
|
||||
func NewFiltering(path string) Filtering {
|
||||
func New(path string) Filtering {
|
||||
return Filtering{path: path}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ package gui
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@@ -35,14 +35,11 @@ func (gui *Gui) getBindings(v *gocui.View) []*Binding {
|
||||
}
|
||||
|
||||
func (gui *Gui) displayDescription(binding *Binding) string {
|
||||
commandColor := color.New(color.FgCyan)
|
||||
menuColor := color.New(color.FgMagenta)
|
||||
|
||||
if binding.OpensMenu {
|
||||
return menuColor.Sprintf("%s...", binding.Description)
|
||||
return style.FgMagenta.Sprintf("%s...", binding.Description)
|
||||
}
|
||||
|
||||
return commandColor.Sprint(binding.Description)
|
||||
return style.FgCyan.Sprint(binding.Description)
|
||||
}
|
||||
|
||||
func (gui *Gui) handleCreateOptionsMenu() error {
|
||||
|
||||
@@ -4,10 +4,9 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func GetBranchListDisplayStrings(branches []*models.Branch, fullDescription bool, diffName string) [][]string {
|
||||
@@ -28,44 +27,54 @@ func getBranchDisplayStrings(b *models.Branch, fullDescription bool, diffed bool
|
||||
displayName = b.DisplayName
|
||||
}
|
||||
|
||||
nameColorAttr := GetBranchColor(b.Name)
|
||||
nameTextStyle := GetBranchTextStyle(b.Name)
|
||||
if diffed {
|
||||
nameColorAttr = theme.DiffTerminalColor
|
||||
nameTextStyle = theme.DiffTerminalColor
|
||||
}
|
||||
coloredName := utils.ColoredString(displayName, nameColorAttr)
|
||||
if b.Pushables != "" && b.Pullables != "" && b.Pushables != "?" && b.Pullables != "?" {
|
||||
trackColor := color.FgYellow
|
||||
if b.Pushables == "0" && b.Pullables == "0" {
|
||||
trackColor = color.FgGreen
|
||||
}
|
||||
track := utils.ColoredString(fmt.Sprintf("↑%s↓%s", b.Pushables, b.Pullables), trackColor)
|
||||
coloredName = fmt.Sprintf("%s %s", coloredName, track)
|
||||
coloredName := nameTextStyle.Sprint(displayName)
|
||||
if b.IsTrackingRemote() {
|
||||
coloredName = fmt.Sprintf("%s %s", coloredName, ColoredBranchStatus(b))
|
||||
}
|
||||
|
||||
recencyColor := color.FgCyan
|
||||
recencyColor := style.FgCyan
|
||||
if b.Recency == " *" {
|
||||
recencyColor = color.FgGreen
|
||||
recencyColor = style.FgGreen
|
||||
}
|
||||
|
||||
res := []string{recencyColor.Sprint(b.Recency), coloredName}
|
||||
if fullDescription {
|
||||
return []string{utils.ColoredString(b.Recency, recencyColor), coloredName, utils.ColoredString(b.UpstreamName, color.FgYellow)}
|
||||
return append(res, style.FgYellow.Sprint(b.UpstreamName))
|
||||
}
|
||||
|
||||
return []string{utils.ColoredString(b.Recency, recencyColor), coloredName}
|
||||
return res
|
||||
}
|
||||
|
||||
// GetBranchColor branch color
|
||||
func GetBranchColor(name string) color.Attribute {
|
||||
// GetBranchTextStyle branch color
|
||||
func GetBranchTextStyle(name string) style.TextStyle {
|
||||
branchType := strings.Split(name, "/")[0]
|
||||
|
||||
switch branchType {
|
||||
case "feature":
|
||||
return color.FgGreen
|
||||
return style.FgGreen
|
||||
case "bugfix":
|
||||
return color.FgYellow
|
||||
return style.FgYellow
|
||||
case "hotfix":
|
||||
return color.FgRed
|
||||
return style.FgRed
|
||||
default:
|
||||
return theme.DefaultTextColor
|
||||
}
|
||||
}
|
||||
|
||||
func ColoredBranchStatus(branch *models.Branch) string {
|
||||
colour := style.FgYellow
|
||||
if branch.MatchesUpstream() {
|
||||
colour = style.FgGreen
|
||||
} else if !branch.IsTrackingRemote() {
|
||||
colour = style.FgRed
|
||||
}
|
||||
|
||||
return colour.Sprint(BranchStatus(branch))
|
||||
}
|
||||
|
||||
func BranchStatus(branch *models.Branch) string {
|
||||
return fmt.Sprintf("↑%s↓%s", branch.Pushables, branch.Pullables)
|
||||
}
|
||||
|
||||
@@ -1,52 +1,46 @@
|
||||
package presentation
|
||||
|
||||
import (
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/patch"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func GetCommitFileLine(name string, diffName string, commitFile *models.CommitFile, status patch.PatchStatus) string {
|
||||
yellow := color.New(color.FgYellow)
|
||||
green := color.New(color.FgGreen)
|
||||
defaultColor := color.New(theme.DefaultTextColor)
|
||||
diffTerminalColor := color.New(theme.DiffTerminalColor)
|
||||
|
||||
colour := defaultColor
|
||||
colour := theme.DefaultTextColor
|
||||
if diffName == name {
|
||||
colour = diffTerminalColor
|
||||
colour = theme.DiffTerminalColor
|
||||
} else {
|
||||
switch status {
|
||||
case patch.UNSELECTED:
|
||||
colour = defaultColor
|
||||
case patch.WHOLE:
|
||||
colour = green
|
||||
colour = style.FgGreen
|
||||
case patch.PART:
|
||||
colour = yellow
|
||||
colour = style.FgYellow
|
||||
}
|
||||
}
|
||||
|
||||
name = utils.EscapeSpecialChars(name)
|
||||
if commitFile == nil {
|
||||
return colour.Sprint(name)
|
||||
}
|
||||
|
||||
return utils.ColoredString(commitFile.ChangeStatus, getColorForChangeStatus(commitFile.ChangeStatus)) + " " + colour.Sprint(name)
|
||||
return getColorForChangeStatus(commitFile.ChangeStatus).Sprint(commitFile.ChangeStatus) + " " + colour.Sprint(name)
|
||||
}
|
||||
|
||||
func getColorForChangeStatus(changeStatus string) color.Attribute {
|
||||
func getColorForChangeStatus(changeStatus string) style.TextStyle {
|
||||
switch changeStatus {
|
||||
case "A":
|
||||
return color.FgGreen
|
||||
return style.FgGreen
|
||||
case "M", "R":
|
||||
return color.FgYellow
|
||||
return style.FgYellow
|
||||
case "D":
|
||||
return color.FgRed
|
||||
return style.FgRed
|
||||
case "C":
|
||||
return color.FgCyan
|
||||
return style.FgCyan
|
||||
case "T":
|
||||
return color.FgMagenta
|
||||
return style.FgMagenta
|
||||
default:
|
||||
return theme.DefaultTextColor
|
||||
}
|
||||
|
||||
@@ -3,16 +3,19 @@ package presentation
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/kyokomi/emoji/v2"
|
||||
)
|
||||
|
||||
func GetCommitListDisplayStrings(commits []*models.Commit, fullDescription bool, cherryPickedCommitShaMap map[string]bool, diffName string) [][]string {
|
||||
var cherryPickedCommitTextStyle = style.FgCyan.MergeStyle(style.BgBlue)
|
||||
|
||||
func GetCommitListDisplayStrings(commits []*models.Commit, fullDescription bool, cherryPickedCommitShaMap map[string]bool, diffName string, parseEmoji bool) [][]string {
|
||||
lines := make([][]string, len(commits))
|
||||
|
||||
var displayFunc func(*models.Commit, map[string]bool, bool) []string
|
||||
var displayFunc func(*models.Commit, map[string]bool, bool, bool) []string
|
||||
if fullDescription {
|
||||
displayFunc = getFullDescriptionDisplayStringsForCommit
|
||||
} else {
|
||||
@@ -21,119 +24,113 @@ func GetCommitListDisplayStrings(commits []*models.Commit, fullDescription bool,
|
||||
|
||||
for i := range commits {
|
||||
diffed := commits[i].Sha == diffName
|
||||
lines[i] = displayFunc(commits[i], cherryPickedCommitShaMap, diffed)
|
||||
lines[i] = displayFunc(commits[i], cherryPickedCommitShaMap, diffed, parseEmoji)
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func getFullDescriptionDisplayStringsForCommit(c *models.Commit, cherryPickedCommitShaMap map[string]bool, diffed bool) []string {
|
||||
red := color.New(color.FgRed)
|
||||
yellow := color.New(color.FgYellow)
|
||||
green := color.New(color.FgGreen)
|
||||
blue := color.New(color.FgBlue)
|
||||
defaultColor := color.New(theme.DefaultTextColor)
|
||||
diffedColor := color.New(theme.DiffTerminalColor)
|
||||
|
||||
// for some reason, setting the background to blue pads out the other commits
|
||||
// horizontally. For the sake of accessibility I'm considering this a feature,
|
||||
// not a bug
|
||||
copied := color.New(color.FgCyan, color.BgBlue)
|
||||
|
||||
var shaColor *color.Color
|
||||
func getFullDescriptionDisplayStringsForCommit(c *models.Commit, cherryPickedCommitShaMap map[string]bool, diffed, parseEmoji bool) []string {
|
||||
shaColor := theme.DefaultTextColor
|
||||
switch c.Status {
|
||||
case "unpushed":
|
||||
shaColor = red
|
||||
shaColor = style.FgRed
|
||||
case "pushed":
|
||||
shaColor = yellow
|
||||
shaColor = style.FgYellow
|
||||
case "merged":
|
||||
shaColor = green
|
||||
shaColor = style.FgGreen
|
||||
case "rebasing":
|
||||
shaColor = blue
|
||||
shaColor = style.FgBlue
|
||||
case "reflog":
|
||||
shaColor = blue
|
||||
default:
|
||||
shaColor = defaultColor
|
||||
shaColor = style.FgBlue
|
||||
}
|
||||
|
||||
if diffed {
|
||||
shaColor = diffedColor
|
||||
shaColor = theme.DiffTerminalColor
|
||||
} else if cherryPickedCommitShaMap[c.Sha] {
|
||||
shaColor = copied
|
||||
// for some reason, setting the background to blue pads out the other commits
|
||||
// horizontally. For the sake of accessibility I'm considering this a feature,
|
||||
// not a bug
|
||||
shaColor = cherryPickedCommitTextStyle
|
||||
}
|
||||
|
||||
tagString := ""
|
||||
secondColumnString := blue.Sprint(utils.UnixToDate(c.UnixTimestamp))
|
||||
secondColumnString := style.FgBlue.Sprint(utils.UnixToDate(c.UnixTimestamp))
|
||||
if c.Action != "" {
|
||||
secondColumnString = color.New(actionColorMap(c.Action)).Sprint(c.Action)
|
||||
secondColumnString = actionColorMap(c.Action).Sprint(c.Action)
|
||||
} else if c.ExtraInfo != "" {
|
||||
tagColor := color.New(color.FgMagenta, color.Bold)
|
||||
tagString = utils.ColoredStringDirect(c.ExtraInfo, tagColor) + " "
|
||||
tagString = style.FgMagenta.SetBold().Sprint(c.ExtraInfo) + " "
|
||||
}
|
||||
|
||||
truncatedAuthor := utils.TruncateWithEllipsis(c.Author, 17)
|
||||
|
||||
return []string{shaColor.Sprint(c.ShortSha()), secondColumnString, yellow.Sprint(truncatedAuthor), tagString + defaultColor.Sprint(c.Name)}
|
||||
name := c.Name
|
||||
if parseEmoji {
|
||||
name = emoji.Sprint(name)
|
||||
}
|
||||
|
||||
return []string{
|
||||
shaColor.Sprint(c.ShortSha()),
|
||||
secondColumnString,
|
||||
style.FgYellow.Sprint(truncatedAuthor),
|
||||
tagString + theme.DefaultTextColor.Sprint(name),
|
||||
}
|
||||
}
|
||||
|
||||
func getDisplayStringsForCommit(c *models.Commit, cherryPickedCommitShaMap map[string]bool, diffed bool) []string {
|
||||
red := color.New(color.FgRed)
|
||||
yellow := color.New(color.FgYellow)
|
||||
green := color.New(color.FgGreen)
|
||||
blue := color.New(color.FgBlue)
|
||||
defaultColor := color.New(theme.DefaultTextColor)
|
||||
diffedColor := color.New(theme.DiffTerminalColor)
|
||||
|
||||
// for some reason, setting the background to blue pads out the other commits
|
||||
// horizontally. For the sake of accessibility I'm considering this a feature,
|
||||
// not a bug
|
||||
copied := color.New(color.FgCyan, color.BgBlue)
|
||||
|
||||
var shaColor *color.Color
|
||||
func getDisplayStringsForCommit(c *models.Commit, cherryPickedCommitShaMap map[string]bool, diffed, parseEmoji bool) []string {
|
||||
shaColor := theme.DefaultTextColor
|
||||
switch c.Status {
|
||||
case "unpushed":
|
||||
shaColor = red
|
||||
shaColor = style.FgRed
|
||||
case "pushed":
|
||||
shaColor = yellow
|
||||
shaColor = style.FgYellow
|
||||
case "merged":
|
||||
shaColor = green
|
||||
shaColor = style.FgGreen
|
||||
case "rebasing":
|
||||
shaColor = blue
|
||||
shaColor = style.FgBlue
|
||||
case "reflog":
|
||||
shaColor = blue
|
||||
default:
|
||||
shaColor = defaultColor
|
||||
shaColor = style.FgBlue
|
||||
}
|
||||
|
||||
if diffed {
|
||||
shaColor = diffedColor
|
||||
shaColor = theme.DiffTerminalColor
|
||||
} else if cherryPickedCommitShaMap[c.Sha] {
|
||||
shaColor = copied
|
||||
// for some reason, setting the background to blue pads out the other commits
|
||||
// horizontally. For the sake of accessibility I'm considering this a feature,
|
||||
// not a bug
|
||||
shaColor = cherryPickedCommitTextStyle
|
||||
}
|
||||
|
||||
actionString := ""
|
||||
tagString := ""
|
||||
if c.Action != "" {
|
||||
actionString = color.New(actionColorMap(c.Action)).Sprint(utils.WithPadding(c.Action, 7)) + " "
|
||||
actionString = actionColorMap(c.Action).Sprint(utils.WithPadding(c.Action, 7)) + " "
|
||||
} else if len(c.Tags) > 0 {
|
||||
tagColor := color.New(color.FgMagenta, color.Bold)
|
||||
tagString = utils.ColoredStringDirect(strings.Join(c.Tags, " "), tagColor) + " "
|
||||
tagString = theme.DiffTerminalColor.SetBold().Sprint(strings.Join(c.Tags, " ")) + " "
|
||||
}
|
||||
|
||||
return []string{shaColor.Sprint(c.ShortSha()), actionString + tagString + defaultColor.Sprint(c.Name)}
|
||||
name := c.Name
|
||||
if parseEmoji {
|
||||
name = emoji.Sprint(name)
|
||||
}
|
||||
|
||||
return []string{
|
||||
shaColor.Sprint(c.ShortSha()),
|
||||
actionString + tagString + theme.DefaultTextColor.Sprint(name),
|
||||
}
|
||||
}
|
||||
|
||||
func actionColorMap(str string) color.Attribute {
|
||||
func actionColorMap(str string) style.TextStyle {
|
||||
switch str {
|
||||
case "pick":
|
||||
return color.FgCyan
|
||||
return style.FgCyan
|
||||
case "drop":
|
||||
return color.FgRed
|
||||
return style.FgRed
|
||||
case "edit":
|
||||
return color.FgGreen
|
||||
return style.FgGreen
|
||||
case "fixup":
|
||||
return color.FgMagenta
|
||||
return style.FgMagenta
|
||||
default:
|
||||
return color.FgYellow
|
||||
return style.FgYellow
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package presentation
|
||||
|
||||
import (
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
@@ -10,35 +10,30 @@ import (
|
||||
func GetFileLine(hasUnstagedChanges bool, hasStagedChanges bool, name string, diffName string, submoduleConfigs []*models.SubmoduleConfig, file *models.File) string {
|
||||
// potentially inefficient to be instantiating these color
|
||||
// objects with each render
|
||||
red := color.New(color.FgRed)
|
||||
green := color.New(color.FgGreen)
|
||||
diffColor := color.New(theme.DiffTerminalColor)
|
||||
partiallyModifiedColor := color.New(color.FgYellow)
|
||||
partiallyModifiedColor := style.FgYellow
|
||||
|
||||
var restColor *color.Color
|
||||
restColor := style.FgGreen
|
||||
if name == diffName {
|
||||
restColor = diffColor
|
||||
restColor = theme.DiffTerminalColor
|
||||
} else if file == nil && hasStagedChanges && hasUnstagedChanges {
|
||||
restColor = partiallyModifiedColor
|
||||
} else if hasUnstagedChanges {
|
||||
restColor = red
|
||||
} else {
|
||||
restColor = green
|
||||
restColor = style.FgRed
|
||||
}
|
||||
|
||||
output := ""
|
||||
if file != nil {
|
||||
// this is just making things look nice when the background attribute is 'reverse'
|
||||
firstChar := file.ShortStatus[0:1]
|
||||
firstCharCl := green
|
||||
firstCharCl := style.FgGreen
|
||||
if firstChar == "?" {
|
||||
firstCharCl = red
|
||||
firstCharCl = style.FgRed
|
||||
} else if firstChar == " " {
|
||||
firstCharCl = restColor
|
||||
}
|
||||
|
||||
secondChar := file.ShortStatus[1:2]
|
||||
secondCharCl := red
|
||||
secondCharCl := style.FgRed
|
||||
if secondChar == " " {
|
||||
secondCharCl = restColor
|
||||
}
|
||||
@@ -48,10 +43,10 @@ func GetFileLine(hasUnstagedChanges bool, hasStagedChanges bool, name string, di
|
||||
output += restColor.Sprint(" ")
|
||||
}
|
||||
|
||||
output += restColor.Sprint(name)
|
||||
output += restColor.Sprint(utils.EscapeSpecialChars(name))
|
||||
|
||||
if file != nil && file.IsSubmodule(submoduleConfigs) {
|
||||
output += utils.ColoredString(" (submodule)", theme.DefaultTextColor)
|
||||
output += theme.DefaultTextColor.Sprint(" (submodule)")
|
||||
}
|
||||
|
||||
return output
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
package presentation
|
||||
|
||||
import (
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/kyokomi/emoji/v2"
|
||||
)
|
||||
|
||||
func GetReflogCommitListDisplayStrings(commits []*models.Commit, fullDescription bool, cherryPickedCommitShaMap map[string]bool, diffName string) [][]string {
|
||||
func GetReflogCommitListDisplayStrings(commits []*models.Commit, fullDescription bool, cherryPickedCommitShaMap map[string]bool, diffName string, parseEmoji bool) [][]string {
|
||||
lines := make([][]string, len(commits))
|
||||
|
||||
var displayFunc func(*models.Commit, map[string]bool, bool) []string
|
||||
var displayFunc func(*models.Commit, map[string]bool, bool, bool) []string
|
||||
if fullDescription {
|
||||
displayFunc = getFullDescriptionDisplayStringsForReflogCommit
|
||||
} else {
|
||||
@@ -19,41 +20,47 @@ func GetReflogCommitListDisplayStrings(commits []*models.Commit, fullDescription
|
||||
|
||||
for i := range commits {
|
||||
diffed := commits[i].Sha == diffName
|
||||
lines[i] = displayFunc(commits[i], cherryPickedCommitShaMap, diffed)
|
||||
lines[i] = displayFunc(commits[i], cherryPickedCommitShaMap, diffed, parseEmoji)
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func coloredReflogSha(c *models.Commit, cherryPickedCommitShaMap map[string]bool) string {
|
||||
var shaColor *color.Color
|
||||
shaColor := style.FgBlue
|
||||
if cherryPickedCommitShaMap[c.Sha] {
|
||||
shaColor = color.New(color.FgCyan, color.BgBlue)
|
||||
} else {
|
||||
shaColor = color.New(color.FgBlue)
|
||||
shaColor = cherryPickedCommitTextStyle
|
||||
}
|
||||
|
||||
return shaColor.Sprint(c.ShortSha())
|
||||
}
|
||||
|
||||
func getFullDescriptionDisplayStringsForReflogCommit(c *models.Commit, cherryPickedCommitShaMap map[string]bool, diffed bool) []string {
|
||||
func getFullDescriptionDisplayStringsForReflogCommit(c *models.Commit, cherryPickedCommitShaMap map[string]bool, diffed, parseEmoji bool) []string {
|
||||
colorAttr := theme.DefaultTextColor
|
||||
if diffed {
|
||||
colorAttr = theme.DiffTerminalColor
|
||||
}
|
||||
|
||||
return []string{
|
||||
coloredReflogSha(c, cherryPickedCommitShaMap),
|
||||
utils.ColoredString(utils.UnixToDate(c.UnixTimestamp), color.FgMagenta),
|
||||
utils.ColoredString(c.Name, colorAttr),
|
||||
name := c.Name
|
||||
if parseEmoji {
|
||||
name = emoji.Sprint(name)
|
||||
}
|
||||
}
|
||||
|
||||
func getDisplayStringsForReflogCommit(c *models.Commit, cherryPickedCommitShaMap map[string]bool, diffed bool) []string {
|
||||
defaultColor := color.New(theme.DefaultTextColor)
|
||||
|
||||
return []string{
|
||||
coloredReflogSha(c, cherryPickedCommitShaMap),
|
||||
defaultColor.Sprint(c.Name),
|
||||
style.FgMagenta.Sprint(utils.UnixToDate(c.UnixTimestamp)),
|
||||
colorAttr.Sprint(name),
|
||||
}
|
||||
}
|
||||
|
||||
func getDisplayStringsForReflogCommit(c *models.Commit, cherryPickedCommitShaMap map[string]bool, diffed, parseEmoji bool) []string {
|
||||
name := c.Name
|
||||
if parseEmoji {
|
||||
name = emoji.Sprint(name)
|
||||
}
|
||||
|
||||
return []string{
|
||||
coloredReflogSha(c, cherryPickedCommitShaMap),
|
||||
theme.DefaultTextColor.Sprint(name),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package presentation
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func GetRemoteBranchListDisplayStrings(branches []*models.RemoteBranch, diffName string) [][]string {
|
||||
@@ -19,12 +18,10 @@ func GetRemoteBranchListDisplayStrings(branches []*models.RemoteBranch, diffName
|
||||
|
||||
// getRemoteBranchDisplayStrings returns the display string of branch
|
||||
func getRemoteBranchDisplayStrings(b *models.RemoteBranch, diffed bool) []string {
|
||||
nameColorAttr := GetBranchColor(b.Name)
|
||||
textStyle := GetBranchTextStyle(b.Name)
|
||||
if diffed {
|
||||
nameColorAttr = theme.DiffTerminalColor
|
||||
textStyle = theme.DiffTerminalColor
|
||||
}
|
||||
|
||||
displayName := utils.ColoredString(b.Name, nameColorAttr)
|
||||
|
||||
return []string{displayName}
|
||||
return []string{textStyle.Sprint(b.Name)}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package presentation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func GetRemoteListDisplayStrings(remotes []*models.Remote, diffName string) [][]string {
|
||||
@@ -24,10 +21,10 @@ func GetRemoteListDisplayStrings(remotes []*models.Remote, diffName string) [][]
|
||||
func getRemoteDisplayStrings(r *models.Remote, diffed bool) []string {
|
||||
branchCount := len(r.Branches)
|
||||
|
||||
nameColorAttr := theme.DefaultTextColor
|
||||
textStyle := theme.DefaultTextColor
|
||||
if diffed {
|
||||
nameColorAttr = theme.DiffTerminalColor
|
||||
textStyle = theme.DiffTerminalColor
|
||||
}
|
||||
|
||||
return []string{utils.ColoredString(r.Name, nameColorAttr), utils.ColoredString(fmt.Sprintf("%d branches", branchCount), color.FgBlue)}
|
||||
return []string{textStyle.Sprint(r.Name), style.FgBlue.Sprintf("%d branches", branchCount)}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package presentation
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func GetStashEntryListDisplayStrings(stashEntries []*models.StashEntry, diffName string) [][]string {
|
||||
@@ -19,9 +18,9 @@ func GetStashEntryListDisplayStrings(stashEntries []*models.StashEntry, diffName
|
||||
|
||||
// getStashEntryDisplayStrings returns the display string of branch
|
||||
func getStashEntryDisplayStrings(s *models.StashEntry, diffed bool) []string {
|
||||
attr := theme.DefaultTextColor
|
||||
textStyle := theme.DefaultTextColor
|
||||
if diffed {
|
||||
attr = theme.DiffTerminalColor
|
||||
textStyle = theme.DiffTerminalColor
|
||||
}
|
||||
return []string{utils.ColoredString(s.Name, attr)}
|
||||
return []string{textStyle.Sprint(s.Name)}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package presentation
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func GetSubmoduleListDisplayStrings(submodules []*models.SubmoduleConfig) [][]string {
|
||||
@@ -17,5 +16,5 @@ func GetSubmoduleListDisplayStrings(submodules []*models.SubmoduleConfig) [][]st
|
||||
}
|
||||
|
||||
func getSubmoduleDisplayStrings(s *models.SubmoduleConfig) []string {
|
||||
return []string{utils.ColoredString(s.Name, theme.DefaultTextColor)}
|
||||
return []string{theme.DefaultTextColor.Sprint(s.Name)}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package presentation
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func GetTagListDisplayStrings(tags []*models.Tag, diffName string) [][]string {
|
||||
@@ -19,9 +18,9 @@ func GetTagListDisplayStrings(tags []*models.Tag, diffName string) [][]string {
|
||||
|
||||
// getTagDisplayStrings returns the display string of branch
|
||||
func getTagDisplayStrings(t *models.Tag, diffed bool) []string {
|
||||
attr := theme.DefaultTextColor
|
||||
textStyle := theme.DefaultTextColor
|
||||
if diffed {
|
||||
attr = theme.DiffTerminalColor
|
||||
textStyle = theme.DiffTerminalColor
|
||||
}
|
||||
return []string{utils.ColoredString(t.Name, attr)}
|
||||
return []string{textStyle.Sprint(t.Name)}
|
||||
}
|
||||
|
||||
67
pkg/gui/pull_request_menu_panel.go
Normal file
67
pkg/gui/pull_request_menu_panel.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package gui
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
)
|
||||
|
||||
func (gui *Gui) createPullRequestMenu(selectedBranch *models.Branch, checkedOutBranch *models.Branch) error {
|
||||
menuItems := make([]*menuItem, 0, 4)
|
||||
|
||||
fromToDisplayStrings := func(from string, to string) []string {
|
||||
return []string{fmt.Sprintf("%s → %s", from, to)}
|
||||
}
|
||||
|
||||
menuItemsForBranch := func(branch *models.Branch) []*menuItem {
|
||||
return []*menuItem{
|
||||
{
|
||||
displayStrings: fromToDisplayStrings(branch.Name, gui.Tr.LcDefaultBranch),
|
||||
onPress: func() error {
|
||||
return gui.createPullRequest(branch.Name, "")
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: fromToDisplayStrings(branch.Name, gui.Tr.LcSelectBranch),
|
||||
onPress: func() error {
|
||||
return gui.prompt(promptOpts{
|
||||
title: branch.Name + " →",
|
||||
findSuggestionsFunc: gui.findBranchNameSuggestions,
|
||||
handleConfirm: func(targetBranchName string) error {
|
||||
return gui.createPullRequest(branch.Name, targetBranchName)
|
||||
}},
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if selectedBranch != checkedOutBranch {
|
||||
menuItems = append(menuItems,
|
||||
&menuItem{
|
||||
displayStrings: fromToDisplayStrings(checkedOutBranch.Name, selectedBranch.Name),
|
||||
onPress: func() error {
|
||||
return gui.createPullRequest(checkedOutBranch.Name, selectedBranch.Name)
|
||||
},
|
||||
},
|
||||
)
|
||||
menuItems = append(menuItems, menuItemsForBranch(checkedOutBranch)...)
|
||||
}
|
||||
|
||||
menuItems = append(menuItems, menuItemsForBranch(selectedBranch)...)
|
||||
|
||||
return gui.createMenu(fmt.Sprintf(gui.Tr.CreatePullRequestOptions), menuItems, createMenuOptions{showCancel: true})
|
||||
}
|
||||
|
||||
func (gui *Gui) createPullRequest(from string, to string) error {
|
||||
pullRequest := commands.NewPullRequest(gui.GitCommand)
|
||||
url, err := pullRequest.Create(from, to)
|
||||
if err != nil {
|
||||
return gui.surfaceError(err)
|
||||
}
|
||||
gui.OnRunCommand(oscommands.NewCmdLogEntry(fmt.Sprintf(gui.Tr.CreatingPullRequestAtUrl, url), gui.Tr.CreatePullRequest, false))
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -4,17 +4,17 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/env"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func (gui *Gui) handleCreateRecentReposMenu() error {
|
||||
recentRepoPaths := gui.Config.GetAppState().RecentRepos
|
||||
reposCount := utils.Min(len(recentRepoPaths), 20)
|
||||
yellow := color.New(color.FgMagenta)
|
||||
|
||||
// we won't show the current repo hence the -1
|
||||
menuItems := make([]*menuItem, reposCount-1)
|
||||
for i, path := range recentRepoPaths[1:reposCount] {
|
||||
@@ -22,7 +22,7 @@ func (gui *Gui) handleCreateRecentReposMenu() error {
|
||||
menuItems[i] = &menuItem{
|
||||
displayStrings: []string{
|
||||
filepath.Base(path),
|
||||
yellow.Sprint(path),
|
||||
style.FgMagenta.Sprint(path),
|
||||
},
|
||||
onPress: func() error {
|
||||
// if we were in a submodule, we want to forget about that stack of repos
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@@ -26,7 +26,7 @@ func (gui *Gui) handleRemoteSelect() error {
|
||||
if remote == nil {
|
||||
task = NewRenderStringTask("No remotes")
|
||||
} else {
|
||||
task = NewRenderStringTask(fmt.Sprintf("%s\nUrls:\n%s", utils.ColoredString(remote.Name, color.FgGreen), strings.Join(remote.Urls, "\n")))
|
||||
task = NewRenderStringTask(fmt.Sprintf("%s\nUrls:\n%s", style.FgGreen.Sprint(remote.Name), strings.Join(remote.Urls, "\n")))
|
||||
}
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
|
||||
@@ -3,8 +3,8 @@ package gui
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
)
|
||||
|
||||
func (gui *Gui) resetToRef(ref string, strength string, span string, options oscommands.RunCommandOptions) error {
|
||||
@@ -36,9 +36,7 @@ func (gui *Gui) createResetMenu(ref string) error {
|
||||
menuItems[i] = &menuItem{
|
||||
displayStrings: []string{
|
||||
fmt.Sprintf("%s reset", strength),
|
||||
color.New(color.FgRed).Sprint(
|
||||
fmt.Sprintf("reset --%s %s", strength, ref),
|
||||
),
|
||||
style.FgRed.Sprintf("reset --%s %s", strength, ref),
|
||||
},
|
||||
onPress: func() error {
|
||||
return gui.resetToRef(ref, strength, "Reset", oscommands.RunCommandOptions{})
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/theme"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
func (gui *Gui) handleOpenSearch(viewName string) error {
|
||||
@@ -53,10 +52,7 @@ func (gui *Gui) onSelectItemWrapper(innerFunc func(int) error) func(int, int, in
|
||||
fmt.Sprintf(
|
||||
"no matches for '%s' %s",
|
||||
gui.State.Searching.searchString,
|
||||
utils.ColoredString(
|
||||
fmt.Sprintf("%s: exit search mode", gui.getKeyDisplay(keybindingConfig.Universal.Return)),
|
||||
theme.OptionsFgColor,
|
||||
),
|
||||
theme.OptionsFgColor.Sprintf("%s: exit search mode", gui.getKeyDisplay(keybindingConfig.Universal.Return)),
|
||||
),
|
||||
)
|
||||
return nil
|
||||
@@ -68,14 +64,11 @@ func (gui *Gui) onSelectItemWrapper(innerFunc func(int) error) func(int, int, in
|
||||
gui.State.Searching.searchString,
|
||||
index+1,
|
||||
total,
|
||||
utils.ColoredString(
|
||||
fmt.Sprintf(
|
||||
"%s: next match, %s: previous match, %s: exit search mode",
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.NextMatch),
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.PrevMatch),
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.Return),
|
||||
),
|
||||
theme.OptionsFgColor,
|
||||
theme.OptionsFgColor.Sprintf(
|
||||
"%s: next match, %s: previous match, %s: exit search mode",
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.NextMatch),
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.PrevMatch),
|
||||
gui.getKeyDisplay(keybindingConfig.Universal.Return),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -34,8 +34,8 @@ func (gui *Gui) refreshStagingPanel(forceSecondaryFocused bool, selectedLineIdx
|
||||
}
|
||||
|
||||
// note for custom diffs, we'll need to send a flag here saying not to use the custom diff
|
||||
diff := gui.GitCommand.WorktreeFileDiff(file, true, secondaryFocused)
|
||||
secondaryDiff := gui.GitCommand.WorktreeFileDiff(file, true, !secondaryFocused)
|
||||
diff := gui.GitCommand.WorktreeFileDiff(file, true, secondaryFocused, false)
|
||||
secondaryDiff := gui.GitCommand.WorktreeFileDiff(file, true, !secondaryFocused, false)
|
||||
|
||||
// if we have e.g. a deleted file with nothing else to the diff will have only
|
||||
// 4-5 lines in which case we'll swap panels
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands"
|
||||
"github.com/jesseduffield/lazygit/pkg/constants"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
@@ -23,22 +23,15 @@ func (gui *Gui) refreshStatus() {
|
||||
}
|
||||
status := ""
|
||||
|
||||
if currentBranch.Pushables != "" && currentBranch.Pullables != "" {
|
||||
trackColor := color.FgYellow
|
||||
if currentBranch.Pushables == "0" && currentBranch.Pullables == "0" {
|
||||
trackColor = color.FgGreen
|
||||
} else if currentBranch.Pushables == "?" && currentBranch.Pullables == "?" {
|
||||
trackColor = color.FgRed
|
||||
}
|
||||
|
||||
status = utils.ColoredString(fmt.Sprintf("↑%s↓%s ", currentBranch.Pushables, currentBranch.Pullables), trackColor)
|
||||
if currentBranch.IsRealBranch() {
|
||||
status += presentation.ColoredBranchStatus(currentBranch) + " "
|
||||
}
|
||||
|
||||
if gui.GitCommand.WorkingTreeState() != commands.REBASE_MODE_NORMAL {
|
||||
status += utils.ColoredString(fmt.Sprintf("(%s) ", gui.GitCommand.WorkingTreeState()), color.FgYellow)
|
||||
status += style.FgYellow.Sprintf("(%s) ", gui.GitCommand.WorkingTreeState())
|
||||
}
|
||||
|
||||
name := utils.ColoredString(currentBranch.Name, presentation.GetBranchColor(currentBranch.Name))
|
||||
name := presentation.GetBranchTextStyle(currentBranch.Name).Sprint(currentBranch.Name)
|
||||
repoName := utils.GetCurrentRepoName()
|
||||
status += fmt.Sprintf("%s → %s ", repoName, name)
|
||||
|
||||
@@ -75,7 +68,7 @@ func (gui *Gui) handleStatusClick() error {
|
||||
}
|
||||
|
||||
cx, _ := gui.Views.Status.Cursor()
|
||||
upstreamStatus := fmt.Sprintf("↑%s↓%s", currentBranch.Pushables, currentBranch.Pullables)
|
||||
upstreamStatus := presentation.BranchStatus(currentBranch)
|
||||
repoName := utils.GetCurrentRepoName()
|
||||
switch gui.GitCommand.WorkingTreeState() {
|
||||
case commands.REBASE_MODE_REBASING, commands.REBASE_MODE_MERGING:
|
||||
@@ -101,8 +94,6 @@ func (gui *Gui) handleStatusSelect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
magenta := color.New(color.FgMagenta)
|
||||
|
||||
dashboardString := strings.Join(
|
||||
[]string{
|
||||
lazygitTitle(),
|
||||
@@ -112,7 +103,7 @@ func (gui *Gui) handleStatusSelect() error {
|
||||
fmt.Sprintf("Tutorial: %s", constants.Links.Docs.Tutorial),
|
||||
fmt.Sprintf("Raise an Issue: %s", constants.Links.Issues),
|
||||
fmt.Sprintf("Release Notes: %s", constants.Links.Releases),
|
||||
magenta.Sprintf("Become a sponsor (github is matching all donations for 12 months): %s", constants.Links.Donate), // caffeine ain't free
|
||||
style.FgMagenta.Sprintf("Become a sponsor (github is matching all donations for 12 months): %s", constants.Links.Donate), // caffeine ain't free
|
||||
}, "\n\n")
|
||||
|
||||
return gui.refreshMainViews(refreshMainOpts{
|
||||
|
||||
63
pkg/gui/style/basic_styles.go
Normal file
63
pkg/gui/style/basic_styles.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package style
|
||||
|
||||
import (
|
||||
"github.com/gookit/color"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var (
|
||||
FgWhite = FromBasicFg(color.FgWhite)
|
||||
FgLightWhite = FromBasicFg(color.FgLightWhite)
|
||||
FgBlack = FromBasicFg(color.FgBlack)
|
||||
FgBlackLighter = FromBasicFg(color.FgBlack.Light())
|
||||
FgCyan = FromBasicFg(color.FgCyan)
|
||||
FgRed = FromBasicFg(color.FgRed)
|
||||
FgGreen = FromBasicFg(color.FgGreen)
|
||||
FgBlue = FromBasicFg(color.FgBlue)
|
||||
FgYellow = FromBasicFg(color.FgYellow)
|
||||
FgMagenta = FromBasicFg(color.FgMagenta)
|
||||
|
||||
BgWhite = FromBasicBg(color.BgWhite)
|
||||
BgBlack = FromBasicBg(color.BgBlack)
|
||||
BgRed = FromBasicBg(color.BgRed)
|
||||
BgGreen = FromBasicBg(color.BgGreen)
|
||||
BgYellow = FromBasicBg(color.BgYellow)
|
||||
BgBlue = FromBasicBg(color.BgBlue)
|
||||
BgMagenta = FromBasicBg(color.BgMagenta)
|
||||
BgCyan = FromBasicBg(color.BgCyan)
|
||||
|
||||
AttrUnderline = New().SetUnderline()
|
||||
AttrBold = New().SetBold()
|
||||
|
||||
ColorMap = map[string]struct {
|
||||
Foreground TextStyle
|
||||
Background TextStyle
|
||||
}{
|
||||
"default": {FgWhite, BgBlack},
|
||||
"black": {FgBlack, BgBlack},
|
||||
"red": {FgRed, BgRed},
|
||||
"green": {FgGreen, BgGreen},
|
||||
"yellow": {FgYellow, BgYellow},
|
||||
"blue": {FgBlue, BgBlue},
|
||||
"magenta": {FgMagenta, BgMagenta},
|
||||
"cyan": {FgCyan, BgCyan},
|
||||
"white": {FgWhite, BgWhite},
|
||||
}
|
||||
)
|
||||
|
||||
func FromBasicFg(fg color.Color) TextStyle {
|
||||
return New().SetFg(NewBasicColor(fg))
|
||||
}
|
||||
|
||||
func FromBasicBg(bg color.Color) TextStyle {
|
||||
return New().SetBg(NewBasicColor(bg))
|
||||
}
|
||||
|
||||
func TemplateFuncMapAddColors(m template.FuncMap) template.FuncMap {
|
||||
for k, v := range ColorMap {
|
||||
m[k] = v.Foreground.Sprint
|
||||
}
|
||||
m["underline"] = color.OpUnderscore.Sprint
|
||||
m["bold"] = color.OpBold.Sprint
|
||||
return m
|
||||
}
|
||||
39
pkg/gui/style/color.go
Normal file
39
pkg/gui/style/color.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package style
|
||||
|
||||
import "github.com/gookit/color"
|
||||
|
||||
type Color struct {
|
||||
rgb *color.RGBColor
|
||||
basic *color.Color
|
||||
}
|
||||
|
||||
func NewRGBColor(cl color.RGBColor) Color {
|
||||
c := Color{}
|
||||
c.rgb = &cl
|
||||
return c
|
||||
}
|
||||
|
||||
func NewBasicColor(cl color.Color) Color {
|
||||
c := Color{}
|
||||
c.basic = &cl
|
||||
return c
|
||||
}
|
||||
|
||||
func (c Color) IsRGB() bool {
|
||||
return c.rgb != nil
|
||||
}
|
||||
|
||||
func (c Color) ToRGB(isBg bool) Color {
|
||||
if c.IsRGB() {
|
||||
return c
|
||||
}
|
||||
|
||||
if isBg {
|
||||
// We need to convert bg color to fg color
|
||||
// This is a gookit/color bug,
|
||||
// https://github.com/gookit/color/issues/39
|
||||
return NewRGBColor((*c.basic - 10).RGB())
|
||||
}
|
||||
|
||||
return NewRGBColor(c.basic.RGB())
|
||||
}
|
||||
55
pkg/gui/style/decoration.go
Normal file
55
pkg/gui/style/decoration.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package style
|
||||
|
||||
import "github.com/gookit/color"
|
||||
|
||||
type Decoration struct {
|
||||
bold bool
|
||||
underline bool
|
||||
reverse bool
|
||||
}
|
||||
|
||||
func (d *Decoration) SetBold() {
|
||||
d.bold = true
|
||||
}
|
||||
|
||||
func (d *Decoration) SetUnderline() {
|
||||
d.underline = true
|
||||
}
|
||||
|
||||
func (d *Decoration) SetReverse() {
|
||||
d.reverse = true
|
||||
}
|
||||
|
||||
func (d Decoration) ToOpts() color.Opts {
|
||||
opts := make([]color.Color, 0, 3)
|
||||
|
||||
if d.bold {
|
||||
opts = append(opts, color.OpBold)
|
||||
}
|
||||
|
||||
if d.underline {
|
||||
opts = append(opts, color.OpUnderscore)
|
||||
}
|
||||
|
||||
if d.reverse {
|
||||
opts = append(opts, color.OpReverse)
|
||||
}
|
||||
|
||||
return opts
|
||||
}
|
||||
|
||||
func (d Decoration) Merge(other Decoration) Decoration {
|
||||
if other.bold {
|
||||
d.bold = true
|
||||
}
|
||||
|
||||
if other.underline {
|
||||
d.underline = true
|
||||
}
|
||||
|
||||
if other.reverse {
|
||||
d.reverse = true
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
211
pkg/gui/style/style_test.go
Normal file
211
pkg/gui/style/style_test.go
Normal file
@@ -0,0 +1,211 @@
|
||||
package style
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
"github.com/gookit/color"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/xo/terminfo"
|
||||
)
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
type scenario struct {
|
||||
name string
|
||||
toMerge []TextStyle
|
||||
expectedStyle TextStyle
|
||||
expectedStr string
|
||||
}
|
||||
|
||||
// on CI we've got no color capability so we're forcing it here
|
||||
color.ForceSetColorLevel(terminfo.ColorLevelMillions)
|
||||
|
||||
fgRed := color.FgRed
|
||||
bgRed := color.BgRed
|
||||
fgBlue := color.FgBlue
|
||||
|
||||
rgbPinkLib := color.Rgb(0xFF, 0x00, 0xFF)
|
||||
rgbPink := NewRGBColor(rgbPinkLib)
|
||||
|
||||
rgbYellowLib := color.Rgb(0xFF, 0xFF, 0x00)
|
||||
rgbYellow := NewRGBColor(rgbYellowLib)
|
||||
|
||||
strToPrint := "foo"
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"no color",
|
||||
nil,
|
||||
TextStyle{style: color.Style{}},
|
||||
"foo",
|
||||
},
|
||||
{
|
||||
"only fg color",
|
||||
[]TextStyle{FgRed},
|
||||
TextStyle{fg: &Color{basic: &fgRed}, style: color.Style{fgRed}},
|
||||
"\x1b[31mfoo\x1b[0m",
|
||||
},
|
||||
{
|
||||
"only bg color",
|
||||
[]TextStyle{BgRed},
|
||||
TextStyle{bg: &Color{basic: &bgRed}, style: color.Style{bgRed}},
|
||||
"\x1b[41mfoo\x1b[0m",
|
||||
},
|
||||
{
|
||||
"fg and bg color",
|
||||
[]TextStyle{FgBlue, BgRed},
|
||||
TextStyle{
|
||||
fg: &Color{basic: &fgBlue},
|
||||
bg: &Color{basic: &bgRed},
|
||||
style: color.Style{fgBlue, bgRed},
|
||||
},
|
||||
"\x1b[34;41mfoo\x1b[0m",
|
||||
},
|
||||
{
|
||||
"single attribute",
|
||||
[]TextStyle{AttrBold},
|
||||
TextStyle{
|
||||
decoration: Decoration{bold: true},
|
||||
style: color.Style{color.OpBold},
|
||||
},
|
||||
"\x1b[1mfoo\x1b[0m",
|
||||
},
|
||||
{
|
||||
"multiple attributes",
|
||||
[]TextStyle{AttrBold, AttrUnderline},
|
||||
TextStyle{
|
||||
decoration: Decoration{
|
||||
bold: true,
|
||||
underline: true,
|
||||
},
|
||||
style: color.Style{color.OpBold, color.OpUnderscore},
|
||||
},
|
||||
"\x1b[1;4mfoo\x1b[0m",
|
||||
},
|
||||
{
|
||||
"multiple attributes and colors",
|
||||
[]TextStyle{AttrBold, FgBlue, AttrUnderline, BgRed},
|
||||
TextStyle{
|
||||
fg: &Color{basic: &fgBlue},
|
||||
bg: &Color{basic: &bgRed},
|
||||
decoration: Decoration{
|
||||
bold: true,
|
||||
underline: true,
|
||||
},
|
||||
style: color.Style{fgBlue, bgRed, color.OpBold, color.OpUnderscore},
|
||||
},
|
||||
"\x1b[34;41;1;4mfoo\x1b[0m",
|
||||
},
|
||||
{
|
||||
"rgb fg color",
|
||||
[]TextStyle{New().SetFg(rgbPink)},
|
||||
TextStyle{
|
||||
fg: &rgbPink,
|
||||
style: color.NewRGBStyle(rgbPinkLib).SetOpts(color.Opts{}),
|
||||
},
|
||||
// '38;2' qualifies an RGB foreground color
|
||||
"\x1b[38;2;255;0;255mfoo\x1b[0m",
|
||||
},
|
||||
{
|
||||
"rgb fg and bg color",
|
||||
[]TextStyle{New().SetFg(rgbPink).SetBg(rgbYellow)},
|
||||
TextStyle{
|
||||
fg: &rgbPink,
|
||||
bg: &rgbYellow,
|
||||
style: color.NewRGBStyle(rgbPinkLib, rgbYellowLib).SetOpts(color.Opts{}),
|
||||
},
|
||||
// '48;2' qualifies an RGB background color
|
||||
"\x1b[38;2;255;0;255;48;2;255;255;0mfoo\x1b[0m",
|
||||
},
|
||||
{
|
||||
"rgb fg and bg color with opts",
|
||||
[]TextStyle{AttrBold, New().SetFg(rgbPink).SetBg(rgbYellow), AttrUnderline},
|
||||
TextStyle{
|
||||
fg: &rgbPink,
|
||||
bg: &rgbYellow,
|
||||
decoration: Decoration{
|
||||
bold: true,
|
||||
underline: true,
|
||||
},
|
||||
style: color.NewRGBStyle(rgbPinkLib, rgbYellowLib).SetOpts(color.Opts{color.OpBold, color.OpUnderscore}),
|
||||
},
|
||||
"\x1b[38;2;255;0;255;48;2;255;255;0;1;4mfoo\x1b[0m",
|
||||
},
|
||||
{
|
||||
"mix color-16 with rgb colors",
|
||||
[]TextStyle{New().SetFg(rgbYellow), BgRed},
|
||||
TextStyle{
|
||||
fg: &rgbYellow,
|
||||
bg: &Color{basic: &bgRed},
|
||||
style: color.NewRGBStyle(
|
||||
rgbYellowLib,
|
||||
fgRed.RGB(), // We need to use FG here, https://github.com/gookit/color/issues/39
|
||||
).SetOpts(color.Opts{}),
|
||||
},
|
||||
"\x1b[38;2;255;255;0;48;2;197;30;20mfoo\x1b[0m",
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
style := New()
|
||||
for _, other := range s.toMerge {
|
||||
style = style.MergeStyle(other)
|
||||
}
|
||||
assert.Equal(t, s.expectedStyle, style)
|
||||
assert.Equal(t, s.expectedStr, style.Sprint(strToPrint))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateFuncMapAddColors(t *testing.T) {
|
||||
type scenario struct {
|
||||
name string
|
||||
tmpl string
|
||||
expect string
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
"normal template",
|
||||
"{{ .Foo }}",
|
||||
"bar",
|
||||
},
|
||||
{
|
||||
"colored string",
|
||||
"{{ .Foo | red }}",
|
||||
"\x1b[31mbar\x1b[0m",
|
||||
},
|
||||
{
|
||||
"string with decorator",
|
||||
"{{ .Foo | bold }}",
|
||||
"\x1b[1mbar\x1b[0m",
|
||||
},
|
||||
{
|
||||
"string with color and decorator",
|
||||
"{{ .Foo | bold | red }}",
|
||||
"\x1b[31m\x1b[1mbar\x1b[0m\x1b[0m",
|
||||
},
|
||||
{
|
||||
"multiple string with diffrent colors",
|
||||
"{{ .Foo | red }} - {{ .Foo | blue }}",
|
||||
"\x1b[31mbar\x1b[0m - \x1b[34mbar\x1b[0m",
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.name, func(t *testing.T) {
|
||||
tmpl, err := template.New("test template").Funcs(TemplateFuncMapAddColors(template.FuncMap{})).Parse(s.tmpl)
|
||||
assert.NoError(t, err)
|
||||
|
||||
buff := bytes.NewBuffer(nil)
|
||||
err = tmpl.Execute(buff, struct{ Foo string }{"bar"})
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, s.expect, buff.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
149
pkg/gui/style/text_style.go
Normal file
149
pkg/gui/style/text_style.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package style
|
||||
|
||||
import (
|
||||
"github.com/gookit/color"
|
||||
)
|
||||
|
||||
// A TextStyle contains a foreground color, background color, and
|
||||
// decorations (bold/underline/reverse).
|
||||
//
|
||||
// Colors may each be either 16-bit or 24-bit RGB colors. When
|
||||
// we need to produce a string with a TextStyle, if either foreground or
|
||||
// background color is RGB, we'll promote the other color component to RGB as well.
|
||||
// We could simplify this code by forcing everything to be RGB, but we're not
|
||||
// sure how compatible or efficient that would be with various terminals.
|
||||
// Lazygit will typically stick to 16-bit colors, but users may configure RGB colors.
|
||||
//
|
||||
// TextStyles are value objects, not entities, so for example if you want to
|
||||
// add the bold decoration to a TextStyle, we'll create a new TextStyle with
|
||||
// that decoration applied.
|
||||
//
|
||||
// Decorations are additive, so when we merge two TextStyles, if either is bold
|
||||
// then the resulting style will also be bold.
|
||||
//
|
||||
// So that we aren't rederiving the underlying style each time we want to print
|
||||
// a string, we derive it when a new TextStyle is created and store it in the
|
||||
// `style` field.
|
||||
|
||||
type TextStyle struct {
|
||||
fg *Color
|
||||
bg *Color
|
||||
decoration Decoration
|
||||
|
||||
style Sprinter
|
||||
}
|
||||
|
||||
type Sprinter interface {
|
||||
Sprint(a ...interface{}) string
|
||||
Sprintf(format string, a ...interface{}) string
|
||||
}
|
||||
|
||||
func New() TextStyle {
|
||||
s := TextStyle{}
|
||||
s.style = s.deriveStyle()
|
||||
return s
|
||||
}
|
||||
|
||||
func (b TextStyle) Sprint(a ...interface{}) string {
|
||||
return b.style.Sprint(a...)
|
||||
}
|
||||
|
||||
func (b TextStyle) Sprintf(format string, a ...interface{}) string {
|
||||
return b.style.Sprintf(format, a...)
|
||||
}
|
||||
|
||||
// note that our receiver here is not a pointer which means we're receiving a
|
||||
// copy of the original TextStyle. This allows us to mutate and return that
|
||||
// TextStyle receiver without actually modifying the original.
|
||||
func (b TextStyle) SetBold() TextStyle {
|
||||
b.decoration.SetBold()
|
||||
b.style = b.deriveStyle()
|
||||
return b
|
||||
}
|
||||
|
||||
func (b TextStyle) SetUnderline() TextStyle {
|
||||
b.decoration.SetUnderline()
|
||||
b.style = b.deriveStyle()
|
||||
return b
|
||||
}
|
||||
|
||||
func (b TextStyle) SetReverse() TextStyle {
|
||||
b.decoration.SetReverse()
|
||||
b.style = b.deriveStyle()
|
||||
return b
|
||||
}
|
||||
|
||||
func (b TextStyle) SetBg(color Color) TextStyle {
|
||||
b.bg = &color
|
||||
b.style = b.deriveStyle()
|
||||
return b
|
||||
}
|
||||
|
||||
func (b TextStyle) SetFg(color Color) TextStyle {
|
||||
b.fg = &color
|
||||
b.style = b.deriveStyle()
|
||||
return b
|
||||
}
|
||||
|
||||
func (b TextStyle) MergeStyle(other TextStyle) TextStyle {
|
||||
b.decoration = b.decoration.Merge(other.decoration)
|
||||
|
||||
if other.fg != nil {
|
||||
b.fg = other.fg
|
||||
}
|
||||
|
||||
if other.bg != nil {
|
||||
b.bg = other.bg
|
||||
}
|
||||
|
||||
b.style = b.deriveStyle()
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b TextStyle) deriveStyle() Sprinter {
|
||||
if b.fg == nil && b.bg == nil {
|
||||
return color.Style(b.decoration.ToOpts())
|
||||
}
|
||||
|
||||
isRgb := (b.fg != nil && b.fg.IsRGB()) || (b.bg != nil && b.bg.IsRGB())
|
||||
if isRgb {
|
||||
return b.deriveRGBStyle()
|
||||
}
|
||||
|
||||
return b.deriveBasicStyle()
|
||||
}
|
||||
|
||||
func (b TextStyle) deriveBasicStyle() color.Style {
|
||||
style := make([]color.Color, 0, 5)
|
||||
|
||||
if b.fg != nil {
|
||||
style = append(style, *b.fg.basic)
|
||||
}
|
||||
|
||||
if b.bg != nil {
|
||||
style = append(style, *b.bg.basic)
|
||||
}
|
||||
|
||||
style = append(style, b.decoration.ToOpts()...)
|
||||
|
||||
return color.Style(style)
|
||||
}
|
||||
|
||||
func (b TextStyle) deriveRGBStyle() *color.RGBStyle {
|
||||
style := &color.RGBStyle{}
|
||||
|
||||
if b.fg != nil {
|
||||
style.SetFg(*b.fg.ToRGB(false).rgb)
|
||||
}
|
||||
|
||||
if b.bg != nil {
|
||||
// We need to convert the bg firstly to a foreground color,
|
||||
// For more info see
|
||||
style.SetBg(*b.bg.ToRGB(true).rgb)
|
||||
}
|
||||
|
||||
style.SetOpts(b.decoration.ToOpts())
|
||||
|
||||
return style
|
||||
}
|
||||
@@ -6,9 +6,8 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/models"
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
)
|
||||
|
||||
func (gui *Gui) getSelectedSubmodule() *models.SubmoduleConfig {
|
||||
@@ -28,16 +27,16 @@ func (gui *Gui) handleSubmoduleSelect() error {
|
||||
} else {
|
||||
prefix := fmt.Sprintf(
|
||||
"Name: %s\nPath: %s\nUrl: %s\n\n",
|
||||
utils.ColoredString(submodule.Name, color.FgGreen),
|
||||
utils.ColoredString(submodule.Path, color.FgYellow),
|
||||
utils.ColoredString(submodule.Url, color.FgCyan),
|
||||
style.FgGreen.Sprint(submodule.Name),
|
||||
style.FgYellow.Sprint(submodule.Path),
|
||||
style.FgCyan.Sprint(submodule.Url),
|
||||
)
|
||||
|
||||
file := gui.fileForSubmodule(submodule)
|
||||
if file == nil {
|
||||
task = NewRenderStringTask(prefix)
|
||||
} else {
|
||||
cmdStr := gui.GitCommand.WorktreeFileDiffCmdStr(file, false, !file.HasUnstagedChanges && file.HasStagedChanges)
|
||||
cmdStr := gui.GitCommand.WorktreeFileDiffCmdStr(file, false, !file.HasUnstagedChanges && file.HasStagedChanges, gui.State.IgnoreWhitespaceInDiffView)
|
||||
cmd := gui.OSCommand.ExecutableFromString(cmdStr)
|
||||
task = NewRunCommandTaskWithPrefix(cmd, prefix)
|
||||
}
|
||||
@@ -213,7 +212,7 @@ func (gui *Gui) handleResetRemoveSubmodule(submodule *models.SubmoduleConfig) er
|
||||
func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||
menuItems := []*menuItem{
|
||||
{
|
||||
displayStrings: []string{gui.Tr.LcBulkInitSubmodules, utils.ColoredString(gui.GitCommand.SubmoduleBulkInitCmdStr(), color.FgGreen)},
|
||||
displayStrings: []string{gui.Tr.LcBulkInitSubmodules, style.FgGreen.Sprint(gui.GitCommand.SubmoduleBulkInitCmdStr())},
|
||||
onPress: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkInitialiseSubmodules).RunCommand(gui.GitCommand.SubmoduleBulkInitCmdStr()); err != nil {
|
||||
@@ -225,7 +224,7 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: []string{gui.Tr.LcBulkUpdateSubmodules, utils.ColoredString(gui.GitCommand.SubmoduleBulkUpdateCmdStr(), color.FgYellow)},
|
||||
displayStrings: []string{gui.Tr.LcBulkUpdateSubmodules, style.FgYellow.Sprint(gui.GitCommand.SubmoduleBulkUpdateCmdStr())},
|
||||
onPress: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkUpdateSubmodules).RunCommand(gui.GitCommand.SubmoduleBulkUpdateCmdStr()); err != nil {
|
||||
@@ -237,7 +236,7 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: []string{gui.Tr.LcSubmoduleStashAndReset, utils.ColoredString(fmt.Sprintf("git stash in each submodule && %s", gui.GitCommand.SubmoduleForceBulkUpdateCmdStr()), color.FgRed)},
|
||||
displayStrings: []string{gui.Tr.LcSubmoduleStashAndReset, style.FgRed.Sprintf("git stash in each submodule && %s", gui.GitCommand.SubmoduleForceBulkUpdateCmdStr())},
|
||||
onPress: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||
if err := gui.GitCommand.WithSpan(gui.Tr.Spans.BulkStashAndResetSubmodules).ResetSubmodules(gui.State.Submodules); err != nil {
|
||||
@@ -249,7 +248,7 @@ func (gui *Gui) handleBulkSubmoduleActionsMenu() error {
|
||||
},
|
||||
},
|
||||
{
|
||||
displayStrings: []string{gui.Tr.LcBulkDeinitSubmodules, utils.ColoredString(gui.GitCommand.SubmoduleBulkDeinitCmdStr(), color.FgRed)},
|
||||
displayStrings: []string{gui.Tr.LcBulkDeinitSubmodules, style.FgRed.Sprint(gui.GitCommand.SubmoduleBulkDeinitCmdStr())},
|
||||
onPress: func() error {
|
||||
return gui.WithWaitingStatus(gui.Tr.LcRunningCommand, func() error {
|
||||
if err := gui.OSCommand.WithSpan(gui.Tr.Spans.BulkDeinitialiseSubmodules).RunCommand(gui.GitCommand.SubmoduleBulkDeinitCmdStr()); err != nil {
|
||||
|
||||
@@ -358,7 +358,7 @@ func (gui *Gui) onViewTabClick(viewName string, tabIndex int) error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handleNextTab() error {
|
||||
v := gui.g.CurrentView()
|
||||
v := getTabbedView(gui)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -370,7 +370,7 @@ func (gui *Gui) handleNextTab() error {
|
||||
}
|
||||
|
||||
func (gui *Gui) handlePrevTab() error {
|
||||
v := gui.g.CurrentView()
|
||||
v := getTabbedView(gui)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -392,3 +392,10 @@ func (gui *Gui) pageDelta(view *gocui.View) int {
|
||||
|
||||
return delta
|
||||
}
|
||||
|
||||
func getTabbedView(gui *Gui) *gocui.View {
|
||||
// It safe assumption that only static contexts have tabs
|
||||
context := gui.currentStaticContext()
|
||||
view, _ := gui.g.View(context.GetViewName())
|
||||
return view
|
||||
}
|
||||
|
||||
13
pkg/gui/whitespace-toggle.go
Normal file
13
pkg/gui/whitespace-toggle.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package gui
|
||||
|
||||
func (gui *Gui) toggleWhitespaceInDiffView() error {
|
||||
gui.State.IgnoreWhitespaceInDiffView = !gui.State.IgnoreWhitespaceInDiffView
|
||||
|
||||
toastMessage := gui.Tr.ShowingWhitespaceInDiffView
|
||||
if gui.State.IgnoreWhitespaceInDiffView {
|
||||
toastMessage = gui.Tr.IgnoringWhitespaceInDiffView
|
||||
}
|
||||
gui.raiseToast(toastMessage)
|
||||
|
||||
return gui.refreshFilesAndSubmodules()
|
||||
}
|
||||
@@ -3,11 +3,11 @@ package gui
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
)
|
||||
|
||||
func (gui *Gui) handleCreateResetMenu() error {
|
||||
red := color.New(color.FgRed)
|
||||
red := style.FgRed
|
||||
|
||||
nukeStr := "reset --hard HEAD && git clean -fd"
|
||||
if len(gui.State.Submodules) > 0 {
|
||||
|
||||
543
pkg/i18n/chinese.go
Normal file
543
pkg/i18n/chinese.go
Normal file
@@ -0,0 +1,543 @@
|
||||
package i18n
|
||||
|
||||
const chineseIntroPopupMessage = `
|
||||
感谢您使用lazygit!与您分享的三件事:
|
||||
|
||||
1) 如果您想了解lazygit的功能,请观看此视频:
|
||||
https://youtu.be/CPLdltN7wgE
|
||||
|
||||
2) 请务必阅读最新的发行说明,网址为::
|
||||
https://github.com/jesseduffield/lazygit/releases
|
||||
|
||||
3) 如果您使用的是git,那将使您成为一名程序员! 在您的帮助下,我们可以使
|
||||
lazygit更好, 因此,请考虑成为一名贡献者并加入
|
||||
https://github.com/jesseduffield/lazygit
|
||||
您也可以赞助我,并通过单击捐赠告诉我要做什么。
|
||||
右下角的按钮.
|
||||
甚至只是给回购交易代码加注星标,我们离20k星就不远了!
|
||||
`
|
||||
|
||||
func chineseTranslationSet() TranslationSet {
|
||||
return TranslationSet{
|
||||
NotEnoughSpace: "没有足够的空间来渲染面板",
|
||||
DiffTitle: "差异",
|
||||
LogTitle: "日志",
|
||||
FilesTitle: "文件",
|
||||
BranchesTitle: "分支",
|
||||
CommitsTitle: "提交",
|
||||
StashTitle: "封存隐藏(Stash)",
|
||||
UnstagedChanges: `未暂存(Unstaged)更改`,
|
||||
StagedChanges: `已暂存(Staged)更改`,
|
||||
PatchBuildingMainTitle: `将 行/块 添加到补丁`,
|
||||
MergingMainTitle: "解决合并冲突",
|
||||
MainTitle: "主要",
|
||||
StagingTitle: "正在暂存",
|
||||
MergingTitle: "合并中",
|
||||
NormalTitle: "正常",
|
||||
CommitMessage: "提交信息",
|
||||
CredentialsUsername: "用户名",
|
||||
CredentialsPassword: "密码",
|
||||
CredentialsPassphrase: "输入SSH密钥的密码",
|
||||
PassUnameWrong: "密码, 密码 和/或 用户名错误",
|
||||
CommitChanges: "提交更改",
|
||||
AmendLastCommit: "修改最后一次提交",
|
||||
SureToAmend: "您确定要修改上一次提交吗?之后,您可以从提交面板更改提交消息.",
|
||||
NoCommitToAmend: "没有提交的修改.",
|
||||
CommitChangesWithEditor: "使用git编辑器提交更改",
|
||||
StatusTitle: "状态",
|
||||
LcNavigate: "导航",
|
||||
LcMenu: "菜单",
|
||||
LcExecute: "执行",
|
||||
LcOpen: "打开",
|
||||
LcIgnore: "忽略",
|
||||
LcDelete: "删除",
|
||||
LcToggleStaged: "切换已暂存(staged)",
|
||||
LcToggleStagedAll: "暂存(stage)/未暂存(unstage)全部",
|
||||
LcToggleTreeView: "切换文件树视图",
|
||||
LcOpenMergeTool: "打开外部合并工具 (git mergetool)",
|
||||
LcRefresh: "刷新",
|
||||
LcPush: "推送(push)",
|
||||
LcPull: "拉取(pull)",
|
||||
LcEdit: "编辑",
|
||||
LcScroll: "滚动",
|
||||
LcAbortMerge: "中止合并",
|
||||
LcResolveMergeConflicts: "解决合并冲突",
|
||||
MergeConflictsTitle: "合并冲突",
|
||||
LcCheckout: "签出(checkout)",
|
||||
NoChangedFiles: "没有更改的文件",
|
||||
FileHasNoUnstagedChanges: "文件没有要添加的未暂存更改",
|
||||
CannotGitAdd: "无法 git add --patch 未跟踪的文件",
|
||||
NoFilesDisplay: "没有文件可显示",
|
||||
NotAFile: "不是文件",
|
||||
PullWait: "拉取中...",
|
||||
PushWait: "推送中...",
|
||||
FetchWait: "正在取得...",
|
||||
FileNoMergeCons: "该文件没有内联合并冲突",
|
||||
LcSoftReset: "软复位",
|
||||
SureTo: "你确定要 {{.deleteVerb}} {{.fileName}} (你会丢失你的更改)?",
|
||||
AlreadyCheckedOutBranch: "您已经签出了这个分支",
|
||||
SureForceCheckout: "您确定要强制签出吗? 您将丢失所有本地更改",
|
||||
ForceCheckoutBranch: "强制签出分支",
|
||||
BranchName: "分支名称",
|
||||
NewBranchNameBranchOff: "新分支名称 (分支不属于 {{.branchName}})",
|
||||
CantDeleteCheckOutBranch: "您不能删除已签出的分支!",
|
||||
DeleteBranch: "删除分支",
|
||||
DeleteBranchMessage: "您确定要删除分支 {{.selectedBranchName}}?",
|
||||
ForceDeleteBranchMessage: "{{.selectedBranchName}} 没有完全合并. 你确定你要删除它?",
|
||||
LcRebaseBranch: "将已签出的分支重新部署到该分支",
|
||||
CantRebaseOntoSelf: "您不能将分支建立在其自身之上",
|
||||
CantMergeBranchIntoItself: "您不能将分支合并到自身中",
|
||||
LcForceCheckout: "强制签出",
|
||||
LcMerge: "合并",
|
||||
LcCheckoutByName: "按名称签出",
|
||||
LcNewBranch: "新分支",
|
||||
LcDeleteBranch: "删除分支",
|
||||
LcForceDeleteBranch: "删除分支 (强制)",
|
||||
NoBranchesThisRepo: "此仓库没有分支",
|
||||
NoTrackingThisBranch: "该分支没有跟踪",
|
||||
CommitMessageConfirm: "{{.keyBindClose}}: 关闭, {{.keyBindNewLine}}: 新行, {{.keyBindConfirm}}: 确认",
|
||||
CommitWithoutMessageErr: "没有提交消息就无法提交",
|
||||
CloseConfirm: "{{.keyBindClose}}: 关闭, {{.keyBindConfirm}}: 确认",
|
||||
LcClose: "关闭",
|
||||
LcQuit: "退出",
|
||||
SureResetThisCommit: "您确定要重置为此提交吗?",
|
||||
ResetToCommit: "重置以提交",
|
||||
LcSquashDown: "向下聚合(squash down)",
|
||||
LcRename: "重命名",
|
||||
LcResetToThisCommit: "重置为此提交",
|
||||
LcFixupCommit: "修正提交",
|
||||
NoCommitsThisBranch: "该分支没有提交",
|
||||
OnlySquashTopmostCommit: "只能压缩最高提交",
|
||||
YouNoCommitsToSquash: "您没有提交来压缩",
|
||||
CantFixupWhileUnstagedChanges: "当有未暂存的更改时无法修正",
|
||||
Fixup: "修正",
|
||||
SureFixupThisCommit: "您确定要'修复'此提交吗?? 它将合并到下面的提交中",
|
||||
SureSquashThisCommit: "您确定要将这个提交压缩到下面的提交中吗?",
|
||||
Squash: "压缩",
|
||||
LcPickCommit: "选择提交 (mid-rebase时)",
|
||||
LcRevertCommit: "还原提交",
|
||||
OnlyRenameTopCommit: "只能从lazygit内部重写最高的提交。请使用shift-R",
|
||||
LcRenameCommit: "改写提交",
|
||||
LcDeleteCommit: "删除提交",
|
||||
LcMoveDownCommit: "下移提交",
|
||||
LcMoveUpCommit: "上移提交",
|
||||
LcEditCommit: "编辑提交",
|
||||
LcAmendToCommit: "用staged的修改来修改提交",
|
||||
LcRenameCommitEditor: "使用编辑器重命名提交",
|
||||
PotentialErrInGetselectedCommit: "获取选定提交中的潜在错误 (ui和状态不匹配)",
|
||||
Error: "错误",
|
||||
RunningSubprocess: "正在运行的子进程",
|
||||
LcSelectHunk: "选择块",
|
||||
LcNavigateConflicts: "解决冲突",
|
||||
LcPickHunk: "选择块",
|
||||
LcPickBothHunks: "pick both hunks",
|
||||
LcUndo: "撤销",
|
||||
LcUndoReflog: "撤销 (via reflog) (实验性)",
|
||||
LcRedoReflog: "重做 (via reflog) (实验性)",
|
||||
LcPop: "pop",
|
||||
LcDrop: "删除",
|
||||
LcApply: "恢复",
|
||||
NoStashEntries: "没有封存隐藏(stach)的条目",
|
||||
StashDrop: "Stash删除",
|
||||
SureDropStashEntry: "您确定要删除此隐藏(stach)条目?",
|
||||
StashPop: "Stash pop(恢复)",
|
||||
SurePopStashEntry: "您确定要弹出此隐藏条stash目吗?",
|
||||
StashApply: "Stash apply(恢复)",
|
||||
SureApplyStashEntry: "您确定要应用此隐藏stash条目?",
|
||||
NoStashTo: "没有stash到 {{.method}}",
|
||||
NoTrackedStagedFilesStash: "您没有要存储的跟踪/暂存文件到stash",
|
||||
StashChanges: "Stash更改",
|
||||
IssntListOfViews: "{{.name}} 不在视图列表中",
|
||||
LcNewFocusedViewIs: "新的焦点视图是 {{.newFocusedView}}",
|
||||
MergeAborted: "合并中止",
|
||||
OpenConfig: "打开配置文件",
|
||||
EditConfig: "编辑配置文件",
|
||||
ForcePush: "强制推送",
|
||||
ForcePushPrompt: "您的分支已与远程分支不同. 按 'esc' 取消, 或 'enter' 强制推送.",
|
||||
ForcePushDisabled: "您的分支已与远程分支不同并且你已经禁用了强行推送",
|
||||
UpdatesRejectedAndForcePushDisabled: "更新被拒绝,您已禁用强制推送",
|
||||
LcCheckForUpdate: "检查更新",
|
||||
CheckingForUpdates: "检查更新...",
|
||||
OnLatestVersionErr: "您已经有最新版本",
|
||||
MajorVersionErr: "新版本 ({{.newVersion}}) 与当前版本相比,具有向后兼容的更改 ({{.currentVersion}})",
|
||||
CouldNotFindBinaryErr: "在{{.url}处找不到任何二进制文件}",
|
||||
AnonymousReportingTitle: "帮助改善lazygit",
|
||||
AnonymousReportingPrompt: "您是否想启用匿名报告数据以帮助改善lazygit? (enter/esc)",
|
||||
MergeToolTitle: "合并工具",
|
||||
MergeToolPrompt: "确定要打开 `git mergetool` 吗?",
|
||||
IntroPopupMessage: chineseIntroPopupMessage,
|
||||
GitconfigParseErr: `由于存在未加引号的'\'字符,因此Gogit无法解析您的gitconfig文件。删除这些应该可以解决问题.`,
|
||||
LcEditFile: `编辑文件`,
|
||||
LcOpenFile: `打开文件`,
|
||||
LcIgnoreFile: `添加到 .gitignore`,
|
||||
LcRefreshFiles: `刷新文件`,
|
||||
LcMergeIntoCurrentBranch: `合并到当前签出的分支`,
|
||||
ConfirmQuit: `你确定你要退出吗?`,
|
||||
SwitchRepo: `切换到最近的仓库`,
|
||||
LcAllBranchesLogGraph: `显示所有分支日志`,
|
||||
UnsupportedGitService: `不支持的git服务`,
|
||||
LcCreatePullRequest: `创建pull请求`,
|
||||
LcCopyPullRequestURL: `将拉取请求URL复制到剪贴板`,
|
||||
NoBranchOnRemote: `该分支在远程上不存在。您需要先将其推送到远程.`,
|
||||
LcFetch: `fetch`,
|
||||
NoAutomaticGitFetchTitle: `没有自动git fetch`,
|
||||
NoAutomaticGitFetchBody: `Lazygit不能在私人仓库中使用"git fetch"; 在文件面板中使用'f'手动运行"git fetch"`,
|
||||
FileEnter: `暂存单个 块/行 用于文件, 或 折叠/展开 目录`,
|
||||
FileStagingRequirements: `只能暂存跟踪文件的单独行`,
|
||||
SelectHunk: `选择块`,
|
||||
StageSelection: `切换行 已暂存/未暂存`,
|
||||
ResetSelection: `删除变更 (git reset)`,
|
||||
ToggleDragSelect: `切换拖动选择`,
|
||||
ToggleSelectHunk: `切换选择块`,
|
||||
ToggleSelectionForPatch: `添加/移除 行到patch`,
|
||||
TogglePanel: `切换到其他面板`,
|
||||
CantStageStaged: `您无法上演已经上演的变更!`,
|
||||
ReturnToFilesPanel: `返回文件面板`,
|
||||
CantFindHunks: `在此补丁中找不到任何块`,
|
||||
CantFindHunk: `找不到块`,
|
||||
FastForward: `从上游快速前进此分支`,
|
||||
Fetching: "抓取和快进{{.from}} -> {{.to}} ...",
|
||||
FoundConflicts: "冲突!要中止,请按 'esc', 否则按'enter'",
|
||||
FoundConflictsTitle: "自动合并失败",
|
||||
Undo: "撤销",
|
||||
PickHunk: "pick hunk",
|
||||
PickBothHunks: "pick both hunks",
|
||||
ViewMergeRebaseOptions: "查看 合并/变基 选项",
|
||||
NotMergingOrRebasing: "您目前既不进行基础调整也不进行合并",
|
||||
RecentRepos: "最近的仓库",
|
||||
MergeOptionsTitle: "合并选项",
|
||||
RebaseOptionsTitle: "变基选项",
|
||||
CommitMessageTitle: "提交讯息",
|
||||
LocalBranchesTitle: "分支标签",
|
||||
SearchTitle: "搜索",
|
||||
TagsTitle: "Tags Tab",
|
||||
BranchCommitsTitle: "提交标签",
|
||||
MenuTitle: "菜单",
|
||||
RemotesTitle: "远程标签",
|
||||
CredentialsTitle: "证书",
|
||||
RemoteBranchesTitle: "远程分支 (在远程选项卡中)",
|
||||
PatchBuildingTitle: "补丁构建中",
|
||||
InformationTitle: "信息",
|
||||
SecondaryTitle: "次要",
|
||||
ReflogCommitsTitle: "Reflog标签",
|
||||
Title: "标题",
|
||||
GlobalTitle: "全局键绑定",
|
||||
ConflictsResolved: "解决所有合并冲突。继续?",
|
||||
RebasingTitle: "变基",
|
||||
ConfirmRebase: "您确定要重新设定基准 {{.checkedOutBranch}} onto {{.selectedBranch}} 吗?",
|
||||
ConfirmMerge: "您确定要合并 {{.selectedBranch}} into {{.checkedOutBranch}} 吗?",
|
||||
FwdNoUpstream: "无法快进没有上游的分支",
|
||||
FwdCommitsToPush: "无法快进并提交推送的分支",
|
||||
ErrorOccurred: "发生错误! 请在以下位置创建问题",
|
||||
NoRoom: "没有足够的空间",
|
||||
YouAreHere: "你在这里",
|
||||
LcRewordNotSupported: "当前不支持交互式重新基准化时的重新措词提交",
|
||||
LcCherryPickCopy: "复制提交 (cherry-pick)",
|
||||
LcCherryPickCopyRange: "复制提交范围 (cherry-pick)",
|
||||
LcPasteCommits: "粘贴提交 (cherry-pick)",
|
||||
SureCherryPick: "您确定要对复制的提交进行cherry-pick吗?",
|
||||
CherryPick: "Cherry-Pick",
|
||||
CannotRebaseOntoFirstCommit: "您不能以交互方式基于第一次提交",
|
||||
CannotSquashOntoSecondCommit: "您不能 聚合(squash)/修正(fixup)第二个提交",
|
||||
Donate: "捐助",
|
||||
AskQuestion: "问题咨询",
|
||||
PrevLine: "选择上一行",
|
||||
NextLine: "选择下一行",
|
||||
PrevHunk: "选择上一个块",
|
||||
NextHunk: "选择下一个块",
|
||||
PrevConflict: "选择上一个冲突",
|
||||
NextConflict: "选择下一个冲突",
|
||||
SelectTop: "选择顶部块",
|
||||
SelectBottom: "选择底部块",
|
||||
ScrollDown: "向下滚动",
|
||||
ScrollUp: "向上滚动",
|
||||
LcScrollUpMainPanel: "向上滚动主面板",
|
||||
LcScrollDownMainPanel: "向下滚动主面板",
|
||||
AmendCommitTitle: "修改提交",
|
||||
AmendCommitPrompt: "您确定要使用暂存文件来修改此提交吗?",
|
||||
DeleteCommitTitle: "删除提交",
|
||||
DeleteCommitPrompt: "您确定要删除此提交吗?",
|
||||
SquashingStatus: "正在聚合(squashing)",
|
||||
FixingStatus: "fixing up",
|
||||
DeletingStatus: "正在删除",
|
||||
MovingStatus: "正在移动",
|
||||
RebasingStatus: "变基",
|
||||
AmendingStatus: "修改",
|
||||
CherryPickingStatus: "cherry-picking",
|
||||
UndoingStatus: "正在撤销",
|
||||
RedoingStatus: "正在重做",
|
||||
CheckingOutStatus: "签出",
|
||||
CommittingStatus: "正在提交",
|
||||
CommitFiles: "提交文件",
|
||||
LcViewCommitFiles: "查看提交的文件",
|
||||
CommitFilesTitle: "提交文件",
|
||||
LcGoBack: "返回",
|
||||
NoCommiteFiles: "没有用于该提交的文件",
|
||||
LcCheckoutCommitFile: "签出文件",
|
||||
LcDiscardOldFileChange: "放弃对此文件的提交更改",
|
||||
DiscardFileChangesTitle: "放弃文件更改",
|
||||
DiscardFileChangesPrompt: "您确定要舍弃此提交对该文件的更改吗?? 如果此文件是在此提交中创建的,它将被删除",
|
||||
DisabledForGPG: "该功能不适用于使用GPG的用户",
|
||||
CreateRepo: "不在git仓库中. 创建一个新的git仓库吗? (y/n): ",
|
||||
AutoStashTitle: "自动存储?",
|
||||
AutoStashPrompt: "您必须隐藏并弹出更改以使更改生效。自动执行? (enter/esc)",
|
||||
StashPrefix: "自动隐藏更改 ",
|
||||
LcViewDiscardOptions: "查看'放弃更改‘选项",
|
||||
LcCancel: "取消",
|
||||
LcDiscardAllChanges: "放弃所有更改",
|
||||
LcDiscardUnstagedChanges: "放弃未进行的变更",
|
||||
LcDiscardAllChangesToAllFiles: "nuke 工作树",
|
||||
LcDiscardAnyUnstagedChanges: "放弃未进行的变更",
|
||||
LcDiscardUntrackedFiles: "丢弃未跟踪的文件",
|
||||
LcHardReset: "硬重置",
|
||||
LcHardResetUpstream: "硬重置到上游分支",
|
||||
LcViewResetOptions: `查看重置选项`,
|
||||
LcCreateFixupCommit: `为此提交创建fixup提交`,
|
||||
LcSquashAboveCommits: `聚合所有的'fixup!'高于所选提交的提交 (自动聚合)`,
|
||||
SquashAboveCommits: `聚合所有的'fixup!'高于所选提交的提交 (自动聚合)`,
|
||||
SureSquashAboveCommits: `您确定要聚合所有修正程序吗! 在{{.commit}}上面提交吗?`,
|
||||
CreateFixupCommit: `创建修正提交`,
|
||||
SureCreateFixupCommit: `您确定要创建修正程序吗! 提交{{.commit}}?`,
|
||||
LcExecuteCustomCommand: "执行自定义命令",
|
||||
CustomCommand: "自定义命令:",
|
||||
LcCommitChangesWithoutHook: "提交更改而无需预先提交钩子",
|
||||
SkipHookPrefixNotConfigured: "您尚未配置用于跳过钩子的提交消息前缀. 在您的配置中设置`git.skipHookPrefix ='WIP'`",
|
||||
LcResetTo: `重置为`,
|
||||
PressEnterToReturn: "按Enter键返回lazygit",
|
||||
LcViewStashOptions: "查看隐藏选项",
|
||||
LcStashAllChanges: "stash更改",
|
||||
LcStashStagedChanges: "stash的staged更改",
|
||||
LcStashOptions: "Stash选项",
|
||||
NotARepository: "错误: 必须在git仓库中运行",
|
||||
LcJump: "跳到面板",
|
||||
DiscardPatch: "丢弃补丁",
|
||||
DiscardPatchConfirm: "您一次只能通过一个commit/stash-entry构建补丁. 放弃当前补丁吗?",
|
||||
CantPatchWhileRebasingError: "处于合并或变基状态时,您无法构建修补程序或运行修补程序命令",
|
||||
LcToggleAddToPatch: "补丁中包含的切换文件",
|
||||
ViewPatchOptions: "查看自定义补丁选项",
|
||||
PatchOptionsTitle: "补丁选项",
|
||||
NoPatchError: "尚未创建补丁. 开始构建补丁, 在提交文件上使用'空格‘或输入以添加特定行",
|
||||
LcEnterFile: "输入文件以将所选行添加到补丁中(或切换目录折叠)",
|
||||
ExitLineByLineMode: `退出逐行模式`,
|
||||
EnterUpstream: `输入上游作为 '<remote> <branchname>'`,
|
||||
EnterUpstreamWithSlash: `输入上游作为 '<remote>/<branchname>'`,
|
||||
LcNotTrackingRemote: "(不跟踪任何远程)",
|
||||
ReturnToRemotesList: `返回远程列表`,
|
||||
LcAddNewRemote: `添加新的远程`,
|
||||
LcNewRemoteName: `新的远程名称:`,
|
||||
LcNewRemoteUrl: `新的远程URL:`,
|
||||
LcEditRemoteName: `输入{{.remoteName}}更新远程名称:`,
|
||||
LcEditRemoteUrl: `输入{{.remoteName}}更新远程URL:`,
|
||||
LcRemoveRemote: `删除远程`,
|
||||
LcRemoveRemotePrompt: "您确定要删除远程",
|
||||
DeleteRemoteBranch: "删除远程分支",
|
||||
DeleteRemoteBranchMessage: "确定要删除远程分支",
|
||||
LcSetUpstream: "设置为检出分支的上游",
|
||||
SetUpstreamTitle: "设置上游分支",
|
||||
SetUpstreamMessage: "您确定要将'{{.checkedOut}}‘的上游分支设置为'{{.selected}}'吗?",
|
||||
LcEditRemote: "编辑远程",
|
||||
LcTagCommit: "标签提交",
|
||||
TagNameTitle: "标签名:",
|
||||
LcDeleteTag: "删除标签",
|
||||
DeleteTagTitle: "删除标签",
|
||||
DeleteTagPrompt: "您确定要删除标签'{{.tagName}}'吗?",
|
||||
PushTagTitle: "远程将标签'{{.tagName}}'推送到:",
|
||||
LcPushTag: "推送标签",
|
||||
LcCreateTag: "创建标签",
|
||||
CreateTagTitle: "标签名:",
|
||||
LcFetchRemote: "获取远程",
|
||||
FetchingRemoteStatus: "获取远程",
|
||||
LcCheckoutCommit: "签出提交",
|
||||
SureCheckoutThisCommit: "您确定要签出此提交吗??",
|
||||
LcGitFlowOptions: "显示git-flow选项",
|
||||
NotAGitFlowBranch: "这似乎不是git flow分支",
|
||||
NewGitFlowBranchPrompt: "新的{{.branchType}}名称:",
|
||||
IgnoreTracked: "忽略跟踪文件",
|
||||
IgnoreTrackedPrompt: "您确定要忽略跟踪的文件吗?",
|
||||
LcViewResetToUpstreamOptions: "查看上游重置选项",
|
||||
LcNextScreenMode: "下一屏模式(正常/半屏/全屏)",
|
||||
LcPrevScreenMode: "上一屏模式",
|
||||
LcStartSearch: "开始搜索",
|
||||
Panel: "面板",
|
||||
Keybindings: "键绑定",
|
||||
LcRenameBranch: "重命名分支",
|
||||
NewBranchNamePrompt: "输入分支的新分支名称",
|
||||
RenameBranchWarning: "该分支正在跟踪远程服务器。此操作将仅重命名本地分支名称,而不重命名远程分支的名称。继续?",
|
||||
LcOpenMenu: "打开菜单",
|
||||
LcCloseMenu: "关闭菜单",
|
||||
LcResetCherryPick: "重置 cherry-picked(复制)提交选择",
|
||||
LcNextTab: "下一个标签",
|
||||
LcPrevTab: "上一个标签",
|
||||
LcCantUndoWhileRebasing: "进行基础调整时无法撤消",
|
||||
LcCantRedoWhileRebasing: "变基时无法重做",
|
||||
MustStashWarning: "将补丁拉出到索引中需要存储和取消存储所做的更改。如果出现问题,您将可以从存储中访问文件。继续?",
|
||||
MustStashTitle: "必须stash",
|
||||
ConfirmationTitle: "确认面板",
|
||||
LcPrevPage: "上一页",
|
||||
LcNextPage: "下一页",
|
||||
LcGotoTop: "滚动到顶部",
|
||||
LcGotoBottom: "滚动到底部",
|
||||
LcFilteringBy: "过滤依据",
|
||||
ResetInParentheses: "(重置)",
|
||||
LcOpenFilteringMenu: "查看按路径过滤选项",
|
||||
LcFilterBy: "过滤",
|
||||
LcExitFilterMode: "停止按路径过滤",
|
||||
LcFilterPathOption: "输入要过滤的路径",
|
||||
LcEnterFileName: "输入路径:",
|
||||
FilteringMenuTitle: "正在过滤",
|
||||
MustExitFilterModeTitle: "命令不可用",
|
||||
MustExitFilterModePrompt: "命令在过滤模式下不可用。退出过滤模式?",
|
||||
LcDiff: "差异(diff)",
|
||||
LcEnterRefToDiff: "输入ref到diff",
|
||||
LcEnteRefName: "输入ref:",
|
||||
LcExitDiffMode: "退出差异模式",
|
||||
DiffingMenuTitle: "差异化",
|
||||
LcSwapDiff: "反向差异方向",
|
||||
LcOpenDiffingMenu: "打开差异菜单",
|
||||
// the actual view is the extras view which I intend to give more tabs in future but for now we'll only mention the command log part
|
||||
LcOpenExtrasMenu: "打开命令日志菜单",
|
||||
LcShowingGitDiff: "显示输出:",
|
||||
LcCopyCommitShaToClipboard: "将提交SHA复制到剪贴板",
|
||||
LcCopyCommitMessageToClipboard: "将提交消息复制到剪贴板",
|
||||
LcCopyBranchNameToClipboard: "将分支名称复制到剪贴板",
|
||||
LcCopyFileNameToClipboard: "将文件名复制到剪贴板",
|
||||
LcCopyCommitFileNameToClipboard: "将提交的文件名复制到剪贴板",
|
||||
LcCommitPrefixPatternError: "提交前缀模式错误",
|
||||
NoFilesStagedTitle: "没有暂存文件",
|
||||
NoFilesStagedPrompt: "您尚未暂存任何文件。提交所有文件?",
|
||||
BranchNotFoundTitle: "找不到分支",
|
||||
BranchNotFoundPrompt: "找不到分支。创建一个新分支命名为",
|
||||
UnstageLinesTitle: "Unstage行",
|
||||
UnstageLinesPrompt: "您确定要删除所选的行(git reset)吗?这是不可逆的。\n要禁用此对话框,请将'gui.skipUnstageLineWarning'的配置键设置为true",
|
||||
LcCreateNewBranchFromCommit: "从提交创建新分支",
|
||||
LcViewStashFiles: "查看stash条目的文件",
|
||||
LcBuildingPatch: "构建补丁",
|
||||
LcViewCommits: "查看提交",
|
||||
MinGitVersionError: "Git版本必须至少为2.0(即从2014年开始)。请升级您的git版本。或者在https://github.com/jesseduffield/lazygit/issues上提出一个问题,以使lazygit更加向后兼容。",
|
||||
LcRunningCustomCommandStatus: "运行自定义命令",
|
||||
LcSubmoduleStashAndReset: "存放未提交的子模块更改和更新",
|
||||
LcAndResetSubmodules: "和重置子模块",
|
||||
LcEnterSubmodule: "输入子模块",
|
||||
LcCopySubmoduleNameToClipboard: "将子模块名称复制到剪贴板",
|
||||
RemoveSubmodule: "删除子模块",
|
||||
LcRemoveSubmodule: "删除子模块",
|
||||
RemoveSubmodulePrompt: "您确定要删除子模块'%s'及其对应的目录吗?这是不可逆的。",
|
||||
LcResettingSubmoduleStatus: "重置子模块",
|
||||
LcNewSubmoduleName: "新的子模块名称:",
|
||||
LcNewSubmoduleUrl: "新的子模块网址:",
|
||||
LcNewSubmodulePath: "新的子模块路径:",
|
||||
LcAddSubmodule: "添加新的子模块",
|
||||
LcAddingSubmoduleStatus: "添加子模块",
|
||||
LcUpdateSubmoduleUrl: "更新子模块'%s'的URL",
|
||||
LcUpdatingSubmoduleUrlStatus: "更新URL",
|
||||
LcEditSubmoduleUrl: "更新子模块URL",
|
||||
LcInitializingSubmoduleStatus: "初始化子模块",
|
||||
LcInitSubmodule: "初始化子模块",
|
||||
LcViewResetAndRemoveOptions: "查看重置和删除子模块选项",
|
||||
LcSubmoduleUpdate: "更新子模块",
|
||||
LcUpdatingSubmoduleStatus: "更新子模块",
|
||||
LcBulkInitSubmodules: "批量初始化子模块",
|
||||
LcBulkUpdateSubmodules: "批量更新子模块",
|
||||
LcBulkDeinitSubmodules: "批量deinit子模块",
|
||||
LcViewBulkSubmoduleOptions: "查看批量子模块选项",
|
||||
LcBulkSubmoduleOptions: "批量子模块选项",
|
||||
LcRunningCommand: "运行命令",
|
||||
SubCommitsTitle: "子提交",
|
||||
SubmodulesTitle: "子模块",
|
||||
NavigationTitle: "列表面板导航",
|
||||
SuggestionsTitle: "意见建议",
|
||||
PushingTagStatus: "推送标签",
|
||||
PullRequestURLCopiedToClipboard: "拉取请求网址已复制到剪贴板",
|
||||
CommitMessageCopiedToClipboard: "提交消息复制到剪贴板",
|
||||
LcCopiedToClipboard: "复制到剪贴板",
|
||||
ErrCannotEditDirectory: "无法编辑目录:您只能编辑单个文件",
|
||||
ErrStageDirWithInlineMergeConflicts: "无法 暂存/取消暂存 包含具有内联合并冲突的文件的目录。请先解决合并冲突",
|
||||
ErrRepositoryMovedOrDeleted: "找不到仓库。它可能已被移动或删除¯\\ _(ツ)_ /¯ ¯\\_(ツ)_/¯",
|
||||
CommandLog: "命令日志",
|
||||
ToggleShowCommandLog: "切换 显示/隐藏 命令日志",
|
||||
FocusCommandLog: "焦点命令日志",
|
||||
CommandLogHeader: "您可以通过按'%s'隐藏或集中显示该面板,或使用 `gui.showCommandLog: false`\n将其永久隐藏在您的配置中",
|
||||
RandomTip: "随机提示",
|
||||
Spans: Spans{
|
||||
// TODO: combine this with the original keybinding descriptions (those are all in lowercase atm)
|
||||
CheckoutCommit: "检出提交",
|
||||
CheckoutReflogCommit: "检出reflog提交",
|
||||
CheckoutTag: "检出标签",
|
||||
CheckoutBranch: "检出分支",
|
||||
ForceCheckoutBranch: "强制检出分支",
|
||||
DeleteBranch: "删除分支",
|
||||
Merge: "合并",
|
||||
RebaseBranch: "变基分支",
|
||||
RenameBranch: "重命名分支",
|
||||
CreateBranch: "建立分支",
|
||||
CherryPick: "(Cherry-pick) 粘贴提交",
|
||||
CheckoutFile: "检出文件",
|
||||
DiscardOldFileChange: "放弃旧文件更改",
|
||||
SquashCommitDown: "向下聚合提交",
|
||||
FixupCommit: "修正提交",
|
||||
RewordCommit: "改写提交",
|
||||
DropCommit: "删除提交",
|
||||
EditCommit: "编辑提交",
|
||||
AmendCommit: "修改提交",
|
||||
RevertCommit: "还原提交",
|
||||
CreateFixupCommit: "创建修正提交",
|
||||
SquashAllAboveFixupCommits: "聚合所有以上的修正提交",
|
||||
CreateLightweightTag: "创建轻量级标签",
|
||||
CopyCommitMessageToClipboard: "将提交消息复制到剪贴板",
|
||||
MoveCommitUp: "向上提交",
|
||||
MoveCommitDown: "下移提交",
|
||||
CustomCommand: "自定义命令",
|
||||
DiscardAllChangesInDirectory: "放弃目录中的所有更改",
|
||||
DiscardUnstagedChangesInDirectory: "放弃目录中未暂存的更改",
|
||||
DiscardAllChangesInFile: "放弃文件中的所有更改",
|
||||
DiscardAllUnstagedChangesInFile: "丢弃文件中所有未暂存的更改",
|
||||
StageFile: "暂存文件",
|
||||
UnstageFile: "未暂存文件",
|
||||
UnstageAllFiles: "取消暂存所有文件",
|
||||
StageAllFiles: "暂存所有文件",
|
||||
IgnoreFile: "忽略文件",
|
||||
Commit: "提交(Commit)",
|
||||
EditFile: "编辑文件",
|
||||
Push: "推送(Push)",
|
||||
Pull: "拉取(Pull)",
|
||||
OpenFile: "打开文件",
|
||||
StashAllChanges: "Stash所有更改",
|
||||
StashStagedChanges: "Stash暂存更改",
|
||||
GitFlowFinish: "Git流完成",
|
||||
GitFlowStart: "Git Flow开始",
|
||||
CopyToClipboard: "复制到剪贴板",
|
||||
RemovePatchFromCommit: "从提交中删除补丁",
|
||||
MovePatchToSelectedCommit: "将补丁移动到选定的提交",
|
||||
MovePatchIntoIndex: "将补丁移到索引",
|
||||
MovePatchIntoNewCommit: "将补丁移到新提交中",
|
||||
DeleteRemoteBranch: "删除远程分支",
|
||||
SetBranchUpstream: "设置分支上游",
|
||||
AddRemote: "添加远程",
|
||||
RemoveRemote: "移除远程",
|
||||
UpdateRemote: "远程更新",
|
||||
ApplyPatch: "套用补丁",
|
||||
Stash: "Stash",
|
||||
RemoveSubmodule: "删除子模块",
|
||||
ResetSubmodule: "重置子模块",
|
||||
AddSubmodule: "添加子模块",
|
||||
UpdateSubmoduleUrl: "更新子模块URL",
|
||||
InitialiseSubmodule: "初始化子模块",
|
||||
BulkInitialiseSubmodules: "批量初始化子模块",
|
||||
BulkUpdateSubmodules: "批量更新子模块",
|
||||
BulkStashAndResetSubmodules: "批量存储和重置子模块",
|
||||
BulkDeinitialiseSubmodules: "批量取消初始化子模块",
|
||||
UpdateSubmodule: "更新子模块",
|
||||
DeleteTag: "删除标签",
|
||||
PushTag: "推送标签",
|
||||
NukeWorkingTree: "Nuke工作树",
|
||||
DiscardUnstagedFileChanges: "放弃未暂存的文件更改",
|
||||
RemoveUntrackedFiles: "删除未跟踪的文件",
|
||||
SoftReset: "软重置",
|
||||
MixedReset: "混合重置",
|
||||
HardReset: "硬重置",
|
||||
FastForwardBranch: "快进分支",
|
||||
Undo: "撤销",
|
||||
Redo: "重做",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -10,17 +10,17 @@ func dutchTranslationSet() TranslationSet {
|
||||
CommitsTitle: "Commits",
|
||||
StashTitle: "Stash",
|
||||
UnstagedChanges: `Unstaged wijzigingen`,
|
||||
StagedChanges: `Staged Wijzigingen`,
|
||||
PatchBuildingMainTitle: `Voeg lijnen/hunks toe aan Patch`,
|
||||
StagedChanges: `Staged wijzigingen`,
|
||||
PatchBuildingMainTitle: `Voeg lijnen/hunks toe aan patch`,
|
||||
MergingMainTitle: "Los merge conflicten op",
|
||||
MainTitle: "Hooft",
|
||||
MainTitle: "Hoofd",
|
||||
StagingTitle: "Staging",
|
||||
NormalTitle: "Normaal",
|
||||
CommitMessage: "Commit bericht",
|
||||
CommitMessage: "Commitbericht",
|
||||
CredentialsUsername: "Gebruikersnaam",
|
||||
CredentialsPassword: "Wachtwoord",
|
||||
CredentialsPassphrase: "Voer een wachtwoordzin in voor de SSH-sleutel",
|
||||
PassUnameWrong: "Wachtwoord en/of gebruikersnaam verkeert",
|
||||
PassUnameWrong: "Wachtwoord en/of gebruikersnaam verkeerd",
|
||||
CommitChanges: "Commit veranderingen",
|
||||
AmendLastCommit: "wijzig laatste commit",
|
||||
SureToAmend: "Weet je zeker dat je de laatste commit wilt wijzigen? U kunt het commit-bericht wijzigen vanuit het commits-paneel.",
|
||||
@@ -152,9 +152,9 @@ func dutchTranslationSet() TranslationSet {
|
||||
LcMergeIntoCurrentBranch: `merge in met huidige checked out branch`,
|
||||
ConfirmQuit: `Weet je zeker dat je dit programma wil sluiten?`,
|
||||
SwitchRepo: "wissel naar een recente repo",
|
||||
LcAllBranchesLogGraph: `alle takken van het houtblok laten zien`,
|
||||
LcAllBranchesLogGraph: `alle logs van de branch laten zien`,
|
||||
UnsupportedGitService: `Niet-ondersteunde git-service`,
|
||||
LcCreatePullRequest: `maak een pull-aanvraag`,
|
||||
LcCreatePullRequest: `maak een pull-request`,
|
||||
LcCopyPullRequestURL: `kopieer de URL van het pull-verzoek naar het klembord`,
|
||||
NoBranchOnRemote: `Deze branch bestaat niet op de remote. U moet het eerst naar de remote pushen.`,
|
||||
LcFetch: `fetch`,
|
||||
@@ -165,8 +165,8 @@ func dutchTranslationSet() TranslationSet {
|
||||
SelectHunk: `selecteer hunk`,
|
||||
StageSelection: `toggle lijnen staged / unstaged`,
|
||||
ResetSelection: `verwijdert change (git reset)`,
|
||||
ToggleDragSelect: `toggle drag selecteer`,
|
||||
ToggleSelectHunk: `toggle selecteer hunk`,
|
||||
ToggleDragSelect: `toggle drag selecteer`,
|
||||
ToggleSelectHunk: `toggle selecteer hunk`,
|
||||
ToggleSelectionForPatch: `voeg toe/verwijder lijn(en) in patch`,
|
||||
TogglePanel: `ga naar een ander paneel`,
|
||||
CantStageStaged: `Je kan niet al gestaged verandering stagen!`,
|
||||
@@ -186,23 +186,23 @@ func dutchTranslationSet() TranslationSet {
|
||||
MergeOptionsTitle: "Merge Opties",
|
||||
RebaseOptionsTitle: "Rebase Opties",
|
||||
CommitMessageTitle: "Commit Bericht",
|
||||
LocalBranchesTitle: "Branches Tab",
|
||||
LocalBranchesTitle: "Branches Tabblad",
|
||||
SearchTitle: "Zoek",
|
||||
TagsTitle: "Tags Tab",
|
||||
BranchCommitsTitle: "Commits Tab",
|
||||
TagsTitle: "Tags Tabblad",
|
||||
BranchCommitsTitle: "Commits Tabblad",
|
||||
MenuTitle: "Menu",
|
||||
RemotesTitle: "Remotes Tab",
|
||||
RemotesTitle: "Remotes Tabblad",
|
||||
CredentialsTitle: "Credentials",
|
||||
RemoteBranchesTitle: "Remote Branches (in Remotes tab)",
|
||||
RemoteBranchesTitle: "Remote Branches (in Remotes tabblad)",
|
||||
PatchBuildingTitle: "Patch Bouwen",
|
||||
InformationTitle: "Informatie",
|
||||
SecondaryTitle: "Secondary",
|
||||
ReflogCommitsTitle: "Reflog Tab",
|
||||
ReflogCommitsTitle: "Reflog Tabblad",
|
||||
Title: "Title",
|
||||
GlobalTitle: "Globaale Sneltoetsen",
|
||||
GlobalTitle: "Globale Sneltoetsen",
|
||||
ConflictsResolved: "alle merge conflicten zijn opgelost. Wilt je verder gaan?",
|
||||
RebasingTitle: "Rebasen",
|
||||
MergingTitle: "Merggen",
|
||||
MergingTitle: "Mergen",
|
||||
ConfirmRebase: "Weet je zeker dat je {{.checkedOutBranch}} op {{.selectedBranch}} wil rebasen?",
|
||||
ConfirmMerge: "Weet je zeker dat je {{.selectedBranch}} in {{.checkedOutBranch}} wil mergen?",
|
||||
FwdNoUpstream: "Kan niet de branch vooruitspoelen zonder upstream",
|
||||
@@ -211,8 +211,8 @@ func dutchTranslationSet() TranslationSet {
|
||||
NoRoom: "Niet genoeg ruimte",
|
||||
YouAreHere: "JE BENT HIER",
|
||||
LcRewordNotSupported: "herformatteren van commits in interactief rebasen is nog niet ondersteund",
|
||||
LcCherryPickCopy: "kopiëer commit (cherry-pick)",
|
||||
LcCherryPickCopyRange: "kopiëer commit reeks (cherry-pick)",
|
||||
LcCherryPickCopy: "kopieer commit (cherry-pick)",
|
||||
LcCherryPickCopyRange: "kopieer commit reeks (cherry-pick)",
|
||||
LcPasteCommits: "plak commits (cherry-pick)",
|
||||
SureCherryPick: "Weet je zeker dat je de gekopieerde commits naar deze branch wil cherry-picken?",
|
||||
CherryPick: "Cherry-Pick",
|
||||
@@ -229,8 +229,8 @@ func dutchTranslationSet() TranslationSet {
|
||||
SelectBottom: "selecteer onderste hunk",
|
||||
ScrollDown: "scroll omlaag",
|
||||
ScrollUp: "scroll omhoog",
|
||||
LcScrollUpMainPanel: "scroll naar beneden vanaf hooft paneel",
|
||||
LcScrollDownMainPanel: "scroll naar beneden vabaf hooft paneel",
|
||||
LcScrollUpMainPanel: "scroll naar beneden vanaf hoofdpaneel",
|
||||
LcScrollDownMainPanel: "scroll naar beneden vanaf hoofdpaneel",
|
||||
AmendCommitTitle: "Commit wijzigen",
|
||||
AmendCommitPrompt: "Weet je zeker dat je deze commit wil wijzigen met de vorige staged bestanden?",
|
||||
DeleteCommitTitle: "Verwijder Commit",
|
||||
@@ -257,14 +257,14 @@ func dutchTranslationSet() TranslationSet {
|
||||
DisabledForGPG: "Onderdelen niet beschikbaar voor gebruikers die GPG gebruiken",
|
||||
CreateRepo: "Niet in een git repository. Creëer een nieuwe git repository? (y/n): ",
|
||||
AutoStashTitle: "Autostash?",
|
||||
AutoStashPrompt: "Je moet je veranderingen stashen en poppen om ze over te bregen. Dit automatisch doen? (enter/esc)",
|
||||
AutoStashPrompt: "Je moet je veranderingen stashen en poppen om ze over te brengen. Dit automatisch doen? (enter/esc)",
|
||||
StashPrefix: "Auto-stashing veranderingen voor ",
|
||||
LcViewDiscardOptions: "bekijk 'veranderingen ongedaan maken' opties",
|
||||
LcCancel: "anuleren",
|
||||
LcCancel: "annuleren",
|
||||
LcDiscardAllChanges: "negeer alle wijzigingen",
|
||||
LcDiscardUnstagedChanges: "negeer unstaged wijzigingen",
|
||||
LcDiscardAllChangesToAllFiles: "verwijder werkende tree",
|
||||
LcDiscardAnyUnstagedChanges: "discard unstaged wijzigingen",
|
||||
LcDiscardAnyUnstagedChanges: "gooi unstaged wijzigingen weg",
|
||||
LcDiscardUntrackedFiles: "negeer niet-gevonden bestanden",
|
||||
LcViewResetOptions: `bekijk reset opties`,
|
||||
LcHardReset: "harde reset",
|
||||
@@ -275,8 +275,8 @@ func dutchTranslationSet() TranslationSet {
|
||||
SureSquashAboveCommits: `Weet je zeker dat je alles wil squash/fixup! voor de bovenstaand commits {{.commit}}?`,
|
||||
CreateFixupCommit: `Creëer fixup commit`,
|
||||
SureCreateFixupCommit: `Weet je zeker dat je een fixup wil maken! commit voor commit {{.commit}}?`,
|
||||
LcExecuteCustomCommand: "voor aangepast commando uit",
|
||||
CustomCommand: "Aangepast commando:",
|
||||
LcExecuteCustomCommand: "voor aangepaste commando uit",
|
||||
CustomCommand: "Aangepaste commando:",
|
||||
LcCommitChangesWithoutHook: "commit veranderingen zonder pre-commit hook",
|
||||
SkipHookPrefixNotConfigured: "Je hebt nog niet een commit bericht voorvoegsel ingesteld voor het overslaan van hooks. Set `git.skipHookPrefix = 'WIP'` in je config",
|
||||
LcResetTo: `reset naar`,
|
||||
@@ -285,17 +285,17 @@ func dutchTranslationSet() TranslationSet {
|
||||
LcStashAllChanges: "stash-bestanden",
|
||||
LcStashStagedChanges: "stash staged wijzigingen",
|
||||
LcStashOptions: "Stash opties",
|
||||
NotARepository: "Fout: must be run inside a git repository",
|
||||
NotARepository: "Fout: moet in een git repository uitgevoerd worden",
|
||||
LcJump: "ga naar paneel",
|
||||
DiscardPatch: "Patch weg gooien",
|
||||
DiscardPatchConfirm: "Je kan alleen maar een patch bouwen van 1 commit. actueel patch weg gooien?",
|
||||
DiscardPatchConfirm: "Je kan alleen maar een patch bouwen van 1 commit. Huidige patch weggooien?",
|
||||
CantPatchWhileRebasingError: "Je kan geen patch bouwen of patch commando uitvoeren wanneer je in een merging of rebasing state zit",
|
||||
LcToggleAddToPatch: "toggle bestand inbegrepen in patch",
|
||||
ViewPatchOptions: "bekijk aangepaste patch opties",
|
||||
PatchOptionsTitle: "Patch Opties",
|
||||
NoPatchError: "Nog geen patch gecreëerd. Om een patch te bouwen gebruik 'space' op een commit bestand of 'enter' om een spesiefieke lijnen toe te voegen",
|
||||
LcEnterFile: "enter bestand to add selecteered lines to the patch",
|
||||
ExitLineByLineMode: `sluit lijn-bij-lijn mode`,
|
||||
LcEnterFile: "enter bestand om geselecteerde regels toe te voegen aan de patch",
|
||||
ExitLineByLineMode: `sluit lijn-bij-lijn modus`,
|
||||
EnterUpstream: `Enter upstream als '<remote> <branchnaam>'`,
|
||||
EnterUpstreamWithSlash: `Enter upstream als '<remote>/<branchnaam>'`,
|
||||
LcNotTrackingRemote: "(nog geen remote aan het volgen)",
|
||||
@@ -330,11 +330,11 @@ func dutchTranslationSet() TranslationSet {
|
||||
NotAGitFlowBranch: "Dit lijkt geen git flow branch te zijn",
|
||||
NewGitFlowBranchPrompt: "nieuwe {{.branchType}} naam:",
|
||||
IgnoreTracked: "Negeer tracked bestand",
|
||||
IgnoreTrackedPrompt: "weet je zeker dat je een getracked bestand wil negeeren?",
|
||||
IgnoreTrackedPrompt: "weet je zeker dat je een getracked bestand wil negeren?",
|
||||
LcViewResetToUpstreamOptions: "bekijk upstream reset opties",
|
||||
LcNextScreenMode: "volgende schermmode (normaal/half/groot )",
|
||||
LcPrevScreenMode: "vorige schermmode",
|
||||
LcStartSearch: "start met zoekken",
|
||||
LcNextScreenMode: "volgende scherm modus (normaal/half/groot)",
|
||||
LcPrevScreenMode: "vorige scherm modus",
|
||||
LcStartSearch: "start met zoeken",
|
||||
Panel: "Paneel",
|
||||
Keybindings: "Sneltoetsen",
|
||||
LcRenameBranch: "hernoem branch",
|
||||
@@ -342,14 +342,14 @@ func dutchTranslationSet() TranslationSet {
|
||||
RenameBranchWarning: "Deze branch volgt een remote. Deze actie zal alleen de locale branch name wijzigen niet de naam van de remote branch. Verder gaan?",
|
||||
LcOpenMenu: "open menu",
|
||||
LcCloseMenu: "sluit menu",
|
||||
LcResetCherryPick: "reset cherry-picked (gecopieerde) commits selectie",
|
||||
LcNextTab: "volgende tab",
|
||||
LcPrevTab: "vorige tab",
|
||||
LcResetCherryPick: "reset cherry-picked (gekopieerde) commits selectie",
|
||||
LcNextTab: "volgende tabblad",
|
||||
LcPrevTab: "vorige tabblad",
|
||||
LcCantUndoWhileRebasing: "Kan niet ongedaan maken terwijl je aan het rebasen bent",
|
||||
LcCantRedoWhileRebasing: "Kan niet opnieuw doen (redo) terwijl je aan het rebasen bent",
|
||||
MustStashWarning: "Een patch in de index stoppen verijst stashen en onstashen van je wijzigingen. Als iets verkeert gaat kan je je bestanden terug vinden in de stash. Verder gaan?",
|
||||
MustStashWarning: "Een patch in de index stoppen vereist stashen en onstashen van je wijzigingen. Als er iets verkeert gaat kan je je bestanden terug vinden in de stash. Verder gaan?",
|
||||
MustStashTitle: "Moet stashen",
|
||||
ConfirmationTitle: "Bevestigings Paneel",
|
||||
ConfirmationTitle: "Bevestigingspaneel",
|
||||
LcPrevPage: "vorige pagina",
|
||||
LcNextPage: "volgende pagina",
|
||||
LcGotoTop: "scroll naar boven",
|
||||
@@ -363,18 +363,18 @@ func dutchTranslationSet() TranslationSet {
|
||||
LcEnterFileName: "vulin path:",
|
||||
FilteringMenuTitle: "Filteren",
|
||||
MustExitFilterModeTitle: "Command niet beschikbaar",
|
||||
MustExitFilterModePrompt: "Command niet beschikbaar in filter mode. Sluit filter mode?",
|
||||
MustExitFilterModePrompt: "Command niet beschikbaar in filter modus. Sluit filter modus?",
|
||||
LcDiff: "diff",
|
||||
LcEnterRefToDiff: "vulin ref to diff",
|
||||
LcEnteRefName: "vulin ref:",
|
||||
LcEnterRefToDiff: "vul in ref naar diff",
|
||||
LcEnteRefName: "vul in ref:",
|
||||
LcExitDiffMode: "sluit diff mode",
|
||||
DiffingMenuTitle: "Diffen",
|
||||
LcSwapDiff: "keer diff richting om",
|
||||
LcOpenDiffingMenu: "open diff menu",
|
||||
LcShowingGitDiff: "laat output zien voor:",
|
||||
LcCopyCommitShaToClipboard: "copieer commit SHA naar clipboard",
|
||||
LcCopyCommitMessageToClipboard: "copieer commit bericht naar clipboard",
|
||||
LcCopyBranchNameToClipboard: "copieer branch name naar clipboard",
|
||||
LcCopyCommitShaToClipboard: "kopieer commit SHA naar klembord",
|
||||
LcCopyCommitMessageToClipboard: "kopieer commit bericht naar klembord",
|
||||
LcCopyBranchNameToClipboard: "kopieer branch name naar klembord",
|
||||
LcCopyFileNameToClipboard: "kopieer de bestandsnaam naar het klembord",
|
||||
LcCopyCommitFileNameToClipboard: "kopieer de vastgelegde bestandsnaam naar het klembord",
|
||||
LcCommitPrefixPatternError: "Fout in commitPrefix patroon",
|
||||
@@ -385,5 +385,18 @@ func dutchTranslationSet() TranslationSet {
|
||||
PullRequestURLCopiedToClipboard: "Pull-aanvraag-URL gekopieerd naar klembord",
|
||||
CommitMessageCopiedToClipboard: "Commit message gekopieerd naar klembord",
|
||||
LcCopiedToClipboard: "gekopieerd naar klembord",
|
||||
NavigationTitle: "Lijstpaneel Navigatie",
|
||||
LcViewCommits: "bekijk commits",
|
||||
LcToggleTreeView: "toggle bestandsboom weergave",
|
||||
LcCreateNewBranchFromCommit: "creëer nieuwe branch van commit",
|
||||
LcCopySubmoduleNameToClipboard: "kopieer submodule naam naar klembord",
|
||||
LcEnterSubmodule: "enter submodule",
|
||||
LcViewResetAndRemoveOptions: "bekijk reset en verwijder submodule opties",
|
||||
LcAddSubmodule: "voeg nieuwe submodule toe",
|
||||
LcInitSubmodule: "initialiseer submodule",
|
||||
LcViewBulkSubmoduleOptions: "bekijk bulk submodule opties",
|
||||
LcViewStashFiles: "bekijk bestanden van stash entry",
|
||||
CreatePullRequestOptions: "Bekijk opties voor pull-aanvraag",
|
||||
LcCreatePullRequestOptions: "bekijk opties voor pull-aanvraag",
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user