Repository: jesseduffield/lazygit
File: pkg/gui/controllers/commits_files_controller.go (Go)

Code before:
func (self *CommitFilesController) checkout(node *filetree.CommitFileNode) error {
	self.c.LogAction(self.c.Tr.Actions.CheckoutFile)
	if err := self.c.Git().WorkingTree.CheckoutFile(self.context().GetRef().RefName(), node.GetPath()); err != nil {
		return self.c.Error(err)
	}

	return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
}

func (self *CommitFilesController) discard(selectedNodes []*filetree.CommitFileNode) error {
	parentContext, ok := self.c.CurrentContext().GetParentContext()
	if !ok || parentContext.GetKey() != context.LOCAL_COMMITS_CONTEXT_KEY {
		return self.c.ErrorMsg(self.c.Tr.CanOnlyDiscardFromLocalCommits)
	}

	removeFileRange := func() error {
		return self.c.WithWaitingStatus(self.c.Tr.UpdatingPatch, func(gocui.Task) error {
			if !self.c.Git().Patch.PatchBuilder.Active() {
				if err := self.startPatchBuilder(); err != nil {
					return err
				}
			}

			selectedNodes = normalisedSelectedCommitFileNodes(selectedNodes)

			// The previous version of this would give a couple of different prompts depending
			// on what's happening. I'm condensing those into a single prompt, but I need to keep track
			// of how many changes are happening and of what type.
			numFilesChanged := 0
			numFilesAdded := 0
			numFilesDeleted := 0

			for _, node := range selectedNodes {
				err := node.ForEachFile(func(file *models.CommitFile) error {
					numFilesChanged++
					if file.Added() {
						numFilesAdded++
					}
					if file.Deleted() {
						numFilesDeleted++
					}

					return self.c.Git().Patch.PatchBuilder.AddFileWhole(file.Name)
				})
				if err != nil {
					return self.c.Error(err)
				}
			}

			return self.c.Confirm(types.ConfirmOpts{
				Title: self.c.Tr.DiscardFileChangesTitle,

Review comment (suggestion):
I don't think we need to be this granular: I would just say:
'
Are you sure you want to discard this commit's changes to the selected file(s)?

This will start an interactive rebase at the commit, revert the changes to the selected file(s), then continue the rebase. If later commits depend on these changes, you may need to resolve conflicts.
'

(I'm adding some extra context there so that the user knows what's going to happen behind the scenes)

Code after:
	if err := self.c.Git().WorkingTree.CheckoutFile(self.context().GetRef().RefName(), node.GetPath()); err != nil {
		return self.c.Error(err)
	}

	return self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC})
}

func (self *CommitFilesController) discard(selectedNodes []*filetree.CommitFileNode) error {
	parentContext, ok := self.c.CurrentContext().GetParentContext()
	if !ok || parentContext.GetKey() != context.LOCAL_COMMITS_CONTEXT_KEY {
		return self.c.ErrorMsg(self.c.Tr.CanOnlyDiscardFromLocalCommits)
	}

	if ok, err := self.c.Helpers().PatchBuilding.ValidateNormalWorkingTreeState(); !ok {
		return err
	}

	return self.c.Confirm(types.ConfirmOpts{
		Title:  self.c.Tr.DiscardFileChangesTitle,
		Prompt: self.c.Tr.DiscardFileChangesPrompt,
		HandleConfirm: func() error {
			return self.c.WithWaitingStatus(self.c.Tr.RebasingStatus, func(gocui.Task) error {
				var filePaths []string
				selectedNodes = normalisedSelectedCommitFileNodes(selectedNodes)

				// Reset the current patch if there is one.
				if self.c.Git().Patch.PatchBuilder.Active() {
					self.c.Git().Patch.PatchBuilder.Reset()
					if err := self.c.Refresh(types.RefreshOptions{Mode: types.BLOCK_UI}); err != nil {
						return err
					}
				}

				for _, node := range selectedNodes {
					err := node.ForEachFile(func(file *models.CommitFile) error {
						filePaths = append(filePaths, file.GetPath())
						return nil
					})
					if err != nil {
						return self.c.Error(err)
					}
				}

				err := self.c.Git().Rebase.DiscardOldFileChanges(self.c.Model().Commits, self.c.Contexts().LocalCommits.GetSelectedLineIdx(), filePaths)
				if err := self.c.Helpers().MergeAndRebase.CheckMergeOrRebase(err); err != nil {
					return err
				}

				if self.context().RangeSelectEnabled() {
					self.context().GetList().CancelRangeSelect()
				}