mirror of
https://github.com/jesseduffield/lazygit.git
synced 2024-11-28 09:08:41 +02:00
Merge branch 'master' into refactor-better-encapsulation
This commit is contained in:
commit
2b30085dba
@ -252,21 +252,21 @@ keybinding:
|
||||
|
||||
```yaml
|
||||
os:
|
||||
openCommand: 'start "" {{filename}}'
|
||||
open: 'start "" {{filename}}'
|
||||
```
|
||||
|
||||
### Linux
|
||||
|
||||
```yaml
|
||||
os:
|
||||
openCommand: 'xdg-open {{filename}} >/dev/null'
|
||||
open: 'xdg-open {{filename}} >/dev/null'
|
||||
```
|
||||
|
||||
### OSX
|
||||
|
||||
```yaml
|
||||
os:
|
||||
openCommand: 'open {{filename}}'
|
||||
open: 'open {{filename}}'
|
||||
```
|
||||
|
||||
### Configuring File Editing
|
||||
@ -285,9 +285,9 @@ os:
|
||||
editPreset: 'vscode'
|
||||
```
|
||||
|
||||
Supported presets are `vim`, `emacs`, `nano`, `vscode`, `sublime`, `bbedit`, and
|
||||
`xcode`. In many cases lazygit will be able to guess the right preset from your
|
||||
$(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
|
||||
Supported presets are `vim`, `nvim`, `emacs`, `nano`, `vscode`, `sublime`, `bbedit`,
|
||||
`kakoune` and `xcode`. In many cases lazygit will be able to guess the right preset
|
||||
from your $(git config core.editor), or an environment variable such as $VISUAL or $EDITOR.
|
||||
|
||||
If for some reason you are not happy with the default commands from a preset, or
|
||||
there simply is no preset for your editor, you can customize the commands by
|
||||
|
@ -181,12 +181,18 @@ func (self *RebaseCommands) PrepareInteractiveRebaseCommand(opts PrepareInteract
|
||||
debug = "TRUE"
|
||||
}
|
||||
|
||||
emptyArg := " --empty=keep"
|
||||
if self.version.IsOlderThan(2, 26, 0) {
|
||||
emptyArg = ""
|
||||
}
|
||||
|
||||
rebaseMergesArg := " --rebase-merges"
|
||||
if self.version.IsOlderThan(2, 22, 0) {
|
||||
rebaseMergesArg = ""
|
||||
}
|
||||
cmdStr := fmt.Sprintf("git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash%s %s",
|
||||
rebaseMergesArg, opts.baseShaOrRoot)
|
||||
|
||||
cmdStr := fmt.Sprintf("git rebase --interactive --autostash --keep-empty%s --no-autosquash%s %s",
|
||||
emptyArg, rebaseMergesArg, opts.baseShaOrRoot)
|
||||
self.Log.WithField("command", cmdStr).Debug("RunCommand")
|
||||
|
||||
cmdObj := self.cmd.New(cmdStr)
|
||||
|
@ -16,37 +16,60 @@ import (
|
||||
|
||||
func TestRebaseRebaseBranch(t *testing.T) {
|
||||
type scenario struct {
|
||||
testName string
|
||||
arg string
|
||||
runner *oscommands.FakeCmdObjRunner
|
||||
test func(error)
|
||||
testName string
|
||||
arg string
|
||||
gitVersion *GitVersion
|
||||
runner *oscommands.FakeCmdObjRunner
|
||||
test func(error)
|
||||
}
|
||||
|
||||
scenarios := []scenario{
|
||||
{
|
||||
testName: "successful rebase",
|
||||
arg: "master",
|
||||
testName: "successful rebase",
|
||||
arg: "master",
|
||||
gitVersion: &GitVersion{2, 26, 0, ""},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash master`, "", nil),
|
||||
Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash --rebase-merges master`, "", nil),
|
||||
test: func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "unsuccessful rebase",
|
||||
arg: "master",
|
||||
testName: "unsuccessful rebase",
|
||||
arg: "master",
|
||||
gitVersion: &GitVersion{2, 26, 0, ""},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash master`, "", errors.New("error")),
|
||||
Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash --rebase-merges master`, "", errors.New("error")),
|
||||
test: func(err error) {
|
||||
assert.Error(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "successful rebase (< 2.26.0)",
|
||||
arg: "master",
|
||||
gitVersion: &GitVersion{2, 25, 5, ""},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
Expect(`git rebase --interactive --autostash --keep-empty --no-autosquash --rebase-merges master`, "", nil),
|
||||
test: func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
{
|
||||
testName: "successful rebase (< 2.22.0)",
|
||||
arg: "master",
|
||||
gitVersion: &GitVersion{2, 21, 9, ""},
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
Expect(`git rebase --interactive --autostash --keep-empty --no-autosquash master`, "", nil),
|
||||
test: func(err error) {
|
||||
assert.NoError(t, err)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, s := range scenarios {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
instance := buildRebaseCommands(commonDeps{runner: s.runner})
|
||||
instance := buildRebaseCommands(commonDeps{runner: s.runner, gitVersion: s.gitVersion})
|
||||
s.test(instance.RebaseBranch(s.arg))
|
||||
})
|
||||
}
|
||||
@ -126,7 +149,7 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
|
||||
commitIndex: 0,
|
||||
fileName: "test999.txt",
|
||||
runner: oscommands.NewFakeRunner(t).
|
||||
Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash abcdef`, "", nil).
|
||||
Expect(`git rebase --interactive --autostash --keep-empty --empty=keep --no-autosquash --rebase-merges abcdef`, "", nil).
|
||||
Expect(`git cat-file -e HEAD^:"test999.txt"`, "", nil).
|
||||
Expect(`git checkout HEAD^ -- "test999.txt"`, "", nil).
|
||||
Expect(`git commit --amend --no-edit --allow-empty`, "", nil).
|
||||
@ -143,8 +166,9 @@ func TestRebaseDiscardOldFileChanges(t *testing.T) {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
instance := buildRebaseCommands(commonDeps{
|
||||
runner: s.runner,
|
||||
gitConfig: git_config.NewFakeGitConfig(s.gitConfigMockResponses),
|
||||
runner: s.runner,
|
||||
gitVersion: &GitVersion{2, 26, 0, ""},
|
||||
gitConfig: git_config.NewFakeGitConfig(s.gitConfigMockResponses),
|
||||
})
|
||||
|
||||
s.test(instance.DiscardOldFileChanges(s.commits, s.commitIndex, s.fileName))
|
||||
|
@ -35,6 +35,7 @@ type editPreset struct {
|
||||
editInTerminal bool
|
||||
}
|
||||
|
||||
// IF YOU ADD A PRESET TO THIS FUNCTION YOU MUST UPDATE THE `Supported presets` SECTION OF docs/Config.md
|
||||
func getPreset(osConfig *OSConfig, guessDefaultEditor func() string) *editPreset {
|
||||
presets := map[string]*editPreset{
|
||||
"vi": standardTerminalEditorPreset("vi"),
|
||||
|
@ -80,6 +80,10 @@ func (self *guiCommon) ActivateContext(context types.Context) error {
|
||||
return self.gui.State.ContextMgr.ActivateContext(context, types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (self *guiCommon) ActivateContext(context types.Context) error {
|
||||
return self.gui.activateContext(context, types.OnFocusOpts{})
|
||||
}
|
||||
|
||||
func (self *guiCommon) GetAppState() *config.AppState {
|
||||
return self.gui.Config.GetAppState()
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ type GitVersionRestriction struct {
|
||||
}
|
||||
|
||||
// Verifies the version is at least the given version (inclusive)
|
||||
func From(version string) GitVersionRestriction {
|
||||
func AtLeast(version string) GitVersionRestriction {
|
||||
return GitVersionRestriction{from: version}
|
||||
}
|
||||
|
||||
|
@ -96,18 +96,18 @@ func TestGitVersionRestriction(t *testing.T) {
|
||||
expectedShouldRun bool
|
||||
}{
|
||||
{
|
||||
testName: "From, current is newer",
|
||||
gitVersion: From("2.24.9"),
|
||||
testName: "AtLeast, current is newer",
|
||||
gitVersion: AtLeast("2.24.9"),
|
||||
expectedShouldRun: true,
|
||||
},
|
||||
{
|
||||
testName: "From, current is same",
|
||||
gitVersion: From("2.25.0"),
|
||||
testName: "AtLeast, current is same",
|
||||
gitVersion: AtLeast("2.25.0"),
|
||||
expectedShouldRun: true,
|
||||
},
|
||||
{
|
||||
testName: "From, current is older",
|
||||
gitVersion: From("2.26.0"),
|
||||
testName: "AtLeast, current is older",
|
||||
gitVersion: AtLeast("2.26.0"),
|
||||
expectedShouldRun: false,
|
||||
},
|
||||
{
|
||||
|
@ -9,7 +9,7 @@ var DropTodoCommitWithUpdateRef = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Drops a commit during interactive rebase when there is an update-ref in the git-rebase-todo file",
|
||||
ExtraCmdArgs: "",
|
||||
Skip: false,
|
||||
GitVersion: From("2.38.0"),
|
||||
GitVersion: AtLeast("2.38.0"),
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.
|
||||
|
@ -9,7 +9,7 @@ var DropTodoCommitWithUpdateRefShowBranchHeads = NewIntegrationTest(NewIntegrati
|
||||
Description: "Drops a commit during interactive rebase when there is an update-ref in the git-rebase-todo file (with experimentalShowBranchHeads on)",
|
||||
ExtraCmdArgs: "",
|
||||
Skip: false,
|
||||
GitVersion: From("2.38.0"),
|
||||
GitVersion: AtLeast("2.38.0"),
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.UserConfig.Gui.ExperimentalShowBranchHeads = true
|
||||
},
|
||||
|
@ -9,6 +9,7 @@ var MoveToEarlierCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Move a patch from a commit to an earlier commit",
|
||||
ExtraCmdArgs: "",
|
||||
Skip: false,
|
||||
GitVersion: AtLeast("2.26.0"),
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateDir("dir")
|
||||
|
@ -0,0 +1,77 @@
|
||||
package patch_building
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
var MoveToEarlierCommitNoKeepEmpty = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Move a patch from a commit to an earlier commit, for older git versions that don't keep the empty commit",
|
||||
ExtraCmdArgs: "",
|
||||
Skip: false,
|
||||
GitVersion: Before("2.26.0"),
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateDir("dir")
|
||||
shell.CreateFileAndAdd("dir/file1", "file1 content")
|
||||
shell.CreateFileAndAdd("dir/file2", "file2 content")
|
||||
shell.Commit("first commit")
|
||||
|
||||
shell.CreateFileAndAdd("unrelated-file", "")
|
||||
shell.Commit("destination commit")
|
||||
|
||||
shell.UpdateFileAndAdd("dir/file1", "file1 content with old changes")
|
||||
shell.DeleteFileAndAdd("dir/file2")
|
||||
shell.CreateFileAndAdd("dir/file3", "file3 content")
|
||||
shell.Commit("commit to move from")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("commit to move from").IsSelected(),
|
||||
Contains("destination commit"),
|
||||
Contains("first commit"),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("dir").IsSelected(),
|
||||
Contains(" M file1"),
|
||||
Contains(" D file2"),
|
||||
Contains(" A file3"),
|
||||
).
|
||||
PressPrimaryAction().
|
||||
PressEscape()
|
||||
|
||||
t.Views().Information().Content(Contains("building patch"))
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
SelectNextItem()
|
||||
|
||||
t.Common().SelectPatchOption(Contains("move patch to selected commit"))
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("destination commit"),
|
||||
Contains("first commit").IsSelected(),
|
||||
).
|
||||
SelectPreviousItem().
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("dir").IsSelected(),
|
||||
Contains(" M file1"),
|
||||
Contains(" D file2"),
|
||||
Contains(" A file3"),
|
||||
Contains("A unrelated-file"),
|
||||
).
|
||||
PressEscape()
|
||||
},
|
||||
})
|
@ -117,6 +117,7 @@ var tests = []*components.IntegrationTest{
|
||||
patch_building.ApplyInReverseWithConflict,
|
||||
patch_building.CopyPatchToClipboard,
|
||||
patch_building.MoveToEarlierCommit,
|
||||
patch_building.MoveToEarlierCommitNoKeepEmpty,
|
||||
patch_building.MoveToIndex,
|
||||
patch_building.MoveToIndexPartOfAdjacentAddedLines,
|
||||
patch_building.MoveToIndexPartial,
|
||||
|
54
pkg/utils/yaml_utils/yaml_utils.go
Normal file
54
pkg/utils/yaml_utils/yaml_utils.go
Normal file
@ -0,0 +1,54 @@
|
||||
package yaml_utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// takes a yaml document in bytes, a path to a key, and a value to set. The value must be a scalar.
|
||||
func UpdateYaml(yamlBytes []byte, path []string, value string) ([]byte, error) {
|
||||
// Parse the YAML file.
|
||||
var node yaml.Node
|
||||
err := yaml.Unmarshal(yamlBytes, &node)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse YAML: %w", err)
|
||||
}
|
||||
|
||||
body := node.Content[0]
|
||||
|
||||
updateYamlNode(body, path, value)
|
||||
|
||||
// Convert the updated YAML node back to YAML bytes.
|
||||
updatedYAMLBytes, err := yaml.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to convert YAML node to bytes: %w", err)
|
||||
}
|
||||
|
||||
return updatedYAMLBytes, nil
|
||||
}
|
||||
|
||||
// Recursive function to update the YAML node.
|
||||
func updateYamlNode(node *yaml.Node, path []string, value string) {
|
||||
if len(path) == 0 {
|
||||
node.Value = value
|
||||
return
|
||||
}
|
||||
|
||||
key := path[0]
|
||||
for i := 0; i < len(node.Content)-1; i += 2 {
|
||||
if node.Content[i].Value == key {
|
||||
updateYamlNode(node.Content[i+1], path[1:], value)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// if the key doesn't exist, we'll add it
|
||||
node.Content = append(node.Content, &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Value: key,
|
||||
}, &yaml.Node{
|
||||
Kind: yaml.ScalarNode,
|
||||
Value: value,
|
||||
})
|
||||
}
|
64
pkg/utils/yaml_utils/yaml_utils_test.go
Normal file
64
pkg/utils/yaml_utils/yaml_utils_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
package yaml_utils
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestUpdateYaml(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
path []string
|
||||
value string
|
||||
expectedOut string
|
||||
expectedErr string
|
||||
}{
|
||||
{
|
||||
name: "update value",
|
||||
in: "foo: bar\n",
|
||||
path: []string{"foo"},
|
||||
value: "baz",
|
||||
expectedOut: "foo: baz\n",
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
name: "add new key and value",
|
||||
in: "foo: bar\n",
|
||||
path: []string{"foo2"},
|
||||
value: "baz",
|
||||
expectedOut: "foo: bar\nfoo2: baz\n",
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
name: "preserve inline comment",
|
||||
in: "foo: bar # my comment\n",
|
||||
path: []string{"foo2"},
|
||||
value: "baz",
|
||||
expectedOut: "foo: bar # my comment\nfoo2: baz\n",
|
||||
expectedErr: "",
|
||||
},
|
||||
{
|
||||
name: "nested update",
|
||||
in: "foo:\n bar: baz\n",
|
||||
path: []string{"foo", "bar"},
|
||||
value: "qux",
|
||||
// indentation is not preserved. See https://github.com/go-yaml/yaml/issues/899
|
||||
expectedOut: "foo:\n bar: qux\n",
|
||||
expectedErr: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
out, err := UpdateYaml([]byte(test.in), test.path, test.value)
|
||||
if test.expectedErr != "" {
|
||||
if err == nil {
|
||||
t.Errorf("expected error %q but got none", test.expectedErr)
|
||||
}
|
||||
} else if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
} else if string(out) != test.expectedOut {
|
||||
t.Errorf("expected %q but got %q", test.expectedOut, string(out))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user