Merge branch 'master' into gh-integration

This commit is contained in:
Yuki Osaki 2022-08-22 11:50:50 +09:00 committed by GitHub
commit c06974b879
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
117 changed files with 627 additions and 198 deletions

10
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,10 @@
- **PR Description**
- **Please check if the PR fulfills these requirements**
* [ ] Cheatsheets are up-to-date (run `go run scripts/cheatsheet/main.go generate`)
* [ ] Code has been formatted (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#code-formatting))
* [ ] Tests have been added/updated (see [here](https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md) for the integration test guide)
* [ ] Text is internationalised (see [here](https://github.com/jesseduffield/lazygit/blob/master/CONTRIBUTING.md#internationalisation))
* [ ] Docs (specifically `docs/Config.md`) have been updated if necessary
* [ ] You've read through your own file changes for silly mistakes etc

View file

@ -135,7 +135,7 @@ jobs:
- name: Build integration test injector
run: |
GOOS=linux go build pkg/integration/clients/injector/main.go
check-cheatsheet:
check-codebase:
runs-on: ubuntu-latest
env:
GOFLAGS: -mod=vendor
@ -159,6 +159,10 @@ jobs:
- name: Check Cheatsheet
run: |
go run scripts/cheatsheet/main.go check
- name: Check Vendor Directory
# ensure our vendor directory matches up with our go modules
run: |
go mod vendor && git diff --exit-code || (echo "Unexpected change to vendor directory. Run 'go mod vendor' locally and commit the changes" && exit 1)
lint:
runs-on: ubuntu-latest
env:
@ -183,12 +187,6 @@ jobs:
uses: golangci/golangci-lint-action@v3.1.0
with:
version: latest
- name: Format code
run: |
if [ $(find . ! -path "./vendor/*" -name "*.go" -exec gofmt -s -d {} \;|wc -l) -gt 0 ]; then
find . ! -path "./vendor/*" -name "*.go" -exec gofmt -s -d {} \;
exit 1
fi
- name: errors
run: golangci-lint run
if: ${{ failure() }}

View file

@ -69,9 +69,15 @@ by setting [`formatting.gofumpt`](https://github.com/golang/tools/blob/master/go
}
```
To run gofumpt from your terminal go:
```
go install mvdan.cc/gofumpt@latest && gofumpt -l -w .
```
## Internationalisation
Boy that's a hard word to spell. Anyway, lazygit is translated into several languages within the pkg/i18n package. If you need to render text to the user, you should add a new field to the TranslationSet struct in `pkg/i18n/english.go` and add the actual content within the `EnglishTranslationSet()` method in the same file. Although it is appreciated if you translate the text into other languages, it's not expected of you (google translate will likely do a bad job anyway!).
Boy that's a hard word to spell. Anyway, lazygit is translated into several languages within the pkg/i18n package. If you need to render text to the user, you should add a new field to the TranslationSet struct in `pkg/i18n/english.go` and add the actual content within the `EnglishTranslationSet()` method in the same file. Then you can access via `gui.Tr.YourNewText` (or `app.Tr.YourNewText`, etc). Although it is appreciated if you translate the text into other languages, it's not expected of you (google translate will likely do a bad job anyway!).
## Debugging

File diff suppressed because one or more lines are too long

View file

@ -13,7 +13,7 @@ Usage:
See https://github.com/jesseduffield/lazygit/tree/master/pkg/integration/README.md
CLI mode:
> go run cmd/integration_test/main.go cli <test1> <test2> ...
> go run cmd/integration_test/main.go cli [--slow] <test1> <test2> ...
If you pass no test names, it runs all tests
Accepted environment variables:
KEY_PRESS_DELAY (e.g. 200): the number of milliseconds to wait between keypresses
@ -40,7 +40,14 @@ func main() {
case "help":
fmt.Println(usage)
case "cli":
clients.RunCLI(os.Args[2:])
testNames := os.Args[2:]
slow := false
// get the next arg if it's --slow
if len(os.Args) > 2 && (os.Args[2] == "--slow" || os.Args[2] == "-slow") {
testNames = os.Args[3:]
slow = true
}
clients.RunCLI(testNames, slow)
case "tui":
clients.RunTUI()
default:

2
go.mod
View file

@ -18,7 +18,7 @@ require (
github.com/integrii/flaggy v1.4.0
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4
github.com/jesseduffield/gocui v0.3.1-0.20220813101052-3a3ab26faa15
github.com/jesseduffield/gocui v0.3.1-0.20220815095708-156fda5e0419
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
github.com/jesseduffield/yaml v2.1.0+incompatible

4
go.sum
View file

@ -72,8 +72,8 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4 h1:GOQrmaE8i+KEdB8NzAegKYd4tPn/inM0I1uo0NXFerg=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20201006095850-341962be15a4/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
github.com/jesseduffield/gocui v0.3.1-0.20220813101052-3a3ab26faa15 h1:DTVj8aCmINqLj5AXBEGmpWwfN1HJ3EWtUiYfcyIaSxs=
github.com/jesseduffield/gocui v0.3.1-0.20220813101052-3a3ab26faa15/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
github.com/jesseduffield/gocui v0.3.1-0.20220815095708-156fda5e0419 h1:p3Ix7RUcy4X16Lk5jTSfTxecJT7ryqYHclfRbo/Svzs=
github.com/jesseduffield/gocui v0.3.1-0.20220815095708-156fda5e0419/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e h1:uw/oo+kg7t/oeMs6sqlAwr85ND/9cpO3up3VxphxY0U=

View file

@ -16,6 +16,7 @@ import (
"github.com/jesseduffield/generics/slices"
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
"github.com/jesseduffield/lazygit/pkg/commands"
"github.com/jesseduffield/lazygit/pkg/commands/git_commands"
"github.com/jesseduffield/lazygit/pkg/commands/git_config"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/common"
@ -200,6 +201,18 @@ func isDirectoryAGitRepository(dir string) (bool, error) {
return info != nil, err
}
func openRecentRepo(app *App) bool {
for _, repoDir := range app.Config.GetAppState().RecentRepos {
if isRepo, _ := isDirectoryAGitRepository(repoDir); isRepo {
if err := os.Chdir(repoDir); err == nil {
return true
}
}
}
return false
}
func (app *App) setupRepo() (bool, error) {
if err := app.validateGitVersion(); err != nil {
return false, err
@ -216,6 +229,7 @@ func (app *App) setupRepo() (bool, error) {
if err != nil {
return false, err
}
if isRepo, err := isDirectoryAGitRepository(cwd); isRepo {
return false, err
}
@ -268,6 +282,28 @@ func (app *App) setupRepo() (bool, error) {
os.Exit(1)
}
// Run this afterward so that the previous repo creation steps can run without this interfering
if isBare, err := git_commands.IsBareRepo(app.OSCommand); isBare {
if err != nil {
return false, err
}
fmt.Print(app.Tr.BareRepo)
response, _ := bufio.NewReader(os.Stdin).ReadString('\n')
if shouldOpenRecent := strings.Trim(response, " \r\n") == "y"; !shouldOpenRecent {
os.Exit(0)
}
if didOpenRepo := openRecentRepo(app); didOpenRepo {
return true, nil
}
fmt.Println(app.Tr.NoRecentRepositories)
os.Exit(1)
}
return false, nil
}

View file

@ -2,8 +2,10 @@ package git_commands
import (
"path/filepath"
"strconv"
"strings"
gogit "github.com/jesseduffield/go-git/v5"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/commands/types/enums"
)
@ -49,13 +51,21 @@ func (self *StatusCommands) WorkingTreeState() enums.RebaseMode {
return enums.REBASE_MODE_NONE
}
func (self *StatusCommands) IsBareRepo() (bool, error) {
return IsBareRepo(self.os)
}
func IsBareRepo(osCommand *oscommands.OSCommand) (bool, error) {
res, err := osCommand.Cmd.New("git rev-parse --is-bare-repository").DontLog().RunWithOutput()
if err != nil {
return false, err
}
// The command returns output with a newline, so we need to strip
return strconv.ParseBool(strings.TrimSpace(res))
}
// IsInMergeState states whether we are still mid-merge
func (self *StatusCommands) IsInMergeState() (bool, error) {
return self.os.FileExists(filepath.Join(self.dotGitDir, "MERGE_HEAD"))
}
func (self *StatusCommands) IsBareRepo() bool {
// note: could use `git rev-parse --is-bare-repository` if we wanna drop go-git
_, err := self.repo.Worktree()
return err == gogit.ErrIsBareRepository
}

View file

@ -315,7 +315,9 @@ type CustomCommand struct {
}
type CustomCommandPrompt struct {
Type string `yaml:"type"` // one of 'input', 'menu', or 'confirm'
// one of 'input', 'menu', 'confirm', or 'menuFromCommand'
Type string `yaml:"type"`
Title string `yaml:"title"`
// this only apply to input prompts

View file

@ -71,3 +71,11 @@ func (self *GuiDriver) LogUI(message string) {
func (self *GuiDriver) CheckedOutRef() *models.Branch {
return self.gui.helpers.Refs.GetCheckedOutRef()
}
func (self *GuiDriver) MainView() *gocui.View {
return self.gui.mainView()
}
func (self *GuiDriver) SecondaryView() *gocui.View {
return self.gui.secondaryView()
}

View file

@ -159,7 +159,12 @@ func (gui *Gui) dispatchSwitchToRepo(path string, reuse bool) error {
// updateRecentRepoList registers the fact that we opened lazygit in this repo,
// so that we can open the same repo via the 'recent repos' menu
func (gui *Gui) updateRecentRepoList() error {
if gui.git.Status.IsBareRepo() {
isBareRepo, err := gui.git.Status.IsBareRepo()
if err != nil {
return err
}
if isBareRepo {
// we could totally do this but it would require storing both the git-dir and the
// worktree in our recent repos list, which is a change that would need to be
// backwards compatible

View file

@ -259,6 +259,7 @@ type TranslationSet struct {
DiscardFileChangesPrompt string
DisabledForGPG string
CreateRepo string
BareRepo string
InitialBranch string
NoRecentRepositories string
IncorrectNotARepository string
@ -901,6 +902,7 @@ func EnglishTranslationSet() TranslationSet {
DiscardFileChangesPrompt: "Are you sure you want to discard this commit's changes to this file? If this file was created in this commit, it will be deleted",
DisabledForGPG: "Feature not available for users using GPG",
CreateRepo: "Not in a git repository. Create a new git repository? (y/n): ",
BareRepo: "You've attempted to open Lazygit in a bare repo but Lazygit does not yet support bare repos. Open most recent repo? (y/n) ",
InitialBranch: "Branch name? (leave empty for git's default): ",
NoRecentRepositories: "Must open lazygit in a git repository. No valid recent repositories. Exiting.",
IncorrectNotARepository: "The value of 'notARepository' is incorrect. It should be one of 'prompt', 'create', 'skip', or 'quit'.",

View file

@ -1,6 +1,18 @@
# Integration Tests
The pkg/integration pacakge is for integration testing: that is, actually running a real lazygit session and having a robot pretend to be a human user and then making assertions that everything works as expected.
The pkg/integration package is for integration testing: that is, actually running a real lazygit session and having a robot pretend to be a human user and then making assertions that everything works as expected.
TL;DR: integration tests live in pkg/integration/tests. Run integration tests with:
```sh
go run cmd/integration_test/main.go tui
```
or
```sh
go run cmd/integration_test/main.go cli [--slow] [testname or testpath...]
```
## Writing tests
@ -37,7 +49,7 @@ If you find yourself doing something frequently in a test, consider making it a
There are three ways to invoke a test:
1. go run cmd/integration_test/main.go cli [<testname>...]
1. go run cmd/integration_test/main.go cli [--slow] [testname or testpath...]
2. go run cmd/integration_test/main.go tui
3. go test pkg/integration/clients/go_test.go
@ -47,7 +59,7 @@ The third, the go-test command, intended only for use in CI, to be run along wit
The name of a test is based on its path, so the name of the test at `pkg/integration/tests/commit/new_branch.go` is commit/new_branch. So to run it with our test runner you would run `go run cmd/integration_test/main.go cli commit/new_branch`.
You can pass the KEY_PRESS_DELAY env var to the test runner in order to set a delay in milliseconds between keypresses, which helps for watching a test at a realistic speed to understand what it's doing. Or in the tui you can press 't' to run the test with a pre-set delay.
You can pass the KEY_PRESS_DELAY env var to the test runner in order to set a delay in milliseconds between keypresses, which helps for watching a test at a realistic speed to understand what it's doing. Or you can pass the '--slow' flag which sets a pre-set 'slow' key delay. In the tui you can press 't' to run the test in slow mode.
### Snapshots

View file

@ -4,8 +4,11 @@ import (
"log"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/integration/components"
"github.com/jesseduffield/lazygit/pkg/integration/tests"
)
@ -20,14 +23,19 @@ import (
// If invoked directly, you can specify tests to run by passing their names as positional arguments
func RunCLI(testNames []string) {
func RunCLI(testNames []string, slow bool) {
keyPressDelay := tryConvert(os.Getenv("KEY_PRESS_DELAY"), 0)
if slow {
keyPressDelay = SLOW_KEY_PRESS_DELAY
}
err := components.RunTests(
getTestsToRun(testNames),
log.Printf,
runCmdInTerminal,
runAndPrintError,
getModeFromEnv(),
tryConvert(os.Getenv("KEY_PRESS_DELAY"), 0),
keyPressDelay,
)
if err != nil {
log.Print(err.Error())
@ -36,21 +44,30 @@ func RunCLI(testNames []string) {
func runAndPrintError(test *components.IntegrationTest, f func() error) {
if err := f(); err != nil {
log.Print(err.Error())
log.Fatalf(err.Error())
}
}
func getTestsToRun(testNames []string) []*components.IntegrationTest {
allIntegrationTests := tests.GetTests()
var testsToRun []*components.IntegrationTest
if len(testNames) == 0 {
return tests.Tests
return allIntegrationTests
}
testNames = slices.Map(testNames, func(name string) string {
// allowing full test paths to be passed for convenience
return strings.TrimSuffix(
regexp.MustCompile(`.*pkg/integration/tests/`).ReplaceAllString(name, ""),
".go",
)
})
outer:
for _, testName := range testNames {
// check if our given test name actually exists
for _, test := range tests.Tests {
for _, test := range allIntegrationTests {
if test.Name() == testName {
testsToRun = append(testsToRun, test)
continue outer

View file

@ -29,7 +29,7 @@ func TestIntegration(t *testing.T) {
testNumber := 0
err := components.RunTests(
tests.Tests,
tests.GetTests(),
t.Logf,
runCmdHeadless,
func(test *components.IntegrationTest, f func() error) {

View file

@ -52,7 +52,8 @@ func getIntegrationTest() integrationTypes.IntegrationTest {
))
}
for _, candidateTest := range tests.Tests {
allTests := tests.GetTests()
for _, candidateTest := range allTests {
if candidateTest.Name() == integrationTestName {
return candidateTest
}

View file

@ -19,6 +19,8 @@ import (
// This program lets you run integration tests from a TUI. See pkg/integration/README.md for more info.
var SLOW_KEY_PRESS_DELAY = 300
func RunTUI() {
rootDir := utils.GetLazygitRootDirectory()
testDir := filepath.Join(rootDir, "test", "integration")
@ -106,7 +108,7 @@ func RunTUI() {
return nil
}
suspendAndRunTest(currentTest, components.ASK_TO_UPDATE_SNAPSHOT, 200)
suspendAndRunTest(currentTest, components.ASK_TO_UPDATE_SNAPSHOT, SLOW_KEY_PRESS_DELAY)
return nil
}); err != nil {
@ -168,7 +170,7 @@ func RunTUI() {
return err
}
app.filteredTests = tests.Tests
app.filteredTests = app.allTests
app.renderTests()
app.editorView.TextArea.Clear()
app.editorView.Clear()
@ -204,6 +206,7 @@ func RunTUI() {
}
type app struct {
allTests []*components.IntegrationTest
filteredTests []*components.IntegrationTest
itemIdx int
testDir string
@ -214,7 +217,7 @@ type app struct {
}
func newApp(testDir string) *app {
return &app{testDir: testDir}
return &app{testDir: testDir, allTests: tests.GetTests()}
}
func (self *app) getCurrentTest() *components.IntegrationTest {
@ -226,7 +229,7 @@ func (self *app) getCurrentTest() *components.IntegrationTest {
}
func (self *app) loadTests() {
self.filteredTests = tests.Tests
self.filteredTests = self.allTests
self.adjustCursor()
}
@ -237,9 +240,9 @@ func (self *app) adjustCursor() {
func (self *app) filterWithString(needle string) {
if needle == "" {
self.filteredTests = tests.Tests
self.filteredTests = self.allTests
} else {
self.filteredTests = slices.Filter(tests.Tests, func(test *components.IntegrationTest) bool {
self.filteredTests = slices.Filter(self.allTests, func(test *components.IntegrationTest) bool {
return strings.Contains(test.Name(), needle)
})
}

View file

@ -19,6 +19,43 @@ func NewAssert(gui integrationTypes.GuiDriver) *Assert {
return &Assert{gui: gui}
}
// for making assertions on string values
type matcher struct {
testFn func(string) (bool, string)
prefix string
}
func (self *matcher) test(value string) (bool, string) {
ok, message := self.testFn(value)
if ok {
return true, ""
}
if self.prefix != "" {
return false, self.prefix + " " + message
}
return false, message
}
func (self *matcher) context(prefix string) *matcher {
self.prefix = prefix
return self
}
func Contains(target string) *matcher {
return &matcher{testFn: func(value string) (bool, string) {
return strings.Contains(value, target), fmt.Sprintf("Expected '%s' to contain '%s'", value, target)
}}
}
func Equals(target string) *matcher {
return &matcher{testFn: func(value string) (bool, string) {
return target == value, fmt.Sprintf("Expected '%s' to equal '%s'", value, target)
}}
}
func (self *Assert) WorkingTreeFileCount(expectedCount int) {
self.assertWithRetries(func() (bool, string) {
actualCount := len(self.gui.Model().Files)
@ -41,22 +78,16 @@ func (self *Assert) CommitCount(expectedCount int) {
})
}
func (self *Assert) HeadCommitMessage(expectedMessage string) {
func (self *Assert) MatchHeadCommitMessage(matcher *matcher) {
self.assertWithRetries(func() (bool, string) {
if len(self.gui.Model().Commits) == 0 {
return false, "Expected at least one commit to be present"
}
headCommit := self.gui.Model().Commits[0]
if headCommit.Name != expectedMessage {
return false, fmt.Sprintf(
"Expected commit message to be '%s', but got '%s'",
expectedMessage, headCommit.Name,
)
}
return true, ""
return len(self.gui.Model().Commits) > 0, "Expected at least one commit to be present"
})
self.matchString(matcher, "Unexpected commit message.",
func() string {
return self.gui.Model().Commits[0].Name
},
)
}
func (self *Assert) CurrentViewName(expectedViewName string) {
@ -81,10 +112,70 @@ func (self *Assert) InListContext() {
})
}
func (self *Assert) SelectedLineContains(text string) {
func (self *Assert) MatchSelectedLine(matcher *matcher) {
self.matchString(matcher, "Unexpected selected line.",
func() string {
return self.gui.CurrentContext().GetView().SelectedLine()
},
)
}
func (self *Assert) InPrompt() {
self.assertWithRetries(func() (bool, string) {
line := self.gui.CurrentContext().GetView().SelectedLine()
return strings.Contains(line, text), fmt.Sprintf("Expected selected line to contain '%s', but got '%s'", text, line)
currentView := self.gui.CurrentContext().GetView()
return currentView.Name() == "confirmation" && currentView.Editable, "Expected prompt popup to be focused"
})
}
func (self *Assert) InConfirm() {
self.assertWithRetries(func() (bool, string) {
currentView := self.gui.CurrentContext().GetView()
return currentView.Name() == "confirmation" && !currentView.Editable, "Expected confirmation popup to be focused"
})
}
func (self *Assert) InAlert() {
// basically the same thing as a confirmation popup with the current implementation
self.assertWithRetries(func() (bool, string) {
currentView := self.gui.CurrentContext().GetView()
return currentView.Name() == "confirmation" && !currentView.Editable, "Expected alert popup to be focused"
})
}
func (self *Assert) InMenu() {
self.assertWithRetries(func() (bool, string) {
return self.gui.CurrentContext().GetView().Name() == "menu", "Expected popup menu to be focused"
})
}
func (self *Assert) MatchCurrentViewTitle(matcher *matcher) {
self.matchString(matcher, "Unexpected current view title.",
func() string {
return self.gui.CurrentContext().GetView().Title
},
)
}
func (self *Assert) MatchMainViewContent(matcher *matcher) {
self.matchString(matcher, "Unexpected main view content.",
func() string {
return self.gui.MainView().Buffer()
},
)
}
func (self *Assert) MatchSecondaryViewContent(matcher *matcher) {
self.matchString(matcher, "Unexpected secondary view title.",
func() string {
return self.gui.SecondaryView().Buffer()
},
)
}
func (self *Assert) matchString(matcher *matcher, context string, getValue func() string) {
self.assertWithRetries(func() (bool, string) {
value := getValue()
return matcher.context(context).test(value)
})
}

View file

@ -77,7 +77,7 @@ func (self *Input) Cancel() {
}
// i.e. pressing space
func (self *Input) Select() {
func (self *Input) PrimaryAction() {
self.pressKey(self.keys.Universal.Select)
}
@ -93,7 +93,7 @@ func (self *Input) PreviousItem() {
func (self *Input) ContinueMerge() {
self.PressKeys(self.keys.Universal.CreateRebaseOptionsMenu)
self.assert.SelectedLineContains("continue")
self.assert.MatchSelectedLine(Contains("continue"))
self.Confirm()
}

View file

@ -136,9 +136,10 @@ func createFixture(test *IntegrationTest, paths Paths) error {
}
shell := NewShell()
shell.RunCommand("git init")
shell.RunCommand("git init -b master")
shell.RunCommand(`git config user.email "CI@example.com"`)
shell.RunCommand(`git config user.name "CI"`)
shell.RunCommand(`git config commit.gpgSign false`)
test.SetupRepo(shell)

View file

@ -53,7 +53,7 @@ func NewIntegrationTest(args NewIntegrationTestArgs) *IntegrationTest {
if args.Description != unitTestDescription {
// this panics if we're in a unit test for our integration tests,
// so we're using "test test" as a sentinel value
name = testNameFromFilePath()
name = testNameFromCurrentFilePath()
}
return &IntegrationTest{
@ -106,8 +106,12 @@ func (self *IntegrationTest) Run(gui integrationTypes.GuiDriver) {
}
}
func testNameFromFilePath() string {
func testNameFromCurrentFilePath() string {
path := utils.FilePath(3)
return TestNameFromFilePath(path)
}
func TestNameFromFilePath(path string) string {
name := strings.Split(path, "integration/tests/")[1]
return name[:len(name)-len(".go")]

View file

@ -3,6 +3,7 @@ package components
import (
"testing"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/types"
@ -47,6 +48,14 @@ func (self *fakeGuiDriver) CheckedOutRef() *models.Branch {
return nil
}
func (self *fakeGuiDriver) MainView() *gocui.View {
return nil
}
func (self *fakeGuiDriver) SecondaryView() *gocui.View {
return nil
}
func TestAssertionFailure(t *testing.T) {
test := NewIntegrationTest(NewIntegrationTestArgs{
Description: unitTestDescription,

View file

@ -2,24 +2,24 @@ package commit
import (
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/integration/components"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var Commit = components.NewIntegrationTest(components.NewIntegrationTestArgs{
var Commit = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Staging a couple files and committing",
ExtraCmdArgs: "",
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *components.Shell) {
SetupRepo: func(shell *Shell) {
shell.CreateFile("myfile", "myfile content")
shell.CreateFile("myfile2", "myfile2 content")
},
Run: func(shell *components.Shell, input *components.Input, assert *components.Assert, keys config.KeybindingConfig) {
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
assert.CommitCount(0)
input.Select()
input.PrimaryAction()
input.NextItem()
input.Select()
input.PrimaryAction()
input.PressKeys(keys.Files.CommitChanges)
commitMessage := "my commit message"
@ -27,6 +27,6 @@ var Commit = components.NewIntegrationTest(components.NewIntegrationTestArgs{
input.Confirm()
assert.CommitCount(1)
assert.HeadCommitMessage(commitMessage)
assert.MatchHeadCommitMessage(Equals(commitMessage))
},
})

View file

@ -2,21 +2,21 @@ package commit
import (
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/integration/components"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var NewBranch = components.NewIntegrationTest(components.NewIntegrationTestArgs{
var NewBranch = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Creating a new branch from a commit",
ExtraCmdArgs: "",
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *components.Shell) {
SetupRepo: func(shell *Shell) {
shell.
EmptyCommit("commit 1").
EmptyCommit("commit 2").
EmptyCommit("commit 3")
},
Run: func(shell *components.Shell, input *components.Input, assert *components.Assert, keys config.KeybindingConfig) {
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
assert.CommitCount(3)
input.SwitchToCommitsWindow()
@ -32,7 +32,7 @@ var NewBranch = components.NewIntegrationTest(components.NewIntegrationTestArgs{
input.Confirm()
assert.CommitCount(2)
assert.HeadCommitMessage("commit 2")
assert.MatchHeadCommitMessage(Contains("commit 2"))
assert.CurrentBranchName(branchName)
},
})

View file

@ -0,0 +1,36 @@
package custom_commands
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var Basic = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Using a custom command to create a new file",
ExtraCmdArgs: "",
Skip: false,
SetupRepo: func(shell *Shell) {
shell.EmptyCommit("blah")
},
SetupConfig: func(cfg *config.AppConfig) {
cfg.UserConfig.CustomCommands = []config.CustomCommand{
{
Key: "a",
Context: "files",
Command: "touch myfile",
},
}
},
Run: func(
shell *Shell,
input *Input,
assert *Assert,
keys config.KeybindingConfig,
) {
assert.WorkingTreeFileCount(0)
input.PressKeys("a")
assert.WorkingTreeFileCount(1)
assert.MatchSelectedLine(Contains("myfile"))
},
})

View file

@ -0,0 +1,74 @@
package custom_commands
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
// NOTE: we're getting a weird offset in the popup prompt for some reason. Not sure what's behind that.
var MenuFromCommand = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Using menuFromCommand prompt type",
ExtraCmdArgs: "",
Skip: false,
SetupRepo: func(shell *Shell) {
shell.
EmptyCommit("foo").
EmptyCommit("bar").
EmptyCommit("baz").
NewBranch("feature/foo")
},
SetupConfig: func(cfg *config.AppConfig) {
cfg.UserConfig.CustomCommands = []config.CustomCommand{
{
Key: "a",
Context: "localBranches",
Command: `echo "{{index .PromptResponses 0}} {{index .PromptResponses 1}} {{ .SelectedLocalBranch.Name }}" > output.txt`,
Prompts: []config.CustomCommandPrompt{
{
Type: "menuFromCommand",
Title: "Choose commit message",
Command: `git log --oneline --pretty=%B`,
Filter: `(?P<commit_message>.*)`,
ValueFormat: `{{ .commit_message }}`,
LabelFormat: `{{ .commit_message | yellow }}`,
},
{
Type: "input",
Title: "Description",
InitialValue: `{{ if .SelectedLocalBranch.Name }}Branch: #{{ .SelectedLocalBranch.Name }}{{end}}`,
},
},
},
}
},
Run: func(
shell *Shell,
input *Input,
assert *Assert,
keys config.KeybindingConfig,
) {
assert.WorkingTreeFileCount(0)
input.SwitchToBranchesWindow()
input.PressKeys("a")
assert.InMenu()
assert.MatchCurrentViewTitle(Equals("Choose commit message"))
assert.MatchSelectedLine(Equals("baz"))
input.NextItem()
assert.MatchSelectedLine(Equals("bar"))
input.Confirm()
assert.InPrompt()
assert.MatchCurrentViewTitle(Equals("Description"))
input.Type(" my branch")
input.Confirm()
input.SwitchToFilesWindow()
assert.WorkingTreeFileCount(1)
assert.MatchSelectedLine(Contains("output.txt"))
assert.MatchMainViewContent(Contains("bar Branch: #feature/foo my branch feature/foo"))
},
})

View file

@ -0,0 +1,86 @@
package custom_commands
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var MultiplePrompts = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Using a custom command with multiple prompts",
ExtraCmdArgs: "",
Skip: false,
SetupRepo: func(shell *Shell) {
shell.EmptyCommit("blah")
},
SetupConfig: func(cfg *config.AppConfig) {
cfg.UserConfig.CustomCommands = []config.CustomCommand{
{
Key: "a",
Context: "files",
Command: `echo "{{index .PromptResponses 1}}" > {{index .PromptResponses 0}}`,
Prompts: []config.CustomCommandPrompt{
{
Type: "input",
Title: "Enter a file name",
},
{
Type: "menu",
Title: "Choose file content",
Options: []config.CustomCommandMenuOption{
{
Name: "foo",
Description: "Foo",
Value: "FOO",
},
{
Name: "bar",
Description: "Bar",
Value: "BAR",
},
{
Name: "baz",
Description: "Baz",
Value: "BAZ",
},
},
},
{
Type: "confirm",
Title: "Are you sure?",
Body: "Are you REALLY sure you want to make this file? Up to you buddy.",
},
},
},
}
},
Run: func(
shell *Shell,
input *Input,
assert *Assert,
keys config.KeybindingConfig,
) {
assert.WorkingTreeFileCount(0)
input.PressKeys("a")
assert.InPrompt()
assert.MatchCurrentViewTitle(Equals("Enter a file name"))
input.Type("myfile")
input.Confirm()
assert.InMenu()
assert.MatchCurrentViewTitle(Equals("Choose file content"))
assert.MatchSelectedLine(Contains("foo"))
input.NextItem()
assert.MatchSelectedLine(Contains("bar"))
input.Confirm()
assert.InConfirm()
assert.MatchCurrentViewTitle(Equals("Are you sure?"))
input.Confirm()
assert.WorkingTreeFileCount(1)
assert.MatchSelectedLine(Contains("myfile"))
assert.MatchMainViewContent(Contains("BAR"))
},
})

View file

@ -2,37 +2,37 @@ package interactive_rebase
import (
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/integration/components"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var One = components.NewIntegrationTest(components.NewIntegrationTestArgs{
var One = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Begins an interactive rebase, then fixups, drops, and squashes some commits",
ExtraCmdArgs: "",
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *components.Shell) {
SetupRepo: func(shell *Shell) {
shell.
CreateNCommits(5) // these will appears at commit 05, 04, 04, down to 01
},
Run: func(shell *components.Shell, input *components.Input, assert *components.Assert, keys config.KeybindingConfig) {
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
input.SwitchToCommitsWindow()
assert.CurrentViewName("commits")
input.NavigateToListItemContainingText("commit 02")
input.PressKeys(keys.Universal.Edit)
assert.SelectedLineContains("YOU ARE HERE")
assert.MatchSelectedLine(Contains("YOU ARE HERE"))
input.PreviousItem()
input.PressKeys(keys.Commits.MarkCommitAsFixup)
assert.SelectedLineContains("fixup")
assert.MatchSelectedLine(Contains("fixup"))
input.PreviousItem()
input.PressKeys(keys.Universal.Remove)
assert.SelectedLineContains("drop")
assert.MatchSelectedLine(Contains("drop"))
input.PreviousItem()
input.PressKeys(keys.Commits.SquashDown)
assert.SelectedLineContains("squash")
assert.MatchSelectedLine(Contains("squash"))
input.ContinueRebase()

View file

@ -1,18 +1,74 @@
package tests
import (
"fmt"
"os"
"path/filepath"
"strings"
"github.com/jesseduffield/generics/set"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/lazygit/pkg/integration/components"
"github.com/jesseduffield/lazygit/pkg/integration/tests/branch"
"github.com/jesseduffield/lazygit/pkg/integration/tests/commit"
"github.com/jesseduffield/lazygit/pkg/integration/tests/custom_commands"
"github.com/jesseduffield/lazygit/pkg/integration/tests/interactive_rebase"
"github.com/jesseduffield/lazygit/pkg/utils"
)
// Here is where we lists the actual tests that will run. When you create a new test,
// be sure to add it to this list.
var Tests = []*components.IntegrationTest{
var tests = []*components.IntegrationTest{
commit.Commit,
commit.NewBranch,
branch.Suggestions,
interactive_rebase.One,
custom_commands.Basic,
custom_commands.MultiplePrompts,
custom_commands.MenuFromCommand,
}
func GetTests() []*components.IntegrationTest {
// first we ensure that each test in this directory has actually been added to the above list.
testCount := 0
testNamesSet := set.NewFromSlice(slices.Map(
tests,
func(test *components.IntegrationTest) string {
return test.Name()
},
))
missingTestNames := []string{}
if err := filepath.Walk(filepath.Join(utils.GetLazygitRootDirectory(), "pkg/integration/tests"), func(path string, info os.FileInfo, err error) error {
if !info.IsDir() && strings.HasSuffix(path, ".go") {
// ignoring this current file
if filepath.Base(path) == "tests.go" {
return nil
}
nameFromPath := components.TestNameFromFilePath(path)
if !testNamesSet.Includes(nameFromPath) {
missingTestNames = append(missingTestNames, nameFromPath)
}
testCount++
}
return nil
}); err != nil {
panic(fmt.Sprintf("failed to walk tests: %v", err))
}
if len(missingTestNames) > 0 {
panic(fmt.Sprintf("The following tests are missing from the list of tests: %s. You need to add them to `pkg/integration/tests/tests.go`.", strings.Join(missingTestNames, ", ")))
}
if testCount > len(tests) {
panic("you have not added all of the tests to the tests list in `pkg/integration/tests/tests.go`")
} else if testCount < len(tests) {
panic("There are more tests in `pkg/integration/tests/tests.go` than there are test files in the tests directory. Ensure that you only have one test per file and you haven't included the same test twice in the tests list.")
}
return tests
}

View file

@ -1,6 +1,7 @@
package types
import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/types"
@ -28,4 +29,9 @@ type GuiDriver interface {
// logs in the actual UI (in the commands panel)
LogUI(message string)
CheckedOutRef() *models.Branch
// the view that appears to the right of the side panel
MainView() *gocui.View
// the other view that sometimes appears to the right of the side panel
// e.g. when we're showing both staged and unstaged changes
SecondaryView() *gocui.View
}

View file

@ -1,18 +0,0 @@
disableStartupPopups: true
customCommands:
- key : 'N'
description: 'Add file'
command: "echo {{index .PromptResponses 0}} > {{index .PromptResponses 1}}"
context: 'files'
prompts:
- type: 'input'
title: 'File name:'
- type: 'input'
title: 'File content:'
gui:
theme:
activeBorderColor:
- green
- bold
SelectedRangeBgcolor:
- reverse

View file

@ -1 +0,0 @@
0000000000000000000000000000000000000000 15bdb2c31c825116ad5af06ee25517d90b24f13b CI <CI@example.com> 1617684452 +1000 commit (initial): test

View file

@ -1 +0,0 @@
0000000000000000000000000000000000000000 15bdb2c31c825116ad5af06ee25517d90b24f13b CI <CI@example.com> 1617684452 +1000 commit (initial): test

View file

@ -1 +0,0 @@
15bdb2c31c825116ad5af06ee25517d90b24f13b

View file

@ -1 +0,0 @@
myfile

View file

@ -1 +0,0 @@
{"KeyEvents":[{"Timestamp":837,"Mod":0,"Key":256,"Ch":78},{"Timestamp":1622,"Mod":0,"Key":256,"Ch":109},{"Timestamp":1798,"Mod":0,"Key":256,"Ch":121},{"Timestamp":1918,"Mod":0,"Key":256,"Ch":102},{"Timestamp":2006,"Mod":0,"Key":256,"Ch":105},{"Timestamp":2078,"Mod":0,"Key":256,"Ch":108},{"Timestamp":2174,"Mod":0,"Key":256,"Ch":101},{"Timestamp":2431,"Mod":0,"Key":13,"Ch":13},{"Timestamp":3246,"Mod":0,"Key":256,"Ch":98},{"Timestamp":3294,"Mod":0,"Key":256,"Ch":108},{"Timestamp":3398,"Mod":0,"Key":256,"Ch":97},{"Timestamp":3462,"Mod":0,"Key":256,"Ch":104},{"Timestamp":3735,"Mod":0,"Key":13,"Ch":13},{"Timestamp":4206,"Mod":0,"Key":256,"Ch":32},{"Timestamp":4421,"Mod":0,"Key":256,"Ch":99},{"Timestamp":4646,"Mod":0,"Key":256,"Ch":116},{"Timestamp":4726,"Mod":0,"Key":256,"Ch":101},{"Timestamp":4886,"Mod":0,"Key":256,"Ch":115},{"Timestamp":4918,"Mod":0,"Key":256,"Ch":116},{"Timestamp":5190,"Mod":0,"Key":13,"Ch":13},{"Timestamp":5550,"Mod":0,"Key":256,"Ch":113}],"ResizeEvents":[{"Timestamp":0,"Width":272,"Height":74}]}

View file

@ -1,10 +0,0 @@
#!/bin/sh
set -e
cd $1
git init
git config user.email "CI@example.com"
git config user.name "CI"

View file

@ -1 +0,0 @@
{ "description": "Invoke a custom command that creates a file, and then stage and commit that file", "speed": 5 }

View file

@ -1,31 +0,0 @@
disableStartupPopups: true
customCommands:
- key: 'N'
description: 'Add file'
context: 'localBranches'
command: 'echo "{{index .PromptResponses 0}} {{index .PromptResponses 1}} {{index .PromptResponses 2}} {{ .SelectedLocalBranch.Name }}" > output.txt'
loadingText: 'Running custom command...'
prompts:
- type: 'menuFromCommand'
title: 'Title'
command: 'git log --oneline --pretty=%B'
filter: '(?P<commit_message>.*)'
valueFormat: '{{ .commit_message }}'
labelFormat: '{{ .commit_message | yellow }}'
- type: 'input'
title: 'Description'
initialValue: "{{ if .SelectedLocalBranch.Name }}Branch: #{{ .SelectedLocalBranch.Name }}{{end}}"
- type: 'menu'
title: 'yes or no'
options:
- name: 'no'
value: 'false'
- name: 'yes'
value: 'true'
gui:
theme:
activeBorderColor:
- green
- bold
SelectedRangeBgcolor:
- reverse

View file

@ -1,5 +0,0 @@
0000000000000000000000000000000000000000 ab38b1ca116f77648925d952e731f419db360cdb CI <CI@example.com> 1642201096 +1100 commit (initial): myfile1
ab38b1ca116f77648925d952e731f419db360cdb 4fdfedfd9d406506be8b02f5b863dbc08d43cc9f CI <CI@example.com> 1642201096 +1100 commit: myfile2
4fdfedfd9d406506be8b02f5b863dbc08d43cc9f 7dd93a4be3d27d40fbe791d6d77e0d2fedc4d785 CI <CI@example.com> 1642201096 +1100 commit: myfile3
7dd93a4be3d27d40fbe791d6d77e0d2fedc4d785 f708d3e3819470a69f6c8562ff1e68eef02f8cac CI <CI@example.com> 1642201096 +1100 commit: myfile4
f708d3e3819470a69f6c8562ff1e68eef02f8cac 5428838691c97ac192c8b8e1c3f573d8541a94b6 CI <CI@example.com> 1642201104 +1100 commit: test

View file

@ -1,5 +0,0 @@
0000000000000000000000000000000000000000 ab38b1ca116f77648925d952e731f419db360cdb CI <CI@example.com> 1642201096 +1100 commit (initial): myfile1
ab38b1ca116f77648925d952e731f419db360cdb 4fdfedfd9d406506be8b02f5b863dbc08d43cc9f CI <CI@example.com> 1642201096 +1100 commit: myfile2
4fdfedfd9d406506be8b02f5b863dbc08d43cc9f 7dd93a4be3d27d40fbe791d6d77e0d2fedc4d785 CI <CI@example.com> 1642201096 +1100 commit: myfile3
7dd93a4be3d27d40fbe791d6d77e0d2fedc4d785 f708d3e3819470a69f6c8562ff1e68eef02f8cac CI <CI@example.com> 1642201096 +1100 commit: myfile4
f708d3e3819470a69f6c8562ff1e68eef02f8cac 5428838691c97ac192c8b8e1c3f573d8541a94b6 CI <CI@example.com> 1642201104 +1100 commit: test

View file

@ -1,2 +0,0 @@
x<01>ÍA
ƒ0@Ñ®sŠÙÊL:#)¸ò1™PÁ!")ØÛ×#tûyðS5[Ë¥íª€*©`”¹ë5df¥ 9<><39>T:žùž…KLïâ§½ëãÏqzém[õ–ª @ÂÞ#a/p%Btg='MÿäξeY•Ü.,,·

View file

@ -1,2 +0,0 @@
x<01>ЮA
Т0@Qз9Eі<45>Ь$<24>I"BW=F<><46>`СиR"шээм~от<D0BE>ЕЕЅ[LtъЛЊu<D08A>1<03><>ЬaШ>АFvфCЩ!ЁйђЎЏnЃHђ<48>fѕтЂдYcBa<42>QA\U)$q&Пћcнэ8йы8нѕ<D0BD>лідKYлЭ"<22>s<EFBFBD><73>и<EFBFBD>ЬQ<D0AC>ЉЎrгОuy*<2A>3н9<D0BD>

View file

@ -1 +0,0 @@
5428838691c97ac192c8b8e1c3f573d8541a94b6

View file

@ -1 +0,0 @@
myfile2 Branch: #master haha true master

View file

@ -1 +0,0 @@
{"KeyEvents":[{"Timestamp":623,"Mod":0,"Key":259,"Ch":0},{"Timestamp":1369,"Mod":0,"Key":256,"Ch":78},{"Timestamp":1904,"Mod":0,"Key":258,"Ch":0},{"Timestamp":2033,"Mod":0,"Key":258,"Ch":0},{"Timestamp":2328,"Mod":0,"Key":13,"Ch":13},{"Timestamp":2848,"Mod":0,"Key":256,"Ch":32},{"Timestamp":3296,"Mod":0,"Key":256,"Ch":97},{"Timestamp":3616,"Mod":0,"Key":127,"Ch":127},{"Timestamp":3824,"Mod":0,"Key":256,"Ch":104},{"Timestamp":3879,"Mod":0,"Key":256,"Ch":97},{"Timestamp":3927,"Mod":0,"Key":256,"Ch":104},{"Timestamp":4000,"Mod":0,"Key":256,"Ch":97},{"Timestamp":4239,"Mod":0,"Key":13,"Ch":13},{"Timestamp":4809,"Mod":0,"Key":258,"Ch":0},{"Timestamp":5024,"Mod":0,"Key":13,"Ch":13},{"Timestamp":5824,"Mod":0,"Key":260,"Ch":0},{"Timestamp":6079,"Mod":0,"Key":256,"Ch":32},{"Timestamp":6376,"Mod":0,"Key":256,"Ch":99},{"Timestamp":6591,"Mod":0,"Key":256,"Ch":116},{"Timestamp":6640,"Mod":0,"Key":256,"Ch":101},{"Timestamp":6816,"Mod":0,"Key":256,"Ch":115},{"Timestamp":6856,"Mod":0,"Key":256,"Ch":116},{"Timestamp":7136,"Mod":0,"Key":13,"Ch":13},{"Timestamp":7487,"Mod":0,"Key":256,"Ch":113}],"ResizeEvents":[{"Timestamp":0,"Width":272,"Height":36}]}

View file

@ -1,23 +0,0 @@
#!/bin/sh
set -e
cd $1
git init
git config user.email "CI@example.com"
git config user.name "CI"
echo test1 > myfile1
git add .
git commit -am "myfile1"
echo test2 > myfile2
git add .
git commit -am "myfile2"
echo test3 > myfile3
git add .
git commit -am "myfile3"
echo test4 > myfile4
git add .
git commit -am "myfile4"

View file

@ -1,4 +0,0 @@
{
"description": "Invoke a custom command that creates a file, and then stage and commit that file. In this case we're using a more customised flow",
"speed": 5
}

View file

@ -0,0 +1 @@
0000000000000000000000000000000000000000 fe47c0cf0521f8864cd0531ddf35d2f741c14abf CI <CI@example.com> 1660476851 +1000 commit (initial): blah

View file

@ -0,0 +1 @@
0000000000000000000000000000000000000000 fe47c0cf0521f8864cd0531ddf35d2f741c14abf CI <CI@example.com> 1660476851 +1000 commit (initial): blah

View file

@ -0,0 +1 @@
fe47c0cf0521f8864cd0531ddf35d2f741c14abf

View file

@ -0,0 +1 @@
ref: refs/heads/feature/foo

View file

@ -0,0 +1,4 @@
0000000000000000000000000000000000000000 d50975554a574b9c66e109927fdb4edfb6bbadb3 CI <CI@example.com> 1660476303 +1000 commit (initial): foo
d50975554a574b9c66e109927fdb4edfb6bbadb3 af550d3777f20bf024ad55c9c796e7e85ef32ccb CI <CI@example.com> 1660476303 +1000 commit: bar
af550d3777f20bf024ad55c9c796e7e85ef32ccb 16919871d6b442beac07e1573c557ca433cff356 CI <CI@example.com> 1660476303 +1000 commit: baz
16919871d6b442beac07e1573c557ca433cff356 16919871d6b442beac07e1573c557ca433cff356 CI <CI@example.com> 1660476303 +1000 checkout: moving from master to feature/foo

View file

@ -0,0 +1 @@
0000000000000000000000000000000000000000 16919871d6b442beac07e1573c557ca433cff356 CI <CI@example.com> 1660476303 +1000 branch: Created from HEAD

View file

@ -0,0 +1,3 @@
0000000000000000000000000000000000000000 d50975554a574b9c66e109927fdb4edfb6bbadb3 CI <CI@example.com> 1660476303 +1000 commit (initial): foo
d50975554a574b9c66e109927fdb4edfb6bbadb3 af550d3777f20bf024ad55c9c796e7e85ef32ccb CI <CI@example.com> 1660476303 +1000 commit: bar
af550d3777f20bf024ad55c9c796e7e85ef32ccb 16919871d6b442beac07e1573c557ca433cff356 CI <CI@example.com> 1660476303 +1000 commit: baz

View file

@ -0,0 +1,3 @@
x<01>ֽA
ֲ0@Q׳9ֵל™₪׃1)BW=F&<26> `˜R"פרצn?~±ײ<C2B1><…K<E280A6>U<EFBFBD>$†q-L¡«₪<C2AB><E282AA>:װ¸r
1VQ¥„הע·¿l‡y<EFBFBD>ַ¼<ץָm<D6B8>ט­Xְ3#<23>®<11>YֿI׳?¹«fמ~<7E>+T

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