ronantakizawa/codereview-bench huggingface dataset:
Repository: jesseduffield/lazygit
File: pkg/integration/tests/branch/new_branch_from_remote_tracking_same_name.go (Go)

Code before:
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Remotes().
			Focus().
			Lines(
				Contains("origin").IsSelected(),
			).
			PressEnter()

		t.Views().RemoteBranches().
			IsFocused().
			Lines(
				Contains("main").IsSelected(),
				Contains("other_branch"),
			).
			SelectNextItem().
			Press(keys.Universal.New)

		t.ExpectPopup().Prompt().
			Title(Equals("New branch name (branch is off of 'origin/other_branch')")).
			Confirm()

		t.Views().Branches().
			Focus().
			Lines(
				Contains("other_branch").IsSelected(),
				Contains("main"),
			).
			Press(keys.Branches.SetUpstream)

		t.ExpectPopup().Menu().Title(Contains("Upstream")).Select(Contains("View divergence from upstream")).Confirm()

		t.Views().SubCommits().
			IsFocused().
			Title(Contains("Commits (other_branch <-> origin/other_branch)")).
			Lines(
				DoesNotContainAnyOf("↓", "↑").Contains("--- Remote ---"),
				DoesNotContainAnyOf("↓", "↑").Contains("--- Local ---"),
			)
	},
})


Review comment (suggestion):
Instead of doing all this, you could simply test that "other_branch" has a checkmark in the branches list (and doesn't in the other test). That's a bit less work, but maybe not quite as explicit. Unsure, just a thought.

Code after:
		t.Views().Remotes().
			Focus().
			Lines(
				Contains("origin").IsSelected(),
			).
			PressEnter()

		t.Views().RemoteBranches().
			IsFocused().
			Lines(
				Contains("master").IsSelected(),
				Contains("other_branch"),
			).
			SelectNextItem().
			Press(keys.Universal.New)

		t.ExpectPopup().Prompt().
			Title(Equals("New branch name (branch is off of 'origin/other_branch')")).
			Confirm()

		t.Views().Branches().
			Focus().
			Lines(
				Contains("other_branch").Contains("✓"),
				Contains("master"),
			)
	},
})
Repository: jesseduffield/lazygit
File: pkg/commands/git_commands/file_loader.go (Go)

Code before:
		if err != nil {
			continue
		}

		fileName := splitLine[2]
		fileDiffs[fileName] = FileDiff{
			LinesAdded:   linesAdded,
			LinesDeleted: linesDeleted,
		}
	}

	return fileDiffs, nil
}

// GitStatus returns the file status of the repo
type GitStatusOptions struct {
	NoRenames         bool
	UntrackedFilesArg string
}

type FileStatus struct {
	StatusString string
	Change       string // ??, MM, AM, ...
	Name         string
	PreviousName string
}

func (fileLoader *FileLoader) gitDiffNumStat() (string, error) {
	return fileLoader.cmd.New(
		NewGitCmd("diff").
			Arg("--numstat").
			Arg("-z").
			Arg("HEAD").
			ToArgv(),
	).DontLog().RunWithOutput()
}

func (self *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
	cmdArgs := NewGitCmd("status").
		Arg(opts.UntrackedFilesArg).
		Arg("--porcelain").
		Arg("-z").
		ArgIfElse(
			opts.NoRenames,
			"--no-renames",
			fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold),
		).
		ToArgv()

	statusLines, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
	if err != nil {

Review comment (performance):
Three things:
1) Just confirming that this can't be bundled into the `git status` command?
2) How expensive is this command to run? If it's expensive, we may need to run it asynchronously
3) I've noticed that this doesn't include untracked files. Is there a way to include those?


Code after:
		}

		fileName := splitLine[2]
		fileDiffs[fileName] = FileDiff{
			LinesAdded:   linesAdded,
			LinesDeleted: linesDeleted,
		}
	}

	return fileDiffs, nil
}

// GitStatus returns the file status of the repo
type GitStatusOptions struct {
	NoRenames         bool
	UntrackedFilesArg string
}

type FileStatus struct {
	StatusString string
	Change       string // ??, MM, AM, ...
	Name         string
	PreviousName string
}

func (fileLoader *FileLoader) gitDiffNumStat() (string, error) {
	return fileLoader.cmd.New(
		NewGitCmd("diff").
			Arg("--numstat").
			Arg("-z").
			Arg("HEAD").
			ToArgv(),
	).DontLog().RunWithOutput()
}

func (self *FileLoader) gitStatus(opts GitStatusOptions) ([]FileStatus, error) {
	cmdArgs := NewGitCmd("status").
		Arg(opts.UntrackedFilesArg).
		Arg("--porcelain").
		Arg("-z").
		ArgIfElse(
			opts.NoRenames,
			"--no-renames",
			fmt.Sprintf("--find-renames=%d%%", self.AppState.RenameSimilarityThreshold),
		).
		ToArgv()

	statusLines, _, err := self.cmd.New(cmdArgs).DontLog().RunWithOutputs()
	if err != nil {
		return []FileStatus{}, err
	}
Repository: jesseduffield/lazygit
File: pkg/gui/presentation/files.go (Go)

Code before:
	return output
}

func formatFileStatus(file *models.File, restColor style.TextStyle) string {
	firstChar := file.ShortStatus[0:1]
	firstCharCl := style.FgGreen
	if firstChar == "?" {
		firstCharCl = theme.UnstagedChangesColor
	} else if firstChar == " " {
		firstCharCl = restColor
	}

	secondChar := file.ShortStatus[1:2]
	secondCharCl := theme.UnstagedChangesColor
	if secondChar == " " {
		secondCharCl = restColor
	}

	return firstCharCl.Sprint(firstChar) + secondCharCl.Sprint(secondChar)
}

func formatLineChanges(linesAdded, linesDeleted int) string {
	output := ""

	if linesAdded != 0 {
		output += style.FgGreen.Sprint("+" + strconv.Itoa(linesAdded))
	}

	if linesDeleted != 0 {
		if output != "" {
			output += " "
		}
		output += style.FgRed.Sprint("-" + strconv.Itoa(linesDeleted))
	}

	return output
}

func getCommitFileLine(
	isCollapsed bool,
	treeDepth int,
	visualDepth int,
	node *filetree.Node[models.CommitFile],
	status patch.PatchStatus,
	showFileIcons bool,
) string {
	indentation := strings.Repeat("  ", visualDepth)
	name := commitFileNameAtDepth(node, treeDepth)
	commitFile := node.File
	output := indentation


Review comment (suggestion):
```suggestion
		output += style.FgGreen.Sprintf("+%d", linesAdded)
```

Code after:
}

func formatFileStatus(file *models.File, restColor style.TextStyle) string {
	firstChar := file.ShortStatus[0:1]
	firstCharCl := style.FgGreen
	if firstChar == "?" {
		firstCharCl = theme.UnstagedChangesColor
	} else if firstChar == " " {
		firstCharCl = restColor
	}

	secondChar := file.ShortStatus[1:2]
	secondCharCl := theme.UnstagedChangesColor
	if secondChar == " " {
		secondCharCl = restColor
	}

	return firstCharCl.Sprint(firstChar) + secondCharCl.Sprint(secondChar)
}

func formatLineChanges(linesAdded, linesDeleted int) string {
	output := ""

	if linesAdded != 0 {
		output += style.FgGreen.Sprintf("+%d", linesAdded)
	}

	if linesDeleted != 0 {
		if output != "" {
			output += " "
		}
		output += style.FgRed.Sprintf("-%d", linesDeleted)
	}

	return output
}

func getCommitFileLine(
	isCollapsed bool,
	treeDepth int,
	visualDepth int,
	node *filetree.Node[models.CommitFile],
	status patch.PatchStatus,
	showFileIcons bool,
) string {
	indentation := strings.Repeat("  ", visualDepth)
	name := commitFileNameAtDepth(node, treeDepth)
	commitFile := node.File
	output := indentation

	isDirectory := commitFile == nil
Repository: jesseduffield/lazygit
File: pkg/gui/presentation/branches.go (Go)

Code before:
}

// GetBranchTextStyle branch color
func GetBranchTextStyle(name string) style.TextStyle {
	if style, ok := branchPrefixColorCache.match(name); ok {
		return *style
	}

	// Default colors for common branch types
	branchType := strings.Split(name, "/")[0]
	switch branchType {
	case "feature":
		return style.FgGreen
	case "bugfix":
		return style.FgYellow
	case "hotfix":
		return style.FgRed
	default:
		return theme.DefaultTextColor
	}
}

func (m *colorMatcher) match(name string) (*style.TextStyle, bool) {
	if m.isRegex {
		for pattern, style := range m.patterns {
			if matched, _ := regexp.MatchString("^"+pattern+"$", name); matched {
				return &style, true
			}
		}
	} else {
		// old behavior using the deprecated branchColors behavior matching on branch type
		branchType := strings.Split(name, "/")[0]
		if value, ok := m.patterns[branchType]; ok {
			return &value, true
		}
	}

	return nil, false
}

func BranchStatus(
	branch *models.Branch,
	itemOperation types.ItemOperation,
	tr *i18n.TranslationSet,
	now time.Time,
	userConfig *config.UserConfig,
) string {
	itemOperationStr := ItemOperationToString(itemOperation, tr)
	if itemOperationStr != "" {
		return style.FgCyan.Sprintf("%s %s", itemOperationStr, utils.Loader(now, userConfig.Gui.Spinner))
	}

Review comment (refactor):
I find the implicit anchoring surprising, and would remove it. If users want it, they can easily add it themselves. For the record, we don't use it for [commit prefix patterns](https://github.com/jesseduffield/lazygit/blob/5e9ba82d7bcdfdc873015f24a0817378e2fa4788/pkg/gui/controllers/helpers/working_tree_helper.go#L160) either.

Code after:
}

// GetBranchTextStyle branch color
func GetBranchTextStyle(name string) style.TextStyle {
	if style, ok := colorPatterns.match(name); ok {
		return *style
	}

	return theme.DefaultTextColor
}

func (m *colorMatcher) match(name string) (*style.TextStyle, bool) {
	if m.isRegex {
		for pattern, style := range m.patterns {
			if matched, _ := regexp.MatchString(pattern, name); matched {
				return &style, true
			}
		}
	} else {
		// old behavior using the deprecated branchColors behavior matching on branch type
		branchType := strings.Split(name, "/")[0]
		if value, ok := m.patterns[branchType]; ok {
			return &value, true
		}
	}

	return nil, false
}

func BranchStatus(
	branch *models.Branch,
	itemOperation types.ItemOperation,
	tr *i18n.TranslationSet,
	now time.Time,
	userConfig *config.UserConfig,
) string {
	itemOperationStr := ItemOperationToString(itemOperation, tr)
	if itemOperationStr != "" {
		return style.FgCyan.Sprintf("%s %s", itemOperationStr, utils.Loader(now, userConfig.Gui.Spinner))
	}

	result := ""
	if branch.IsTrackingRemote() {
		if branch.UpstreamGone {
			result = style.FgRed.Sprint(tr.UpstreamGone)
		} else if branch.MatchesUpstream() {
			result = style.FgGreen.Sprint("✓")
		} else if branch.RemoteBranchNotStoredLocally() {
			result = style.FgMagenta.Sprint("?")
		} else if branch.IsBehindForPull() && branch.IsAheadForPull() {
			result = style.FgYellow.Sprintf("↓%s↑%s", branch.BehindForPull, branch.AheadForPull)
Repository: jesseduffield/lazygit
File: pkg/integration/tests/tag/crud_annotated.go (Go)

Code before:
			Lines(
				MatchesRegexp(`new-tag.*message`).IsSelected(),
			).
			Tap(func() {
				t.Git().
					RemoteTagDeleted("origin", "new-tag")
			}).
			Press(keys.Universal.Remove).
			Tap(func() {
				t.ExpectPopup().
					Menu().
					Title(Equals("Delete tag 'new-tag'?")).
					Select(Contains("Delete local tag")).
					Confirm()
			}).
			IsEmpty().
			Press(keys.Universal.New).
			Tap(func() {
				// confirm content is cleared on next tag create
				t.ExpectPopup().CommitMessagePanel().
					Title(Equals("Tag name")).
					InitialText(Equals("")).
					Close()
			}).

			// Delete both local and remote tag new-tag-2

			Press(keys.Universal.New).
			Tap(func() {
				t.ExpectPopup().CommitMessagePanel().
					Title(Equals("Tag name")).
					Type("new-tag-2").
					SwitchToDescription().
					Title(Equals("Tag description")).
					Type("message 2").
					SwitchToSummary().
					Confirm()
			}).
			Lines(
				MatchesRegexp(`new-tag-2.*message 2`).IsSelected(),
			).
			Press(keys.Universal.Push).
			Tap(func() {
				t.ExpectPopup().Prompt().
					Title(Equals("Remote to push tag 'new-tag-2' to:")).
					InitialText(Equals("origin")).
					SuggestionLines(
						Contains("origin"),
					).
					Confirm()
			}).

Review comment (suggestion):
Two things:
1) I would create this as a separate test because it's fairly independent from what's already in this test.
2) We need an assertion that the tag has been removed from the remote. Funnily enough I think that we don't actually show remote tags anywhere, so we'll need to instead settle for a shell command (you can add a function in pkg/integration/components/shell.go called AssertRemoteTagNotFound)

Code after:
			Lines(
				MatchesRegexp(`new-tag.*message`).IsSelected(),
			).
			Tap(func() {
				t.Git().
					RemoteTagDeleted("origin", "new-tag")
			}).
			Press(keys.Universal.Remove).
			Tap(func() {
				t.ExpectPopup().
					Menu().
					Title(Equals("Delete tag 'new-tag'?")).
					Select(Contains("Delete local tag")).
					Confirm()
			}).
			IsEmpty().
			Press(keys.Universal.New).
			Tap(func() {
				// confirm content is cleared on next tag create
				t.ExpectPopup().CommitMessagePanel().
					Title(Equals("Tag name")).
					InitialText(Equals(""))
			})
	},
})
Repository: jesseduffield/lazygit
File: pkg/gui/controllers/basic_commits_controller.go (Go)

Code before:
			Key: 'm',
		},
		{
			Label: self.c.Tr.CommitBody,
			OnPress: func() error {
				return self.copyCommitBodyToClipboard(commit)
			},
			Key: 'b',
		},
		{
			Label: self.c.Tr.CommitURL,
			OnPress: func() error {
				return self.copyCommitURLToClipboard(commit)
			},
			Key: 'u',
		},
		{
			Label: self.c.Tr.CommitDiff,
			OnPress: func() error {
				return self.copyCommitDiffToClipboard(commit)
			},
			Key: 'd',
		},
		{
			Label: self.c.Tr.CommitAuthor,
			OnPress: func() error {
				return self.copyAuthorToClipboard(commit)
			},
			Key: 'a',
		},
	}

	commitTagsItem := types.MenuItem{
		Label: self.c.Tr.CommitTags,
		OnPress: func() error {
			return self.copyCommitTagsToClipboard(commit)
		},
		Key: 't',
	}

	if len(commit.Tags) == 0 {
		commitTagsItem.DisabledReason = &types.DisabledReason{Text: self.c.Tr.CommitHasNoTags}
	}

	items = append(items, &commitTagsItem)

	return self.c.Menu(types.CreateMenuOptions{
		Title: self.c.Tr.Actions.CopyCommitAttributeToClipboard,
		Items: items,
	})
}

Review comment (suggestion):
None of the other keys in this menu allow customization, so I felt it was fine to leave this here. I can't think of a different commit attribute that might want to claim the `b` shortcut one day.

Code after:
			Label: self.c.Tr.CommitHash,
			OnPress: func() error {
				return self.copyCommitHashToClipboard(commit)
			},
		},
		{
			Label: self.c.Tr.CommitSubject,
			OnPress: func() error {
				return self.copyCommitSubjectToClipboard(commit)
			},
			Key: 's',
		},
		{
			Label: self.c.Tr.CommitMessage,
			OnPress: func() error {
				return self.copyCommitMessageToClipboard(commit)
			},
			Key: 'm',
		},
		{
			Label:          self.c.Tr.CommitMessageBody,
			DisabledReason: commitMessageBodyDisabled,
			OnPress: func() error {
				return self.copyCommitMessageBodyToClipboard(commitMessageBody)
			},
			Key: 'b',
		},
		{
			Label: self.c.Tr.CommitURL,
			OnPress: func() error {
				return self.copyCommitURLToClipboard(commit)
			},
			Key: 'u',
		},
		{
			Label: self.c.Tr.CommitDiff,
			OnPress: func() error {
				return self.copyCommitDiffToClipboard(commit)
			},
			Key: 'd',
		},
		{
			Label: self.c.Tr.CommitAuthor,
			OnPress: func() error {
				return self.copyAuthorToClipboard(commit)
			},
			Key: 'a',
		},
	}

	commitTagsItem := types.MenuItem{
Repository: jesseduffield/lazygit
File: pkg/config/user_config_validation.go (Go)

Code before:
			if len(path) == 0 {
				newPath = field.Name
			} else {
				newPath = fmt.Sprintf("%s.%s", path, field.Name)
			}
			if err := validateKeybindingsRecurse(newPath,
				value.FieldByName(field.Name).Interface()); err != nil {
				return err
			}
		}
	} else if value.Kind() == reflect.Slice {
		for i := 0; i < value.Len(); i++ {
			if err := validateKeybindingsRecurse(
				fmt.Sprintf("%s[%d]", path, i), value.Index(i).Interface()); err != nil {
				return err
			}
		}
	} else if value.Kind() == reflect.String {
		key := node.(string)
		if !isValidKeybindingKey(key) {
			return fmt.Errorf("Unrecognized key '%s' for keybinding '%s'. For permitted values see %s",
				strings.ToLower(key), path, constants.Links.Docs.CustomKeybindings)
		}
	} else {
		panic("Unexpected type")
	}
	return nil
}

func validateKeybindings(keybindingConfig KeybindingConfig) error {
	return validateKeybindingsRecurse("", keybindingConfig)
}


Review comment (suggestion):
Unfortunately we can't i18n-ize these, because the config is read very early, before the translation system is initialized. And it is important that we validate the config right after loading, so that all clients of it can just use it without having to worry about valid data.

Code after:
	value := reflect.ValueOf(node)
	if value.Kind() == reflect.Struct {
		for _, field := range reflect.VisibleFields(reflect.TypeOf(node)) {
			var newPath string
			if len(path) == 0 {
				newPath = field.Name
			} else {
				newPath = fmt.Sprintf("%s.%s", path, field.Name)
			}
			if err := validateKeybindingsRecurse(newPath,
				value.FieldByName(field.Name).Interface()); err != nil {
				return err
			}
		}
	} else if value.Kind() == reflect.Slice {
		for i := 0; i < value.Len(); i++ {
			if err := validateKeybindingsRecurse(
				fmt.Sprintf("%s[%d]", path, i), value.Index(i).Interface()); err != nil {
				return err
			}
		}
	} else if value.Kind() == reflect.String {
		key := node.(string)
		if !isValidKeybindingKey(key) {
			return fmt.Errorf("Unrecognized key '%s' for keybinding '%s'. For permitted values see %s",
				key, path, constants.Links.Docs.CustomKeybindings)
		}
	} else {
		log.Fatalf("Unexpected type for property '%s': %s", path, value.Kind())
	}
	return nil
}

func validateKeybindings(keybindingConfig KeybindingConfig) error {
	if err := validateKeybindingsRecurse("", keybindingConfig); err != nil {
		return err
	}

	if len(keybindingConfig.Universal.JumpToBlock) != 5 {
		return fmt.Errorf("keybinding.universal.jumpToBlock must have 5 elements; found %d.",
			len(keybindingConfig.Universal.JumpToBlock))
	}

	return nil
}

func validateCustomCommandKey(key string) error {
	if !isValidKeybindingKey(key) {
		return fmt.Errorf("Unrecognized key '%s' for custom command. For permitted values see %s",
			key, constants.Links.Docs.CustomKeybindings)
	}
Repository: jesseduffield/lazygit
File: pkg/config/user_config_validation.go (Go)

Code before:
			return fmt.Errorf("Unrecognized key '%s' for keybinding '%s'. For permitted values see %s",
				strings.ToLower(key), path, constants.Links.Docs.CustomKeybindings)
		}
	} else {
		panic("Unexpected type")
	}
	return nil
}

func validateKeybindings(keybindingConfig KeybindingConfig) error {
	if err := validateKeybindingsRecurse("", keybindingConfig); err != nil {
		return err
	}

	if len(keybindingConfig.Universal.JumpToBlock) != 5 {
		return fmt.Errorf("keybinding.universal.jumpToBlock must have 5 elements; found %d.",
			len(keybindingConfig.Universal.JumpToBlock))
	}

	return nil
}

func validateCustomCommandKey(key string) error {
	if !isValidKeybindingKey(key) {
		return fmt.Errorf("Unrecognized key '%s' for custom command. For permitted values see %s",
			strings.ToLower(key), constants.Links.Docs.CustomKeybindings)
	}
	return nil
}

func validateCustomCommands(customCommands []CustomCommand) error {
	for _, customCommand := range customCommands {
		if err := validateCustomCommandKey(customCommand.Key); err != nil {
			return err
		}
	}
	return nil
}


Review comment (security):
I don't believe that we have an equivalent to the `path` variable that we can use to point them to where in their config they messed up, but maybe using the `CustomCommand.Command`, or the index in the slice could be helpful to somebody.

Particularly because we do `string.ToLower` what they send to use, so if someone creates a custom command with key `<CC-4>`, and then we give them an error message about `<cc-4>`, and they do a case sensitive search for where that is in their config. Might be confusing.

That example is starting to seem a bit contrived though....

Code after:
		if !isValidKeybindingKey(key) {
			return fmt.Errorf("Unrecognized key '%s' for keybinding '%s'. For permitted values see %s",
				key, path, constants.Links.Docs.CustomKeybindings)
		}
	} else {
		log.Fatalf("Unexpected type for property '%s': %s", path, value.Kind())
	}
	return nil
}

func validateKeybindings(keybindingConfig KeybindingConfig) error {
	if err := validateKeybindingsRecurse("", keybindingConfig); err != nil {
		return err
	}

	if len(keybindingConfig.Universal.JumpToBlock) != 5 {
		return fmt.Errorf("keybinding.universal.jumpToBlock must have 5 elements; found %d.",
			len(keybindingConfig.Universal.JumpToBlock))
	}

	return nil
}

func validateCustomCommandKey(key string) error {
	if !isValidKeybindingKey(key) {
		return fmt.Errorf("Unrecognized key '%s' for custom command. For permitted values see %s",
			key, constants.Links.Docs.CustomKeybindings)
	}
	return nil
}

func validateCustomCommands(customCommands []CustomCommand) error {
	for _, customCommand := range customCommands {
		if err := validateCustomCommandKey(customCommand.Key); err != nil {
			return err
		}
	}
	return nil
}
Repository: jesseduffield/lazygit
File: pkg/integration/tests/commit/commit_skip_hooks.go (Go)

Code before:
var CommitSkipHooks = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Commit with skip hook using CommitChangesWithoutHook",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.NewBranch("feature/TEST-002")
		shell.CreateFile("test-wip-commit-prefix", "This is foo bar")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		t.Views().Commits().
			IsEmpty()

		t.Views().Files().
			IsFocused().
			PressPrimaryAction().
			Press(keys.Files.CommitChangesWithoutHook)

		t.ExpectPopup().CommitMessagePanel().
			Title(Equals("Commit summary")).
			Type("foo bar").
			Confirm()

		t.Views().Commits().Focus()
		t.Views().Main().Content(Contains("foo bar"))
		t.Views().Extras().Content(Contains("--no-verify"))
	},
})


Review comment (bug):
This is an interesting way of checking whether we passed the no-verify flag, but I'm not sure I like it much. If you had a test that committed twice, once with and once without the flag, you couldn't use this check for the second commit.

I'd prefer it if we installed an actual hook (using the technique in `fail_hooks_then_commit_no_hooks.go`), and have it write something to a file; we can then check whether the file exists (or what it contains) using `t.FileSystem().PathPresent()` or `t.FileSystem().FileContent()`.

Code after:
var blockingHook = `#!/bin/bash

# For this test all we need is a hook that always fails
exit 1
`

var CommitSkipHooks = NewIntegrationTest(NewIntegrationTestArgs{
	Description:  "Commit with skip hook using CommitChangesWithoutHook",
	ExtraCmdArgs: []string{},
	Skip:         false,
	SetupConfig:  func(config *config.AppConfig) {},
	SetupRepo: func(shell *Shell) {
		shell.CreateFile(".git/hooks/pre-commit", blockingHook)
		shell.MakeExecutable(".git/hooks/pre-commit")

		shell.CreateFile("file.txt", "content")
	},
	Run: func(t *TestDriver, keys config.KeybindingConfig) {
		checkBlockingHook(t, keys)

		t.Views().Files().
			IsFocused().
			PressPrimaryAction().
			Lines(
				Equals("A  file.txt"),
			).
			Press(keys.Files.CommitChangesWithoutHook)

		t.ExpectPopup().CommitMessagePanel().
			Title(Equals("Commit summary")).
			Type("foo bar").
			Confirm()

		t.Views().Commits().Focus()
		t.Views().Main().Content(Contains("foo bar"))
	},
})