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

Code before:
			return handle(self.c.Git().WorkingTree.StageFile, self.c.Tr.Actions.ResolveConflictByKeepingFile)
		},
		Key: 'k',
	}
	deleteItem := &types.MenuItem{
		Label: self.c.Tr.MergeConflictDeleteFile,
		OnPress: func() error {
			return handle(self.c.Git().WorkingTree.RemoveConflictedFile, self.c.Tr.Actions.ResolveConflictByDeletingFile)
		},
		Key: 'd',
	}
	items := []*types.MenuItem{}
	switch file.ShortStatus {
	case "DD":
		// For "both deleted" conflicts, deleting the file is the only reasonable thing you can do.
		// Restoring to the state before deletion is not the responsibility of a conflict resolution tool.
		items = append(items, deleteItem)
	case "DU", "UD":
		// For these, we put the delete option first because it's the most common one,
		// even if it's more destructive.
		items = append(items, deleteItem, keepItem)
	case "AU", "UA":
		// For these, we put the keep option first because it's less destructive,
		// and the chances between keep and delete are 50/50.
		items = append(items, keepItem, deleteItem)
	default:
		panic("should only be called if there's a merge conflict")
	}
	return self.c.Menu(types.CreateMenuOptions{
		Title:  self.c.Tr.MergeConflictsTitle,
		Prompt: file.GetMergeStateDescription(self.c.Tr),
		Items:  items,
	})
}

func (self *FilesController) toggleStagedAll() error {
	if err := self.toggleStagedAllWithLock(); err != nil {
		return err
	}

	if err := self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}, Mode: types.ASYNC}); err != nil {
		return err
	}

	self.context().HandleFocus(types.OnFocusOpts{})
	return nil
}

func (self *FilesController) toggleStagedAllWithLock() error {
	self.c.Mutexes().RefreshingFilesMutex.Lock()
	defer self.c.Mutexes().RefreshingFilesMutex.Unlock()

Review comment (suggestion):
I would have put them both in the same order so that you can use muscle memory to make the choice. Given that this second one is 50/50, why not just retain the order of the first one? i.e. always have delete first.

Code after:
		OnPress: func() error {
			return handle(self.c.Git().WorkingTree.StageFile, self.c.Tr.Actions.ResolveConflictByKeepingFile)
		},
		Key: 'k',
	}
	deleteItem := &types.MenuItem{
		Label: self.c.Tr.MergeConflictDeleteFile,
		OnPress: func() error {
			return handle(self.c.Git().WorkingTree.RemoveConflictedFile, self.c.Tr.Actions.ResolveConflictByDeletingFile)
		},
		Key: 'd',
	}
	items := []*types.MenuItem{}
	switch file.ShortStatus {
	case "DD":
		// For "both deleted" conflicts, deleting the file is the only reasonable thing you can do.
		// Restoring to the state before deletion is not the responsibility of a conflict resolution tool.
		items = append(items, deleteItem)
	case "DU", "UD":
		// For these, we put the delete option first because it's the most common one,
		// even if it's more destructive.
		items = append(items, deleteItem, keepItem)
	case "AU", "UA":
		// For these, we put the keep option first because it's less destructive,
		// and the chances between keep and delete are 50/50.
		items = append(items, keepItem, deleteItem)
	default:
		panic("should only be called if there's a merge conflict")
	}
	return self.c.Menu(types.CreateMenuOptions{
		Title:  self.c.Tr.MergeConflictsTitle,
		Prompt: file.GetMergeStateDescription(self.c.Tr),
		Items:  items,
	})
}

func (self *FilesController) toggleStagedAll() error {
	if err := self.toggleStagedAllWithLock(); err != nil {
		return err
	}

	if err := self.c.Refresh(types.RefreshOptions{Scope: []types.RefreshableView{types.FILES}, Mode: types.ASYNC}); err != nil {
		return err
	}

	self.context().HandleFocus(types.OnFocusOpts{})
	return nil
}

func (self *FilesController) toggleStagedAllWithLock() error {
	self.c.Mutexes().RefreshingFilesMutex.Lock()