mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-12 04:45:47 +02:00
Merge branch 'master' into #2319_default_screen_mode
This commit is contained in:
commit
01f0efb997
25 changed files with 453 additions and 158 deletions
89
.github/workflows/uffizzi-build.yml
vendored
Normal file
89
.github/workflows/uffizzi-build.yml
vendored
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
name: Build PR Image
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, closed]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-application:
|
||||||
|
name: Build and Push `lazygit`
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }}
|
||||||
|
outputs:
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout git repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: Generate UUID image name
|
||||||
|
id: uuid
|
||||||
|
run: echo "UUID_APP_TAG=$(uuidgen)" >> $GITHUB_ENV
|
||||||
|
- name: Docker metadata
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v3
|
||||||
|
with:
|
||||||
|
images: registry.uffizzi.com/${{ env.UUID_APP_TAG }}
|
||||||
|
tags: type=raw,value=60d
|
||||||
|
- name: Build and Push Image to registry.uffizzi.com ephemeral registry
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
context: ./
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
file: ./uffizzi/DockerfileTtyd
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
||||||
|
|
||||||
|
render-compose-file:
|
||||||
|
name: Render Docker Compose File
|
||||||
|
# Pass output of this workflow to another triggered by `workflow_run` event.
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs:
|
||||||
|
- build-application
|
||||||
|
outputs:
|
||||||
|
compose-file-cache-key: ${{ steps.hash.outputs.hash }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout git repo
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Render Compose File
|
||||||
|
run: |
|
||||||
|
APP_IMAGE=$(echo ${{ needs.build-application.outputs.tags }})
|
||||||
|
export APP_IMAGE
|
||||||
|
# Render simple template from environment variables.
|
||||||
|
envsubst < ./uffizzi/docker-compose.uffizzi.yml > docker-compose.rendered.yml
|
||||||
|
cat docker-compose.rendered.yml
|
||||||
|
- name: Upload Rendered Compose File as Artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: preview-spec
|
||||||
|
path: docker-compose.rendered.yml
|
||||||
|
retention-days: 2
|
||||||
|
- name: Serialize PR Event to File
|
||||||
|
run: |
|
||||||
|
cat << EOF > event.json
|
||||||
|
${{ toJSON(github.event) }}
|
||||||
|
|
||||||
|
EOF
|
||||||
|
- name: Upload PR Event as Artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: preview-spec
|
||||||
|
path: event.json
|
||||||
|
retention-days: 2
|
||||||
|
|
||||||
|
delete-preview:
|
||||||
|
name: Call for Preview Deletion
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ github.event.action == 'closed' }}
|
||||||
|
steps:
|
||||||
|
# If this PR is closing, we will not render a compose file nor pass it to the next workflow.
|
||||||
|
- name: Serialize PR Event to File
|
||||||
|
run: echo '${{ toJSON(github.event) }}' > event.json
|
||||||
|
- name: Upload PR Event as Artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: preview-spec
|
||||||
|
path: event.json
|
||||||
|
retention-days: 2
|
84
.github/workflows/uffizzi-preview.yml
vendored
Normal file
84
.github/workflows/uffizzi-preview.yml
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
name: Deploy Uffizzi Preview
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows:
|
||||||
|
- "Build PR Image"
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
cache-compose-file:
|
||||||
|
name: Cache Compose File
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
compose-file-cache-key: ${{ env.COMPOSE_FILE_HASH }}
|
||||||
|
pr-number: ${{ env.PR_NUMBER }}
|
||||||
|
steps:
|
||||||
|
- name: 'Download artifacts'
|
||||||
|
# Fetch output (zip archive) from the workflow run that triggered this workflow.
|
||||||
|
uses: actions/github-script@v6
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
run_id: context.payload.workflow_run.id,
|
||||||
|
});
|
||||||
|
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
|
||||||
|
return artifact.name == "preview-spec"
|
||||||
|
})[0];
|
||||||
|
let download = await github.rest.actions.downloadArtifact({
|
||||||
|
owner: context.repo.owner,
|
||||||
|
repo: context.repo.repo,
|
||||||
|
artifact_id: matchArtifact.id,
|
||||||
|
archive_format: 'zip',
|
||||||
|
});
|
||||||
|
let fs = require('fs');
|
||||||
|
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/preview-spec.zip`, Buffer.from(download.data));
|
||||||
|
- name: 'Unzip artifact'
|
||||||
|
run: unzip preview-spec.zip
|
||||||
|
- name: Read Event into ENV
|
||||||
|
run: |
|
||||||
|
echo 'EVENT_JSON<<EOF' >> $GITHUB_ENV
|
||||||
|
cat event.json >> $GITHUB_ENV
|
||||||
|
echo 'EOF' >> $GITHUB_ENV
|
||||||
|
- name: Hash Rendered Compose File
|
||||||
|
id: hash
|
||||||
|
# If the previous workflow was triggered by a PR close event, we will not have a compose file artifact.
|
||||||
|
if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }}
|
||||||
|
run: echo "COMPOSE_FILE_HASH=$(md5sum docker-compose.rendered.yml | awk '{ print $1 }')" >> $GITHUB_ENV
|
||||||
|
- name: Cache Rendered Compose File
|
||||||
|
if: ${{ fromJSON(env.EVENT_JSON).action != 'closed' }}
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: docker-compose.rendered.yml
|
||||||
|
key: ${{ env.COMPOSE_FILE_HASH }}
|
||||||
|
|
||||||
|
- name: Read PR Number From Event Object
|
||||||
|
id: pr
|
||||||
|
run: echo "PR_NUMBER=${{ fromJSON(env.EVENT_JSON).number }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: DEBUG - Print Job Outputs
|
||||||
|
if: ${{ runner.debug }}
|
||||||
|
run: |
|
||||||
|
echo "PR number: ${{ env.PR_NUMBER }}"
|
||||||
|
echo "Compose file hash: ${{ env.COMPOSE_FILE_HASH }}"
|
||||||
|
cat event.json
|
||||||
|
deploy-uffizzi-preview:
|
||||||
|
name: Use Remote Workflow to Preview on Uffizzi
|
||||||
|
needs:
|
||||||
|
- cache-compose-file
|
||||||
|
uses: UffizziCloud/preview-action/.github/workflows/reusable.yaml@v2.6.1
|
||||||
|
with:
|
||||||
|
# If this workflow was triggered by a PR close event, cache-key will be an empty string
|
||||||
|
# and this reusable workflow will delete the preview deployment.
|
||||||
|
compose-file-cache-key: ${{ needs.cache-compose-file.outputs.compose-file-cache-key }}
|
||||||
|
compose-file-cache-path: docker-compose.rendered.yml
|
||||||
|
server: https://app.uffizzi.com
|
||||||
|
pr-number: ${{ needs.cache-compose-file.outputs.pr-number }}
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
id-token: write
|
13
README.md
13
README.md
File diff suppressed because one or more lines are too long
|
@ -62,20 +62,21 @@ gui:
|
||||||
showIcons: false
|
showIcons: false
|
||||||
commandLogSize: 8
|
commandLogSize: 8
|
||||||
splitDiff: 'auto' # one of 'auto' | 'always'
|
splitDiff: 'auto' # one of 'auto' | 'always'
|
||||||
|
skipRewordInEditorWarning: false # for skipping the confirmation before launching the reword editor
|
||||||
git:
|
git:
|
||||||
paging:
|
paging:
|
||||||
colorArg: always
|
colorArg: always
|
||||||
useConfig: false
|
useConfig: false
|
||||||
commit:
|
commit:
|
||||||
signOff: false
|
signOff: false
|
||||||
verbose: false
|
verbose: default # one of 'default' | 'always' | 'never'
|
||||||
merging:
|
merging:
|
||||||
# only applicable to unix users
|
# only applicable to unix users
|
||||||
manualCommit: false
|
manualCommit: false
|
||||||
# extra args passed to `git merge`, e.g. --no-ff
|
# extra args passed to `git merge`, e.g. --no-ff
|
||||||
args: ''
|
args: ''
|
||||||
log:
|
log:
|
||||||
# one of date-order, author-date-order, topo-order.
|
# one of date-order, author-date-order, topo-order or default.
|
||||||
# topo-order makes it easier to read the git log graph, but commits may not
|
# topo-order makes it easier to read the git log graph, but commits may not
|
||||||
# appear chronologically. See https://git-scm.com/docs/git-log#_commit_ordering
|
# appear chronologically. See https://git-scm.com/docs/git-log#_commit_ordering
|
||||||
order: 'topo-order'
|
order: 'topo-order'
|
||||||
|
@ -190,6 +191,8 @@ keybinding:
|
||||||
viewResetOptions: 'D'
|
viewResetOptions: 'D'
|
||||||
fetch: 'f'
|
fetch: 'f'
|
||||||
toggleTreeView: '`'
|
toggleTreeView: '`'
|
||||||
|
openMergeTool: 'M'
|
||||||
|
openStatusFilter: '<c-b>'
|
||||||
branches:
|
branches:
|
||||||
createPullRequest: 'o'
|
createPullRequest: 'o'
|
||||||
viewPullRequestOptions: 'O'
|
viewPullRequestOptions: 'O'
|
||||||
|
|
|
@ -2,19 +2,12 @@ package git_commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// this takes something like:
|
|
||||||
// * (HEAD detached at 264fc6f5)
|
|
||||||
// remotes
|
|
||||||
// and returns '264fc6f5' as the second match
|
|
||||||
const CurrentBranchNameRegex = `(?m)^\*.*?([^ ]*?)\)?$`
|
|
||||||
|
|
||||||
type BranchCommands struct {
|
type BranchCommands struct {
|
||||||
*GitCommon
|
*GitCommon
|
||||||
}
|
}
|
||||||
|
@ -41,19 +34,18 @@ func (self *BranchCommands) CurrentBranchInfo() (BranchInfo, error) {
|
||||||
DetachedHead: false,
|
DetachedHead: false,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
output, err := self.cmd.New("git branch --contains").DontLog().RunWithOutput()
|
output, err := self.cmd.New(`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`).DontLog().RunWithOutput()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return BranchInfo{}, err
|
return BranchInfo{}, err
|
||||||
}
|
}
|
||||||
for _, line := range utils.SplitLines(output) {
|
for _, line := range utils.SplitLines(output) {
|
||||||
re := regexp.MustCompile(CurrentBranchNameRegex)
|
split := strings.Split(strings.TrimRight(line, "\r\n"), "\x00")
|
||||||
match := re.FindStringSubmatch(line)
|
if len(split) == 3 && split[0] == "*" {
|
||||||
if len(match) > 0 {
|
sha := split[1]
|
||||||
branchName = match[1]
|
displayName := split[2]
|
||||||
displayBranchName := match[0][2:]
|
|
||||||
return BranchInfo{
|
return BranchInfo{
|
||||||
RefName: branchName,
|
RefName: sha,
|
||||||
DisplayName: displayBranchName,
|
DisplayName: displayName,
|
||||||
DetachedHead: true,
|
DetachedHead: true,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,26 +181,30 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"falls back to git `git branch --contains` if symbolic-ref fails",
|
"falls back to git `git branch --points-at=HEAD` if symbolic-ref fails",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
|
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
|
||||||
Expect(`git branch --contains`, "* (HEAD detached at 8982166a)", nil),
|
Expect(`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`, "*\x006f71c57a8d4bd6c11399c3f55f42c815527a73a4\x00(HEAD detached at 6f71c57a)\n", nil),
|
||||||
func(info BranchInfo, err error) {
|
func(info BranchInfo, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, "8982166a", info.RefName)
|
assert.EqualValues(t, "6f71c57a8d4bd6c11399c3f55f42c815527a73a4", info.RefName)
|
||||||
assert.EqualValues(t, "(HEAD detached at 8982166a)", info.DisplayName)
|
assert.EqualValues(t, "(HEAD detached at 6f71c57a)", info.DisplayName)
|
||||||
assert.True(t, info.DetachedHead)
|
assert.True(t, info.DetachedHead)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"handles a detached head",
|
"handles a detached head (LANG=zh_CN.UTF-8)",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
|
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
|
||||||
Expect(`git branch --contains`, "* (HEAD detached at 123abcd)", nil),
|
Expect(
|
||||||
|
`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`,
|
||||||
|
"*\x00679b0456f3db7c505b398def84e7d023e5b55a8d\x00(头指针在 679b0456 分离)\n"+
|
||||||
|
" \x00679b0456f3db7c505b398def84e7d023e5b55a8d\x00refs/heads/master\n",
|
||||||
|
nil),
|
||||||
func(info BranchInfo, err error) {
|
func(info BranchInfo, err error) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.EqualValues(t, "123abcd", info.RefName)
|
assert.EqualValues(t, "679b0456f3db7c505b398def84e7d023e5b55a8d", info.RefName)
|
||||||
assert.EqualValues(t, "(HEAD detached at 123abcd)", info.DisplayName)
|
assert.EqualValues(t, "(头指针在 679b0456 分离)", info.DisplayName)
|
||||||
assert.True(t, info.DetachedHead)
|
assert.True(t, info.DetachedHead)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -208,7 +212,7 @@ func TestBranchCurrentBranchInfo(t *testing.T) {
|
||||||
"bubbles up error if there is one",
|
"bubbles up error if there is one",
|
||||||
oscommands.NewFakeRunner(t).
|
oscommands.NewFakeRunner(t).
|
||||||
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
|
Expect(`git symbolic-ref --short HEAD`, "", errors.New("error")).
|
||||||
Expect(`git branch --contains`, "", errors.New("error")),
|
Expect(`git branch --points-at=HEAD --format="%(HEAD)%00%(objectname)%00%(refname)"`, "", errors.New("error")),
|
||||||
func(info BranchInfo, err error) {
|
func(info BranchInfo, err error) {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
assert.EqualValues(t, "", info.RefName)
|
assert.EqualValues(t, "", info.RefName)
|
||||||
|
|
|
@ -74,9 +74,12 @@ func (self *CommitCommands) signoffFlag() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *CommitCommands) verboseFlag() string {
|
func (self *CommitCommands) verboseFlag() string {
|
||||||
if self.UserConfig.Git.Commit.Verbose {
|
switch self.config.UserConfig.Git.Commit.Verbose {
|
||||||
|
case "always":
|
||||||
return " --verbose"
|
return " --verbose"
|
||||||
} else {
|
case "never":
|
||||||
|
return " --no-verbose"
|
||||||
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -426,7 +426,10 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
||||||
|
|
||||||
config := self.UserConfig.Git.Log
|
config := self.UserConfig.Git.Log
|
||||||
|
|
||||||
orderFlag := "--" + config.Order
|
orderFlag := ""
|
||||||
|
if config.Order != "default" {
|
||||||
|
orderFlag = " --" + config.Order
|
||||||
|
}
|
||||||
allFlag := ""
|
allFlag := ""
|
||||||
if opts.All {
|
if opts.All {
|
||||||
allFlag = " --all"
|
allFlag = " --all"
|
||||||
|
@ -434,7 +437,7 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
||||||
|
|
||||||
return self.cmd.New(
|
return self.cmd.New(
|
||||||
fmt.Sprintf(
|
fmt.Sprintf(
|
||||||
"git -c log.showSignature=false log %s %s %s --oneline %s%s --abbrev=%d%s",
|
"git -c log.showSignature=false log %s%s%s --oneline %s%s --abbrev=%d%s",
|
||||||
self.cmd.Quote(opts.RefName),
|
self.cmd.Quote(opts.RefName),
|
||||||
orderFlag,
|
orderFlag,
|
||||||
allFlag,
|
allFlag,
|
||||||
|
@ -446,14 +449,4 @@ func (self *CommitLoader) getLogCmd(opts GetCommitsOptions) oscommands.ICmdObj {
|
||||||
).DontLog()
|
).DontLog()
|
||||||
}
|
}
|
||||||
|
|
||||||
var prettyFormat = fmt.Sprintf(
|
const prettyFormat = `--pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s"`
|
||||||
"--pretty=format:\"%%H%s%%at%s%%aN%s%%ae%s%%d%s%%p%s%%s\"",
|
|
||||||
NULL_CODE,
|
|
||||||
NULL_CODE,
|
|
||||||
NULL_CODE,
|
|
||||||
NULL_CODE,
|
|
||||||
NULL_CODE,
|
|
||||||
NULL_CODE,
|
|
||||||
)
|
|
||||||
|
|
||||||
const NULL_CODE = "%x00"
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ func TestGetCommits(t *testing.T) {
|
||||||
runner *oscommands.FakeCmdObjRunner
|
runner *oscommands.FakeCmdObjRunner
|
||||||
expectedCommits []*models.Commit
|
expectedCommits []*models.Commit
|
||||||
expectedError error
|
expectedError error
|
||||||
|
logOrder string
|
||||||
rebaseMode enums.RebaseMode
|
rebaseMode enums.RebaseMode
|
||||||
currentBranchName string
|
currentBranchName string
|
||||||
opts GetCommitsOptions
|
opts GetCommitsOptions
|
||||||
|
@ -35,18 +36,20 @@ func TestGetCommits(t *testing.T) {
|
||||||
scenarios := []scenario{
|
scenarios := []scenario{
|
||||||
{
|
{
|
||||||
testName: "should return no commits if there are none",
|
testName: "should return no commits if there are none",
|
||||||
|
logOrder: "topo-order",
|
||||||
rebaseMode: enums.REBASE_MODE_NONE,
|
rebaseMode: enums.REBASE_MODE_NONE,
|
||||||
currentBranchName: "master",
|
currentBranchName: "master",
|
||||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||||
runner: oscommands.NewFakeRunner(t).
|
runner: oscommands.NewFakeRunner(t).
|
||||||
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
Expect(`git -c log.showSignature=false log "HEAD" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40`, "", nil),
|
Expect(`git -c log.showSignature=false log "HEAD" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40`, "", nil),
|
||||||
|
|
||||||
expectedCommits: []*models.Commit{},
|
expectedCommits: []*models.Commit{},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
testName: "should return commits if they are present",
|
testName: "should return commits if they are present",
|
||||||
|
logOrder: "topo-order",
|
||||||
rebaseMode: enums.REBASE_MODE_NONE,
|
rebaseMode: enums.REBASE_MODE_NONE,
|
||||||
currentBranchName: "master",
|
currentBranchName: "master",
|
||||||
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||||
|
@ -54,7 +57,7 @@ func TestGetCommits(t *testing.T) {
|
||||||
// here it's seeing which commits are yet to be pushed
|
// here it's seeing which commits are yet to be pushed
|
||||||
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
// here it's actually getting all the commits in a formatted form, one per line
|
// here it's actually getting all the commits in a formatted form, one per line
|
||||||
Expect(`git -c log.showSignature=false log "HEAD" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40`, commitsOutput, nil).
|
Expect(`git -c log.showSignature=false log "HEAD" --topo-order --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40`, commitsOutput, nil).
|
||||||
// here it's seeing where our branch diverged from the master branch so that we can mark that commit and parent commits as 'merged'
|
// here it's seeing where our branch diverged from the master branch so that we can mark that commit and parent commits as 'merged'
|
||||||
Expect(`git merge-base "HEAD" "master"`, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
|
Expect(`git merge-base "HEAD" "master"`, "26c07b1ab33860a1a7591a0638f9925ccf497ffa", nil),
|
||||||
|
|
||||||
|
@ -174,13 +177,29 @@ func TestGetCommits(t *testing.T) {
|
||||||
},
|
},
|
||||||
expectedError: nil,
|
expectedError: nil,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
testName: "should not specify order if `log.order` is `default`",
|
||||||
|
logOrder: "default",
|
||||||
|
rebaseMode: enums.REBASE_MODE_NONE,
|
||||||
|
currentBranchName: "master",
|
||||||
|
opts: GetCommitsOptions{RefName: "HEAD", IncludeRebaseCommits: false},
|
||||||
|
runner: oscommands.NewFakeRunner(t).
|
||||||
|
Expect(`git merge-base "HEAD" "HEAD"@{u}`, "b21997d6b4cbdf84b149d8e6a2c4d06a8e9ec164", nil).
|
||||||
|
Expect(`git -c log.showSignature=false log "HEAD" --oneline --pretty=format:"%H%x00%at%x00%aN%x00%ae%x00%d%x00%p%x00%s" --abbrev=40`, "", nil),
|
||||||
|
|
||||||
|
expectedCommits: []*models.Commit{},
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, scenario := range scenarios {
|
for _, scenario := range scenarios {
|
||||||
scenario := scenario
|
scenario := scenario
|
||||||
t.Run(scenario.testName, func(t *testing.T) {
|
t.Run(scenario.testName, func(t *testing.T) {
|
||||||
|
common := utils.NewDummyCommon()
|
||||||
|
common.UserConfig.Git.Log.Order = scenario.logOrder
|
||||||
|
|
||||||
builder := &CommitLoader{
|
builder := &CommitLoader{
|
||||||
Common: utils.NewDummyCommon(),
|
Common: common,
|
||||||
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
|
cmd: oscommands.NewDummyCmdObjBuilder(scenario.runner),
|
||||||
getCurrentBranchInfo: func() (BranchInfo, error) {
|
getCurrentBranchInfo: func() (BranchInfo, error) {
|
||||||
return BranchInfo{RefName: scenario.currentBranchName, DisplayName: scenario.currentBranchName, DetachedHead: false}, nil
|
return BranchInfo{RefName: scenario.currentBranchName, DisplayName: scenario.currentBranchName, DetachedHead: false}, nil
|
||||||
|
|
|
@ -27,12 +27,11 @@ func TestCommitResetToCommit(t *testing.T) {
|
||||||
runner.CheckForMissingCalls()
|
runner.CheckForMissingCalls()
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCommitCommitObj(t *testing.T) {
|
func TestCommitCommitCmdObj(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
message string
|
message string
|
||||||
configSignoff bool
|
configSignoff bool
|
||||||
configVerbose bool
|
|
||||||
configSkipHookPrefix string
|
configSkipHookPrefix string
|
||||||
expected string
|
expected string
|
||||||
}
|
}
|
||||||
|
@ -42,7 +41,6 @@ func TestCommitCommitObj(t *testing.T) {
|
||||||
testName: "Commit",
|
testName: "Commit",
|
||||||
message: "test",
|
message: "test",
|
||||||
configSignoff: false,
|
configSignoff: false,
|
||||||
configVerbose: false,
|
|
||||||
configSkipHookPrefix: "",
|
configSkipHookPrefix: "",
|
||||||
expected: `git commit -m "test"`,
|
expected: `git commit -m "test"`,
|
||||||
},
|
},
|
||||||
|
@ -50,7 +48,6 @@ func TestCommitCommitObj(t *testing.T) {
|
||||||
testName: "Commit with --no-verify flag",
|
testName: "Commit with --no-verify flag",
|
||||||
message: "WIP: test",
|
message: "WIP: test",
|
||||||
configSignoff: false,
|
configSignoff: false,
|
||||||
configVerbose: false,
|
|
||||||
configSkipHookPrefix: "WIP",
|
configSkipHookPrefix: "WIP",
|
||||||
expected: `git commit --no-verify -m "WIP: test"`,
|
expected: `git commit --no-verify -m "WIP: test"`,
|
||||||
},
|
},
|
||||||
|
@ -58,7 +55,6 @@ func TestCommitCommitObj(t *testing.T) {
|
||||||
testName: "Commit with multiline message",
|
testName: "Commit with multiline message",
|
||||||
message: "line1\nline2",
|
message: "line1\nline2",
|
||||||
configSignoff: false,
|
configSignoff: false,
|
||||||
configVerbose: false,
|
|
||||||
configSkipHookPrefix: "",
|
configSkipHookPrefix: "",
|
||||||
expected: `git commit -m "line1" -m "line2"`,
|
expected: `git commit -m "line1" -m "line2"`,
|
||||||
},
|
},
|
||||||
|
@ -66,23 +62,13 @@ func TestCommitCommitObj(t *testing.T) {
|
||||||
testName: "Commit with signoff",
|
testName: "Commit with signoff",
|
||||||
message: "test",
|
message: "test",
|
||||||
configSignoff: true,
|
configSignoff: true,
|
||||||
configVerbose: false,
|
|
||||||
configSkipHookPrefix: "",
|
configSkipHookPrefix: "",
|
||||||
expected: `git commit --signoff -m "test"`,
|
expected: `git commit --signoff -m "test"`,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
testName: "Commit with message ignores verbose flag",
|
|
||||||
message: "test",
|
|
||||||
configSignoff: false,
|
|
||||||
configVerbose: true,
|
|
||||||
configSkipHookPrefix: "",
|
|
||||||
expected: `git commit -m "test"`,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
testName: "Commit with signoff and no-verify",
|
testName: "Commit with signoff and no-verify",
|
||||||
message: "WIP: test",
|
message: "WIP: test",
|
||||||
configSignoff: true,
|
configSignoff: true,
|
||||||
configVerbose: false,
|
|
||||||
configSkipHookPrefix: "WIP",
|
configSkipHookPrefix: "WIP",
|
||||||
expected: `git commit --no-verify --signoff -m "WIP: test"`,
|
expected: `git commit --no-verify --signoff -m "WIP: test"`,
|
||||||
},
|
},
|
||||||
|
@ -93,7 +79,6 @@ func TestCommitCommitObj(t *testing.T) {
|
||||||
t.Run(s.testName, func(t *testing.T) {
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
userConfig := config.GetDefaultConfig()
|
userConfig := config.GetDefaultConfig()
|
||||||
userConfig.Git.Commit.SignOff = s.configSignoff
|
userConfig.Git.Commit.SignOff = s.configSignoff
|
||||||
userConfig.Git.Commit.Verbose = s.configVerbose
|
|
||||||
userConfig.Git.SkipHookPrefix = s.configSkipHookPrefix
|
userConfig.Git.SkipHookPrefix = s.configSkipHookPrefix
|
||||||
|
|
||||||
instance := buildCommitCommands(commonDeps{userConfig: userConfig})
|
instance := buildCommitCommands(commonDeps{userConfig: userConfig})
|
||||||
|
@ -104,6 +89,62 @@ func TestCommitCommitObj(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCommitCommitEditorCmdObj(t *testing.T) {
|
||||||
|
type scenario struct {
|
||||||
|
testName string
|
||||||
|
configSignoff bool
|
||||||
|
configVerbose string
|
||||||
|
expected string
|
||||||
|
}
|
||||||
|
|
||||||
|
scenarios := []scenario{
|
||||||
|
{
|
||||||
|
testName: "Commit using editor",
|
||||||
|
configSignoff: false,
|
||||||
|
configVerbose: "default",
|
||||||
|
expected: `git commit`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "Commit with --no-verbose flag",
|
||||||
|
configSignoff: false,
|
||||||
|
configVerbose: "never",
|
||||||
|
expected: `git commit --no-verbose`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "Commit with --verbose flag",
|
||||||
|
configSignoff: false,
|
||||||
|
configVerbose: "always",
|
||||||
|
expected: `git commit --verbose`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "Commit with --signoff",
|
||||||
|
configSignoff: true,
|
||||||
|
configVerbose: "default",
|
||||||
|
expected: `git commit --signoff`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "Commit with --signoff and --no-verbose",
|
||||||
|
configSignoff: true,
|
||||||
|
configVerbose: "never",
|
||||||
|
expected: `git commit --signoff --no-verbose`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
s := s
|
||||||
|
t.Run(s.testName, func(t *testing.T) {
|
||||||
|
userConfig := config.GetDefaultConfig()
|
||||||
|
userConfig.Git.Commit.SignOff = s.configSignoff
|
||||||
|
userConfig.Git.Commit.Verbose = s.configVerbose
|
||||||
|
|
||||||
|
instance := buildCommitCommands(commonDeps{userConfig: userConfig})
|
||||||
|
|
||||||
|
cmdStr := instance.CommitEditorCmdObj().ToString()
|
||||||
|
assert.Equal(t, s.expected, cmdStr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCommitCreateFixupCommit(t *testing.T) {
|
func TestCommitCreateFixupCommit(t *testing.T) {
|
||||||
type scenario struct {
|
type scenario struct {
|
||||||
testName string
|
testName string
|
||||||
|
|
|
@ -202,10 +202,6 @@ func (p *PatchParser) Render(isFocused bool, firstLineIndex int, lastLineIndex i
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PatchParser) RenderPlain() string {
|
|
||||||
return renderLinesPlain(p.PatchLines)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenderLinesPlain returns the non-coloured string of diff part from firstLineIndex to
|
// RenderLinesPlain returns the non-coloured string of diff part from firstLineIndex to
|
||||||
// lastLineIndex
|
// lastLineIndex
|
||||||
func (p *PatchParser) RenderLinesPlain(firstLineIndex, lastLineIndex int) string {
|
func (p *PatchParser) RenderLinesPlain(firstLineIndex, lastLineIndex int) string {
|
||||||
|
@ -214,10 +210,10 @@ func (p *PatchParser) RenderLinesPlain(firstLineIndex, lastLineIndex int) string
|
||||||
|
|
||||||
func renderLinesPlain(lines []*PatchLine) string {
|
func renderLinesPlain(lines []*PatchLine) string {
|
||||||
renderedLines := slices.Map(lines, func(line *PatchLine) string {
|
renderedLines := slices.Map(lines, func(line *PatchLine) string {
|
||||||
return line.Content
|
return line.Content + "\n"
|
||||||
})
|
})
|
||||||
|
|
||||||
return strings.Join(renderedLines, "\n")
|
return strings.Join(renderedLines, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNextStageableLineIndex takes a line index and returns the line index of the next stageable line
|
// GetNextStageableLineIndex takes a line index and returns the line index of the next stageable line
|
||||||
|
|
|
@ -27,30 +27,31 @@ type RefresherConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type GuiConfig struct {
|
type GuiConfig struct {
|
||||||
AuthorColors map[string]string `yaml:"authorColors"`
|
AuthorColors map[string]string `yaml:"authorColors"`
|
||||||
BranchColors map[string]string `yaml:"branchColors"`
|
BranchColors map[string]string `yaml:"branchColors"`
|
||||||
ScrollHeight int `yaml:"scrollHeight"`
|
ScrollHeight int `yaml:"scrollHeight"`
|
||||||
ScrollPastBottom bool `yaml:"scrollPastBottom"`
|
ScrollPastBottom bool `yaml:"scrollPastBottom"`
|
||||||
MouseEvents bool `yaml:"mouseEvents"`
|
MouseEvents bool `yaml:"mouseEvents"`
|
||||||
SkipUnstageLineWarning bool `yaml:"skipUnstageLineWarning"`
|
SkipUnstageLineWarning bool `yaml:"skipUnstageLineWarning"`
|
||||||
SkipStashWarning bool `yaml:"skipStashWarning"`
|
SkipStashWarning bool `yaml:"skipStashWarning"`
|
||||||
SidePanelWidth float64 `yaml:"sidePanelWidth"`
|
SidePanelWidth float64 `yaml:"sidePanelWidth"`
|
||||||
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
|
ExpandFocusedSidePanel bool `yaml:"expandFocusedSidePanel"`
|
||||||
MainPanelSplitMode string `yaml:"mainPanelSplitMode"`
|
MainPanelSplitMode string `yaml:"mainPanelSplitMode"`
|
||||||
Language string `yaml:"language"`
|
Language string `yaml:"language"`
|
||||||
TimeFormat string `yaml:"timeFormat"`
|
TimeFormat string `yaml:"timeFormat"`
|
||||||
Theme ThemeConfig `yaml:"theme"`
|
Theme ThemeConfig `yaml:"theme"`
|
||||||
CommitLength CommitLengthConfig `yaml:"commitLength"`
|
CommitLength CommitLengthConfig `yaml:"commitLength"`
|
||||||
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
|
SkipNoStagedFilesWarning bool `yaml:"skipNoStagedFilesWarning"`
|
||||||
ShowListFooter bool `yaml:"showListFooter"`
|
ShowListFooter bool `yaml:"showListFooter"`
|
||||||
ShowFileTree bool `yaml:"showFileTree"`
|
ShowFileTree bool `yaml:"showFileTree"`
|
||||||
ShowRandomTip bool `yaml:"showRandomTip"`
|
ShowRandomTip bool `yaml:"showRandomTip"`
|
||||||
ShowCommandLog bool `yaml:"showCommandLog"`
|
ShowCommandLog bool `yaml:"showCommandLog"`
|
||||||
ShowBottomLine bool `yaml:"showBottomLine"`
|
ShowBottomLine bool `yaml:"showBottomLine"`
|
||||||
ShowIcons bool `yaml:"showIcons"`
|
ShowIcons bool `yaml:"showIcons"`
|
||||||
CommandLogSize int `yaml:"commandLogSize"`
|
CommandLogSize int `yaml:"commandLogSize"`
|
||||||
SplitDiff string `yaml:"splitDiff"`
|
SplitDiff string `yaml:"splitDiff"`
|
||||||
WindowSize string `yaml:"windowSize"`
|
SkipRewordInEditorWarning bool `yaml:"skipRewordInEditorWarning"`
|
||||||
|
WindowSize string `yaml:"windowSize"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ThemeConfig struct {
|
type ThemeConfig struct {
|
||||||
|
@ -94,8 +95,8 @@ type PagingConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CommitConfig struct {
|
type CommitConfig struct {
|
||||||
SignOff bool `yaml:"signOff"`
|
SignOff bool `yaml:"signOff"`
|
||||||
Verbose bool `yaml:"verbose"`
|
Verbose string `yaml:"verbose"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MergingConfig struct {
|
type MergingConfig struct {
|
||||||
|
@ -369,16 +370,17 @@ func GetDefaultConfig() *UserConfig {
|
||||||
UnstagedChangesColor: []string{"red"},
|
UnstagedChangesColor: []string{"red"},
|
||||||
DefaultFgColor: []string{"default"},
|
DefaultFgColor: []string{"default"},
|
||||||
},
|
},
|
||||||
CommitLength: CommitLengthConfig{Show: true},
|
CommitLength: CommitLengthConfig{Show: true},
|
||||||
SkipNoStagedFilesWarning: false,
|
SkipNoStagedFilesWarning: false,
|
||||||
ShowListFooter: true,
|
ShowListFooter: true,
|
||||||
ShowCommandLog: true,
|
ShowCommandLog: true,
|
||||||
ShowBottomLine: true,
|
ShowBottomLine: true,
|
||||||
ShowFileTree: true,
|
ShowFileTree: true,
|
||||||
ShowRandomTip: true,
|
ShowRandomTip: true,
|
||||||
ShowIcons: false,
|
ShowIcons: false,
|
||||||
CommandLogSize: 8,
|
CommandLogSize: 8,
|
||||||
SplitDiff: "auto",
|
SplitDiff: "auto",
|
||||||
|
SkipRewordInEditorWarning: false,
|
||||||
},
|
},
|
||||||
Git: GitConfig{
|
Git: GitConfig{
|
||||||
Paging: PagingConfig{
|
Paging: PagingConfig{
|
||||||
|
@ -388,7 +390,7 @@ func GetDefaultConfig() *UserConfig {
|
||||||
},
|
},
|
||||||
Commit: CommitConfig{
|
Commit: CommitConfig{
|
||||||
SignOff: false,
|
SignOff: false,
|
||||||
Verbose: false,
|
Verbose: "default",
|
||||||
},
|
},
|
||||||
Merging: MergingConfig{
|
Merging: MergingConfig{
|
||||||
ManualCommit: false,
|
ManualCommit: false,
|
||||||
|
|
|
@ -226,6 +226,26 @@ func (self *LocalCommitsController) reword(commit *models.Commit) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (self *LocalCommitsController) doRewordEditor() error {
|
||||||
|
self.c.LogAction(self.c.Tr.Actions.RewordCommit)
|
||||||
|
|
||||||
|
if self.context().GetSelectedLineIdx() == 0 {
|
||||||
|
return self.c.RunSubprocessAndRefresh(self.os.Cmd.New("git commit --allow-empty --amend --only"))
|
||||||
|
}
|
||||||
|
|
||||||
|
subProcess, err := self.git.Rebase.RewordCommitInEditor(
|
||||||
|
self.model.Commits, self.context().GetSelectedLineIdx(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return self.c.Error(err)
|
||||||
|
}
|
||||||
|
if subProcess != nil {
|
||||||
|
return self.c.RunSubprocessAndRefresh(subProcess)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) rewordEditor(commit *models.Commit) error {
|
func (self *LocalCommitsController) rewordEditor(commit *models.Commit) error {
|
||||||
midRebase, err := self.handleMidRebaseCommand("reword", commit)
|
midRebase, err := self.handleMidRebaseCommand("reword", commit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -235,29 +255,15 @@ func (self *LocalCommitsController) rewordEditor(commit *models.Commit) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.c.Confirm(types.ConfirmOpts{
|
if self.c.UserConfig.Gui.SkipRewordInEditorWarning {
|
||||||
Title: self.c.Tr.RewordInEditorTitle,
|
return self.doRewordEditor()
|
||||||
Prompt: self.c.Tr.RewordInEditorPrompt,
|
} else {
|
||||||
HandleConfirm: func() error {
|
return self.c.Confirm(types.ConfirmOpts{
|
||||||
self.c.LogAction(self.c.Tr.Actions.RewordCommit)
|
Title: self.c.Tr.RewordInEditorTitle,
|
||||||
|
Prompt: self.c.Tr.RewordInEditorPrompt,
|
||||||
if self.context().GetSelectedLineIdx() == 0 {
|
HandleConfirm: self.doRewordEditor,
|
||||||
return self.c.RunSubprocessAndRefresh(self.os.Cmd.New("git commit --allow-empty --amend --only"))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
subProcess, err := self.git.Rebase.RewordCommitInEditor(
|
|
||||||
self.model.Commits, self.context().GetSelectedLineIdx(),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return self.c.Error(err)
|
|
||||||
}
|
|
||||||
if subProcess != nil {
|
|
||||||
return self.c.RunSubprocessAndRefresh(subProcess)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *LocalCommitsController) drop(commit *models.Commit) error {
|
func (self *LocalCommitsController) drop(commit *models.Commit) error {
|
||||||
|
|
|
@ -73,8 +73,6 @@ func chineseTranslationSet() TranslationSet {
|
||||||
MergeConflictsTitle: "合并冲突",
|
MergeConflictsTitle: "合并冲突",
|
||||||
LcCheckout: "检出",
|
LcCheckout: "检出",
|
||||||
NoChangedFiles: "没有更改过文件",
|
NoChangedFiles: "没有更改过文件",
|
||||||
NoFilesDisplay: "没有文件可显示",
|
|
||||||
NotAFile: "不是文件",
|
|
||||||
PullWait: "正在拉取…",
|
PullWait: "正在拉取…",
|
||||||
PushWait: "正在推送…",
|
PushWait: "正在推送…",
|
||||||
FetchWait: "正在抓取…",
|
FetchWait: "正在抓取…",
|
||||||
|
@ -104,7 +102,6 @@ func chineseTranslationSet() TranslationSet {
|
||||||
LcSquashDown: "向下压缩",
|
LcSquashDown: "向下压缩",
|
||||||
LcFixupCommit: "修正提交(fixup)",
|
LcFixupCommit: "修正提交(fixup)",
|
||||||
NoCommitsThisBranch: "该分支没有提交",
|
NoCommitsThisBranch: "该分支没有提交",
|
||||||
OnlySquashTopmostCommit: "只能压缩最顶层的提交",
|
|
||||||
YouNoCommitsToSquash: "您没有提交可以压缩",
|
YouNoCommitsToSquash: "您没有提交可以压缩",
|
||||||
Fixup: "修正(fixup)",
|
Fixup: "修正(fixup)",
|
||||||
SureFixupThisCommit: "您确定要“修正”此提交吗?它将合并到下面的提交中",
|
SureFixupThisCommit: "您确定要“修正”此提交吗?它将合并到下面的提交中",
|
||||||
|
|
|
@ -39,8 +39,6 @@ func dutchTranslationSet() TranslationSet {
|
||||||
ResetCommitFilterState: "Reset commit file state filter",
|
ResetCommitFilterState: "Reset commit file state filter",
|
||||||
MergeConflictsTitle: "Merge Conflicten",
|
MergeConflictsTitle: "Merge Conflicten",
|
||||||
LcCheckout: "uitchecken",
|
LcCheckout: "uitchecken",
|
||||||
NoFilesDisplay: "Geen bestanden om te laten zien",
|
|
||||||
NotAFile: "Dit is geen bestand",
|
|
||||||
PullWait: "Pullen...",
|
PullWait: "Pullen...",
|
||||||
PushWait: "Pushen...",
|
PushWait: "Pushen...",
|
||||||
FetchWait: "Fetchen...",
|
FetchWait: "Fetchen...",
|
||||||
|
@ -69,7 +67,6 @@ func dutchTranslationSet() TranslationSet {
|
||||||
LcQuit: "quit",
|
LcQuit: "quit",
|
||||||
LcSquashDown: "squash beneden",
|
LcSquashDown: "squash beneden",
|
||||||
LcFixupCommit: "Fixup commit",
|
LcFixupCommit: "Fixup commit",
|
||||||
OnlySquashTopmostCommit: "Kan alleen bovenste commit squashen",
|
|
||||||
YouNoCommitsToSquash: "Je hebt geen commits om mee te squashen",
|
YouNoCommitsToSquash: "Je hebt geen commits om mee te squashen",
|
||||||
Fixup: "Fixup",
|
Fixup: "Fixup",
|
||||||
SureFixupThisCommit: "Weet je zeker dat je fixup wil uitvoeren op deze commit? De commit hieronder zol worden squashed in deze",
|
SureFixupThisCommit: "Weet je zeker dat je fixup wil uitvoeren op deze commit? De commit hieronder zol worden squashed in deze",
|
||||||
|
|
|
@ -59,8 +59,6 @@ type TranslationSet struct {
|
||||||
MergeConflictsTitle string
|
MergeConflictsTitle string
|
||||||
LcCheckout string
|
LcCheckout string
|
||||||
NoChangedFiles string
|
NoChangedFiles string
|
||||||
NoFilesDisplay string
|
|
||||||
NotAFile string
|
|
||||||
PullWait string
|
PullWait string
|
||||||
PushWait string
|
PushWait string
|
||||||
FetchWait string
|
FetchWait string
|
||||||
|
@ -89,7 +87,6 @@ type TranslationSet struct {
|
||||||
LcQuit string
|
LcQuit string
|
||||||
LcSquashDown string
|
LcSquashDown string
|
||||||
LcFixupCommit string
|
LcFixupCommit string
|
||||||
OnlySquashTopmostCommit string
|
|
||||||
YouNoCommitsToSquash string
|
YouNoCommitsToSquash string
|
||||||
Fixup string
|
Fixup string
|
||||||
SureFixupThisCommit string
|
SureFixupThisCommit string
|
||||||
|
@ -709,8 +706,6 @@ func EnglishTranslationSet() TranslationSet {
|
||||||
FilterUnstagedFiles: "Show only unstaged files",
|
FilterUnstagedFiles: "Show only unstaged files",
|
||||||
ResetCommitFilterState: "Reset filter",
|
ResetCommitFilterState: "Reset filter",
|
||||||
NoChangedFiles: "No changed files",
|
NoChangedFiles: "No changed files",
|
||||||
NoFilesDisplay: "No file to display",
|
|
||||||
NotAFile: "Not a file",
|
|
||||||
PullWait: "Pulling...",
|
PullWait: "Pulling...",
|
||||||
PushWait: "Pushing...",
|
PushWait: "Pushing...",
|
||||||
FetchWait: "Fetching...",
|
FetchWait: "Fetching...",
|
||||||
|
@ -740,7 +735,6 @@ func EnglishTranslationSet() TranslationSet {
|
||||||
LcSquashDown: "squash down",
|
LcSquashDown: "squash down",
|
||||||
LcFixupCommit: "fixup commit",
|
LcFixupCommit: "fixup commit",
|
||||||
NoCommitsThisBranch: "No commits for this branch",
|
NoCommitsThisBranch: "No commits for this branch",
|
||||||
OnlySquashTopmostCommit: "Can only squash topmost commit",
|
|
||||||
YouNoCommitsToSquash: "You have no commits to squash with",
|
YouNoCommitsToSquash: "You have no commits to squash with",
|
||||||
Fixup: "Fixup",
|
Fixup: "Fixup",
|
||||||
SureFixupThisCommit: "Are you sure you want to 'fixup' this commit? It will be merged into the commit below",
|
SureFixupThisCommit: "Are you sure you want to 'fixup' this commit? It will be merged into the commit below",
|
||||||
|
|
|
@ -64,8 +64,6 @@ func japaneseTranslationSet() TranslationSet {
|
||||||
FilterUnstagedFiles: "ステージされていないファイルのみを表示",
|
FilterUnstagedFiles: "ステージされていないファイルのみを表示",
|
||||||
ResetCommitFilterState: "フィルタをリセット",
|
ResetCommitFilterState: "フィルタをリセット",
|
||||||
// NoChangedFiles: "No changed files",
|
// NoChangedFiles: "No changed files",
|
||||||
// NoFilesDisplay: "No file to display",
|
|
||||||
// NotAFile: "Not a file",
|
|
||||||
PullWait: "Pull中...",
|
PullWait: "Pull中...",
|
||||||
PushWait: "Push中...",
|
PushWait: "Push中...",
|
||||||
FetchWait: "Fetch中...",
|
FetchWait: "Fetch中...",
|
||||||
|
@ -95,7 +93,6 @@ func japaneseTranslationSet() TranslationSet {
|
||||||
// LcSquashDown: "squash down",
|
// LcSquashDown: "squash down",
|
||||||
// LcFixupCommit: "fixup commit",
|
// LcFixupCommit: "fixup commit",
|
||||||
// NoCommitsThisBranch: "No commits for this branch",
|
// NoCommitsThisBranch: "No commits for this branch",
|
||||||
// OnlySquashTopmostCommit: "Can only squash topmost commit",
|
|
||||||
// YouNoCommitsToSquash: "You have no commits to squash with",
|
// YouNoCommitsToSquash: "You have no commits to squash with",
|
||||||
// Fixup: "Fixup",
|
// Fixup: "Fixup",
|
||||||
// SureFixupThisCommit: "Are you sure you want to 'fixup' this commit? It will be merged into the commit below",
|
// SureFixupThisCommit: "Are you sure you want to 'fixup' this commit? It will be merged into the commit below",
|
||||||
|
|
|
@ -63,8 +63,6 @@ func koreanTranslationSet() TranslationSet {
|
||||||
FilterUnstagedFiles: "Stage되지 않은 파일만 표시",
|
FilterUnstagedFiles: "Stage되지 않은 파일만 표시",
|
||||||
ResetCommitFilterState: "필터 리셋",
|
ResetCommitFilterState: "필터 리셋",
|
||||||
NoChangedFiles: "변경된 파일이 없습니다.",
|
NoChangedFiles: "변경된 파일이 없습니다.",
|
||||||
NoFilesDisplay: "표시할 파일이 없습니다",
|
|
||||||
NotAFile: "파일이 아닙니다.",
|
|
||||||
PullWait: "업데이트 중...",
|
PullWait: "업데이트 중...",
|
||||||
PushWait: "푸시 중...",
|
PushWait: "푸시 중...",
|
||||||
FetchWait: "패치 중...",
|
FetchWait: "패치 중...",
|
||||||
|
@ -94,7 +92,6 @@ func koreanTranslationSet() TranslationSet {
|
||||||
LcSquashDown: "squash down",
|
LcSquashDown: "squash down",
|
||||||
LcFixupCommit: "fixup commit",
|
LcFixupCommit: "fixup commit",
|
||||||
NoCommitsThisBranch: "이 브랜치에 커밋이 없습니다.",
|
NoCommitsThisBranch: "이 브랜치에 커밋이 없습니다.",
|
||||||
OnlySquashTopmostCommit: "Can only squash topmost commit",
|
|
||||||
YouNoCommitsToSquash: "You have no commits to squash with",
|
YouNoCommitsToSquash: "You have no commits to squash with",
|
||||||
Fixup: "Fixup",
|
Fixup: "Fixup",
|
||||||
SureFixupThisCommit: "Are you sure you want to 'fixup' this commit? It will be merged into the commit below",
|
SureFixupThisCommit: "Are you sure you want to 'fixup' this commit? It will be merged into the commit below",
|
||||||
|
|
|
@ -35,7 +35,6 @@ func polishTranslationSet() TranslationSet {
|
||||||
ResetCommitFilterState: "Resetuj filtr commitów",
|
ResetCommitFilterState: "Resetuj filtr commitów",
|
||||||
LcCheckout: "przełącz",
|
LcCheckout: "przełącz",
|
||||||
NoChangedFiles: "Brak zmienionych plików",
|
NoChangedFiles: "Brak zmienionych plików",
|
||||||
NoFilesDisplay: "Brak plików do wyświetlenia",
|
|
||||||
PullWait: "Pobieranie zmian...",
|
PullWait: "Pobieranie zmian...",
|
||||||
PushWait: "Wysyłanie zmian...",
|
PushWait: "Wysyłanie zmian...",
|
||||||
FetchWait: "Pobieram...",
|
FetchWait: "Pobieram...",
|
||||||
|
@ -63,7 +62,6 @@ func polishTranslationSet() TranslationSet {
|
||||||
LcSquashDown: "ściśnij",
|
LcSquashDown: "ściśnij",
|
||||||
LcFixupCommit: "napraw commit",
|
LcFixupCommit: "napraw commit",
|
||||||
NoCommitsThisBranch: "Brak commitów dla tej gałęzi",
|
NoCommitsThisBranch: "Brak commitów dla tej gałęzi",
|
||||||
OnlySquashTopmostCommit: "Można tylko spłaszczyć najwyższy commit",
|
|
||||||
YouNoCommitsToSquash: "Nie masz commitów do spłaszczenia",
|
YouNoCommitsToSquash: "Nie masz commitów do spłaszczenia",
|
||||||
Fixup: "Napraw",
|
Fixup: "Napraw",
|
||||||
SureFixupThisCommit: "Jesteś pewny, ze chcesz naprawić ten commit? Commit poniżej zostanie spłaszczony w górę wraz z tym",
|
SureFixupThisCommit: "Jesteś pewny, ze chcesz naprawić ten commit? Commit poniżej zostanie spłaszczony w górę wraz z tym",
|
||||||
|
|
40
pkg/integration/tests/branch/detached_head.go
Normal file
40
pkg/integration/tests/branch/detached_head.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package branch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
|
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||||
|
)
|
||||||
|
|
||||||
|
var DetachedHead = NewIntegrationTest(NewIntegrationTestArgs{
|
||||||
|
Description: "Create a new branch on detached head",
|
||||||
|
ExtraCmdArgs: "",
|
||||||
|
Skip: false,
|
||||||
|
SetupConfig: func(config *config.AppConfig) {},
|
||||||
|
SetupRepo: func(shell *Shell) {
|
||||||
|
shell.
|
||||||
|
CreateNCommits(10).
|
||||||
|
Checkout("HEAD^")
|
||||||
|
},
|
||||||
|
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||||
|
t.Views().Branches().
|
||||||
|
Focus().
|
||||||
|
Lines(
|
||||||
|
MatchesRegexp(`\*.*HEAD`).IsSelected(),
|
||||||
|
MatchesRegexp(`master`),
|
||||||
|
).
|
||||||
|
Press(keys.Universal.New)
|
||||||
|
|
||||||
|
t.ExpectPopup().Prompt().
|
||||||
|
Title(MatchesRegexp(`^New Branch Name \(Branch is off of '[0-9a-f]+'\)$`)).
|
||||||
|
Type("new-branch").
|
||||||
|
Confirm()
|
||||||
|
|
||||||
|
t.Views().Branches().
|
||||||
|
Lines(
|
||||||
|
MatchesRegexp(`\* new-branch`).IsSelected(),
|
||||||
|
MatchesRegexp(`master`),
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Git().CurrentBranchName("new-branch")
|
||||||
|
},
|
||||||
|
})
|
|
@ -38,6 +38,7 @@ var tests = []*components.IntegrationTest{
|
||||||
branch.RebaseAndDrop,
|
branch.RebaseAndDrop,
|
||||||
branch.Suggestions,
|
branch.Suggestions,
|
||||||
branch.Reset,
|
branch.Reset,
|
||||||
|
branch.DetachedHead,
|
||||||
cherry_pick.CherryPick,
|
cherry_pick.CherryPick,
|
||||||
cherry_pick.CherryPickConflicts,
|
cherry_pick.CherryPickConflicts,
|
||||||
commit.Commit,
|
commit.Commit,
|
||||||
|
|
|
@ -22,9 +22,12 @@ func ResolveTemplate(templateStr string, object interface{}, funcs template.Func
|
||||||
|
|
||||||
// ResolvePlaceholderString populates a template with values
|
// ResolvePlaceholderString populates a template with values
|
||||||
func ResolvePlaceholderString(str string, arguments map[string]string) string {
|
func ResolvePlaceholderString(str string, arguments map[string]string) string {
|
||||||
|
oldnews := make([]string, 0, len(arguments)*4)
|
||||||
for key, value := range arguments {
|
for key, value := range arguments {
|
||||||
str = strings.Replace(str, "{{"+key+"}}", value, -1)
|
oldnews = append(oldnews,
|
||||||
str = strings.Replace(str, "{{."+key+"}}", value, -1)
|
"{{"+key+"}}", value,
|
||||||
|
"{{."+key+"}}", value,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return str
|
return strings.NewReplacer(oldnews...).Replace(str)
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,6 +53,13 @@ func TestResolvePlaceholderString(t *testing.T) {
|
||||||
},
|
},
|
||||||
"{{}} {{ this }} { should not throw}} an {{{{}}}} error",
|
"{{}} {{ this }} { should not throw}} an {{{{}}}} error",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"{{a}}",
|
||||||
|
map[string]string{
|
||||||
|
"a": "X{{.a}}X",
|
||||||
|
},
|
||||||
|
"X{{.a}}X",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range scenarios {
|
for _, s := range scenarios {
|
||||||
|
|
17
uffizzi/DockerfileTtyd
Normal file
17
uffizzi/DockerfileTtyd
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
FROM uffizzi/ttyd:golang1.18-alpine as build
|
||||||
|
|
||||||
|
WORKDIR /go/src/github.com/jesseduffield/lazygit/
|
||||||
|
COPY go.mod go.sum ./
|
||||||
|
RUN go mod download
|
||||||
|
COPY . .
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux go build
|
||||||
|
|
||||||
|
RUN apk update --quiet && \
|
||||||
|
apk add -q --no-cache libgcc tini
|
||||||
|
|
||||||
|
|
||||||
|
EXPOSE 7700/tcp
|
||||||
|
|
||||||
|
ENTRYPOINT ["tini", "--"]
|
||||||
|
CMD ["ttyd", "/bin/zsh"]
|
20
uffizzi/docker-compose.uffizzi.yml
Normal file
20
uffizzi/docker-compose.uffizzi.yml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
version: "3"
|
||||||
|
|
||||||
|
x-uffizzi:
|
||||||
|
ingress:
|
||||||
|
service: application
|
||||||
|
port: 7681
|
||||||
|
|
||||||
|
services:
|
||||||
|
|
||||||
|
application:
|
||||||
|
image: "${APP_IMAGE}"
|
||||||
|
entrypoint: ["/bin/bash", "-c"]
|
||||||
|
command: ["ttyd /usr/local/go/bin/go run /go/src/github.com/jesseduffield/lazygit/cmd/integration_test/main.go tui"]
|
||||||
|
ports:
|
||||||
|
- "7700:7700"
|
||||||
|
- "7681:7681"
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
memory: 2000M
|
Loading…
Add table
Add a link
Reference in a new issue