Merge branch 'master' into #2319_default_screen_mode

This commit is contained in:
Phanindra Kumar Paladi 2023-01-29 10:25:14 +05:30 committed by GitHub
commit 01f0efb997
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 453 additions and 158 deletions

89
.github/workflows/uffizzi-build.yml vendored Normal file
View 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
View 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

File diff suppressed because one or more lines are too long

View file

@ -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'

View file

@ -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
} }

View file

@ -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)

View file

@ -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 ""
} }
} }

View file

@ -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"
@ -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"

View file

@ -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,6 +36,7 @@ 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},
@ -47,6 +49,7 @@ func TestGetCommits(t *testing.T) {
}, },
{ {
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},
@ -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

View file

@ -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

View file

@ -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

View file

@ -50,6 +50,7 @@ type GuiConfig struct {
ShowIcons bool `yaml:"showIcons"` ShowIcons bool `yaml:"showIcons"`
CommandLogSize int `yaml:"commandLogSize"` CommandLogSize int `yaml:"commandLogSize"`
SplitDiff string `yaml:"splitDiff"` SplitDiff string `yaml:"splitDiff"`
SkipRewordInEditorWarning bool `yaml:"skipRewordInEditorWarning"`
WindowSize string `yaml:"windowSize"` WindowSize string `yaml:"windowSize"`
} }
@ -95,7 +96,7 @@ 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 {
@ -379,6 +380,7 @@ func GetDefaultConfig() *UserConfig {
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,

View file

@ -226,19 +226,7 @@ func (self *LocalCommitsController) reword(commit *models.Commit) error {
}) })
} }
func (self *LocalCommitsController) rewordEditor(commit *models.Commit) error { func (self *LocalCommitsController) doRewordEditor() error {
midRebase, err := self.handleMidRebaseCommand("reword", commit)
if err != nil {
return err
}
if midRebase {
return nil
}
return self.c.Confirm(types.ConfirmOpts{
Title: self.c.Tr.RewordInEditorTitle,
Prompt: self.c.Tr.RewordInEditorPrompt,
HandleConfirm: func() error {
self.c.LogAction(self.c.Tr.Actions.RewordCommit) self.c.LogAction(self.c.Tr.Actions.RewordCommit)
if self.context().GetSelectedLineIdx() == 0 { if self.context().GetSelectedLineIdx() == 0 {
@ -256,9 +244,27 @@ func (self *LocalCommitsController) rewordEditor(commit *models.Commit) error {
} }
return nil return nil
}, }
func (self *LocalCommitsController) rewordEditor(commit *models.Commit) error {
midRebase, err := self.handleMidRebaseCommand("reword", commit)
if err != nil {
return err
}
if midRebase {
return nil
}
if self.c.UserConfig.Gui.SkipRewordInEditorWarning {
return self.doRewordEditor()
} else {
return self.c.Confirm(types.ConfirmOpts{
Title: self.c.Tr.RewordInEditorTitle,
Prompt: self.c.Tr.RewordInEditorPrompt,
HandleConfirm: self.doRewordEditor,
}) })
} }
}
func (self *LocalCommitsController) drop(commit *models.Commit) error { func (self *LocalCommitsController) drop(commit *models.Commit) error {
applied, err := self.handleMidRebaseCommand("drop", commit) applied, err := self.handleMidRebaseCommand("drop", commit)

View file

@ -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: "您确定要“修正”此提交吗?它将合并到下面的提交中",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View 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")
},
})

View file

@ -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,

View file

@ -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)
} }

View file

@ -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
View 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"]

View 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