mirror of
				https://github.com/jesseduffield/lazygit.git
				synced 2025-10-30 23:57:43 +02:00 
			
		
		
		
	Add merge options menu
Replace merge-tool with merge options menu that allows resolving all conflicts for selected files as ours, theirs, or union, while still providing access to the merge tool.
This commit is contained in:
		
				
					committed by
					
						 Stefan Haller
						Stefan Haller
					
				
			
			
				
	
			
			
			
						parent
						
							1f002af06b
						
					
				
				
					commit
					703f053a7e
				
			| @@ -619,7 +619,7 @@ keybinding: | ||||
|     viewResetOptions: D | ||||
|     fetch: f | ||||
|     toggleTreeView: '`' | ||||
|     openMergeTool: M | ||||
|     openMergeOptions: M | ||||
|     openStatusFilter: <c-b> | ||||
|     copyFileInfoToClipboard: "y" | ||||
|     collapseAll: '-' | ||||
|   | ||||
| @@ -153,7 +153,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_ | ||||
| | `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). | | ||||
| | `` ` `` | Toggle file tree view | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. | | ||||
| | `` <c-t> `` | Open external diff tool (git difftool) |  | | ||||
| | `` M `` | Open external merge tool | Run `git mergetool`. | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` f `` | Fetch | Fetch changes from remote. | | ||||
| | `` - `` | Collapse all files | Collapse all directories in the files tree | | ||||
| | `` = `` | Expand all files | Expand all directories in the file tree | | ||||
| @@ -210,7 +210,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_ | ||||
| | `` z `` | Undo | Undo last merge conflict resolution. | | ||||
| | `` e `` | Edit file | Open file in external editor. | | ||||
| | `` o `` | Open file | Open file in default application. | | ||||
| | `` M `` | Open external merge tool | Run `git mergetool`. | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` <esc> `` | Return to files panel |  | | ||||
|  | ||||
| ## Main panel (normal) | ||||
|   | ||||
| @@ -235,7 +235,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味 | ||||
| | `` D `` | リセット | 作業ツリーのリセットオプション(例:作業ツリーの完全破棄)を表示します。 | | ||||
| | `` ` `` | ファイルツリービューを切り替え | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. | | ||||
| | `` <c-t> `` | 外部差分ツールを開く(git difftool) |  | | ||||
| | `` M `` | 外部マージツールを開く | `git mergetool`を実行します。 | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` f `` | フェッチ | リモートから変更をフェッチします。 | | ||||
| | `` - `` | すべてのファイルを折りたたむ | ファイルツリー内のすべてのディレクトリを折りたたみます | | ||||
| | `` = `` | すべてのファイルを展開 | ファイルツリー内のすべてのディレクトリを展開します | | ||||
| @@ -292,7 +292,7 @@ _凡例:`<c-b>` はctrl+b、`<a-b>` はalt+b、`B` はshift+bを意味 | ||||
| | `` z `` | 元に戻す | 最後のマージコンフリクト解決を元に戻します。 | | ||||
| | `` e `` | ファイルを編集 | 外部エディタでファイルを開きます。 | | ||||
| | `` o `` | ファイルを開く | デフォルトのアプリケーションでファイルを開きます。 | | ||||
| | `` M `` | 外部マージツールを開く | `git mergetool`を実行します。 | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` <esc> `` | ファイルパネルに戻る |  | | ||||
|  | ||||
| ## メインパネル(通常) | ||||
|   | ||||
| @@ -152,7 +152,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_ | ||||
| | `` z `` | 되돌리기 | Undo last merge conflict resolution. | | ||||
| | `` e `` | 파일 편집 | Open file in external editor. | | ||||
| | `` o `` | 파일 닫기 | Open file in default application. | | ||||
| | `` M `` | Git mergetool를 열기 | Run `git mergetool`. | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` <esc> `` | 파일 목록으로 돌아가기 |  | | ||||
|  | ||||
| ## 메인 패널 (Normal) | ||||
| @@ -396,7 +396,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_ | ||||
| | `` D `` | 초기화 | View reset options for working tree (e.g. nuking the working tree). | | ||||
| | `` ` `` | 파일 트리뷰로 전환 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. | | ||||
| | `` <c-t> `` | Open external diff tool (git difftool) |  | | ||||
| | `` M `` | Git mergetool를 열기 | Run `git mergetool`. | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` f `` | Fetch | Fetch changes from remote. | | ||||
| | `` - `` | Collapse all files | Collapse all directories in the files tree | | ||||
| | `` = `` | Expand all files | Expand all directories in the file tree | | ||||
|   | ||||
| @@ -78,7 +78,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_ | ||||
| | `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). | | ||||
| | `` ` `` | Toggle bestandsboom weergave | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. | | ||||
| | `` <c-t> `` | Open external diff tool (git difftool) |  | | ||||
| | `` M `` | Open external merge tool | Run `git mergetool`. | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` f `` | Fetch | Fetch changes from remote. | | ||||
| | `` - `` | Collapse all files | Collapse all directories in the files tree | | ||||
| | `` = `` | Expand all files | Expand all directories in the file tree | | ||||
| @@ -218,7 +218,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_ | ||||
| | `` z `` | Ongedaan maken | Undo last merge conflict resolution. | | ||||
| | `` e `` | Verander bestand | Open file in external editor. | | ||||
| | `` o `` | Open bestand | Open file in default application. | | ||||
| | `` M `` | Open external merge tool | Run `git mergetool`. | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` <esc> `` | Ga terug naar het bestanden paneel |  | | ||||
|  | ||||
| ## Normaal | ||||
|   | ||||
| @@ -193,7 +193,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_ | ||||
| | `` z `` | Cofnij | Cofnij ostatnie rozwiązanie konfliktu scalania. | | ||||
| | `` e `` | Edytuj plik | Otwórz plik w zewnętrznym edytorze. | | ||||
| | `` o `` | Otwórz plik | Otwórz plik w domyślnej aplikacji. | | ||||
| | `` M `` | Otwórz zewnętrzne narzędzie scalania | Uruchom `git mergetool`. | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` <esc> `` | Wróć do panelu plików |  | | ||||
|  | ||||
| ## Panel główny (zatwierdzanie) | ||||
| @@ -252,7 +252,7 @@ _Legenda: `<c-b>` oznacza ctrl+b, `<a-b>` oznacza alt+b, `B` oznacza shift+b_ | ||||
| | `` D `` | Reset | Wyświetl opcje resetu dla drzewa roboczego (np. zniszczenie drzewa roboczego). | | ||||
| | `` ` `` | Przełącz widok drzewa plików | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. | | ||||
| | `` <c-t> `` | Otwórz zewnętrzne narzędzie różnic (git difftool) |  | | ||||
| | `` M `` | Otwórz zewnętrzne narzędzie scalania | Uruchom `git mergetool`. | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` f `` | Pobierz | Pobierz zmiany ze zdalnego serwera. | | ||||
| | `` - `` | Collapse all files | Collapse all directories in the files tree | | ||||
| | `` = `` | Expand all files | Expand all directories in the file tree | | ||||
|   | ||||
| @@ -78,7 +78,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_ | ||||
| | `` D `` | Restaurar | Opções de redefinição de exibição para árvore de trabalho (por exemplo, nukando a árvore de trabalho). | | ||||
| | `` ` `` | Alternar exibição de árvore de arquivo | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. | | ||||
| | `` <c-t> `` | Abrir ferramenta de diff externa (git difftool) |  | | ||||
| | `` M `` | Abrir ferramenta de merge externa | Execute `git mergetool`. | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` f `` | Buscar | Buscar alterações do controle remoto. | | ||||
| | `` - `` | Recolher todos os arquivos | Recolher todos os diretórios na árvore de arquivos | | ||||
| | `` = `` | Expandir todos os arquivos | Expandir todos os diretórios na árvore do arquivo | | ||||
| @@ -278,7 +278,7 @@ _Legend: `<c-b>` means ctrl+b, `<a-b>` means alt+b, `B` means shift+b_ | ||||
| | `` z `` | Desfazer | Desfazer resolução de conflitos de última mesclagem. | | ||||
| | `` e `` | Editar arquivo | Abrir arquivo no editor externo. | | ||||
| | `` o `` | Abrir arquivo | Abrir arquivo no aplicativo padrão. | | ||||
| | `` M `` | Abrir ferramenta de merge externa | Execute `git mergetool`. | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` <esc> `` | Retornar ao painel de arquivos |  | | ||||
|  | ||||
| ## Painel principal (patch build) | ||||
|   | ||||
| @@ -122,7 +122,7 @@ _Связки клавиш_ | ||||
| | `` z `` | Отменить | Undo last merge conflict resolution. | | ||||
| | `` e `` | Редактировать файл | Open file in external editor. | | ||||
| | `` o `` | Открыть файл | Open file in default application. | | ||||
| | `` M `` | Открыть внешний инструмент слияния (git mergetool) | Run `git mergetool`. | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` <esc> `` | Вернуться к панели файлов |  | | ||||
|  | ||||
| ## Главная панель (сборка патчей) | ||||
| @@ -390,7 +390,7 @@ _Связки клавиш_ | ||||
| | `` D `` | Reset | View reset options for working tree (e.g. nuking the working tree). | | ||||
| | `` ` `` | Переключить вид дерева файлов | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. | | ||||
| | `` <c-t> `` | Open external diff tool (git difftool) |  | | ||||
| | `` M `` | Открыть внешний инструмент слияния (git mergetool) | Run `git mergetool`. | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` f `` | Получить изменения | Fetch changes from remote. | | ||||
| | `` - `` | Collapse all files | Collapse all directories in the files tree | | ||||
| | `` = `` | Expand all files | Expand all directories in the file tree | | ||||
|   | ||||
| @@ -216,7 +216,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_ | ||||
| | `` D `` | 重置 | 查看工作树的重置选项(例如:清除工作树)。 | | ||||
| | `` ` `` | 切换文件树视图 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. | | ||||
| | `` <c-t> `` | 使用外部差异比较工具(git difftool) |  | | ||||
| | `` M `` | 打开外部合并工具(git mergetool) | 执行 `git mergetool`. | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` f `` | 抓取 | 从远程获取变更 | | ||||
| | `` - `` | 折叠全部文件 | 折叠文件树中的全部目录 | | ||||
| | `` = `` | 展开全部文件 | 展开文件树中的全部目录 | | ||||
| @@ -305,7 +305,7 @@ _图例:`<c-b>` 意味着ctrl+b, `<a-b>意味着Alt+b, `B` 意味着shift+b_ | ||||
| | `` z `` | 撤销 | 撤消上次合并冲突解决 | | ||||
| | `` e `` | 编辑文件 | 使用外部编辑器打开文件 | | ||||
| | `` o `` | 打开文件 | 使用默认程序打开该文件 | | ||||
| | `` M `` | 打开外部合并工具(git mergetool) | 执行 `git mergetool`. | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` <esc> `` | 返回文件面板 |  | | ||||
|  | ||||
| ## 正在暂存 | ||||
|   | ||||
| @@ -97,7 +97,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B | ||||
| | `` z `` | 復原 | Undo last merge conflict resolution. | | ||||
| | `` e `` | 編輯檔案 | 使用外部編輯器開啟 | | ||||
| | `` o `` | 開啟檔案 | 使用預設軟體開啟 | | ||||
| | `` M `` | 開啟外部合併工具 | 執行 `git mergetool`。 | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` <esc> `` | 返回檔案面板 |  | | ||||
|  | ||||
| ## 主面板(預存) | ||||
| @@ -347,7 +347,7 @@ _說明:`<c-b>` 表示 Ctrl+B、`<a-b>` 表示 Alt+B,`B`表示 Shift+B | ||||
| | `` D `` | 重設 | View reset options for working tree (e.g. nuking the working tree). | | ||||
| | `` ` `` | 顯示檔案樹狀視圖 | Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.<br><br>The default can be changed in the config file with the key 'gui.showFileTree'. | | ||||
| | `` <c-t> `` | 開啟外部差異工具 (git difftool) |  | | ||||
| | `` M `` | 開啟外部合併工具 | 執行 `git mergetool`。 | | ||||
| | `` M `` | View merge conflict options | View options for resolving merge conflicts. | | ||||
| | `` f `` | 擷取 | 同步遠端異動 | | ||||
| | `` - `` | Collapse all files | Collapse all directories in the files tree | | ||||
| | `` = `` | Expand all files | Expand all directories in the file tree | | ||||
|   | ||||
| @@ -5,6 +5,7 @@ import ( | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/go-errors/errors" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| @@ -407,3 +408,46 @@ func (self *WorkingTreeCommands) ResetMixed(ref string) error { | ||||
|  | ||||
| 	return self.cmd.New(cmdArgs).Run() | ||||
| } | ||||
|  | ||||
| func (self *WorkingTreeCommands) ShowFileAtStage(path string, stage int) (string, error) { | ||||
| 	cmdArgs := NewGitCmd("show"). | ||||
| 		Arg(fmt.Sprintf(":%d:%s", stage, path)). | ||||
| 		ToArgv() | ||||
|  | ||||
| 	return self.cmd.New(cmdArgs).RunWithOutput() | ||||
| } | ||||
|  | ||||
| func (self *WorkingTreeCommands) ObjectIDAtStage(path string, stage int) (string, error) { | ||||
| 	cmdArgs := NewGitCmd("rev-parse"). | ||||
| 		Arg(fmt.Sprintf(":%d:%s", stage, path)). | ||||
| 		ToArgv() | ||||
|  | ||||
| 	output, err := self.cmd.New(cmdArgs).RunWithOutput() | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	return strings.TrimSpace(output), nil | ||||
| } | ||||
|  | ||||
| func (self *WorkingTreeCommands) MergeFileForFiles(strategy string, oursFilepath string, baseFilepath string, theirsFilepath string) (string, error) { | ||||
| 	cmdArgs := NewGitCmd("merge-file"). | ||||
| 		Arg(strategy). | ||||
| 		Arg("--stdout"). | ||||
| 		Arg(oursFilepath, baseFilepath, theirsFilepath). | ||||
| 		ToArgv() | ||||
|  | ||||
| 	return self.cmd.New(cmdArgs).RunWithOutput() | ||||
| } | ||||
|  | ||||
| // OIDs mode (Git 2.43+) | ||||
| func (self *WorkingTreeCommands) MergeFileForObjectIDs(strategy string, oursID string, baseID string, theirsID string) (string, error) { | ||||
| 	cmdArgs := NewGitCmd("merge-file"). | ||||
| 		Arg(strategy). | ||||
| 		Arg("--stdout"). | ||||
| 		Arg("--object-id"). | ||||
| 		Arg(oursID, baseID, theirsID). | ||||
| 		ToArgv() | ||||
|  | ||||
| 	return self.cmd.New(cmdArgs).RunWithOutput() | ||||
| } | ||||
|   | ||||
| @@ -272,6 +272,7 @@ func computeMigratedConfig(path string, content []byte, changes *ChangesSet) ([] | ||||
| 		{[]string{"gui", "skipUnstageLineWarning"}, "skipDiscardChangeWarning"}, | ||||
| 		{[]string{"keybinding", "universal", "executeCustomCommand"}, "executeShellCommand"}, | ||||
| 		{[]string{"gui", "windowSize"}, "screenMode"}, | ||||
| 		{[]string{"keybinding", "files", "openMergeTool"}, "openMergeOptions"}, | ||||
| 	} | ||||
|  | ||||
| 	for _, pathToReplace := range pathsToReplace { | ||||
|   | ||||
| @@ -897,7 +897,7 @@ keybinding: | ||||
|     toggleStagedAll: a | ||||
|     viewResetOptions: D | ||||
|     fetch: f | ||||
|     openMergeTool: M | ||||
|     openMergeOptions: M | ||||
|     openStatusFilter: <c-b> | ||||
|     copyFileInfoToClipboard: "y" | ||||
|     collapseAll: '-' | ||||
|   | ||||
| @@ -487,7 +487,7 @@ type KeybindingFilesConfig struct { | ||||
| 	ViewResetOptions         string `yaml:"viewResetOptions"` | ||||
| 	Fetch                    string `yaml:"fetch"` | ||||
| 	ToggleTreeView           string `yaml:"toggleTreeView"` | ||||
| 	OpenMergeTool            string `yaml:"openMergeTool"` | ||||
| 	OpenMergeOptions         string `yaml:"openMergeOptions"` | ||||
| 	OpenStatusFilter         string `yaml:"openStatusFilter"` | ||||
| 	CopyFileInfoToClipboard  string `yaml:"copyFileInfoToClipboard"` | ||||
| 	CollapseAll              string `yaml:"collapseAll"` | ||||
| @@ -950,7 +950,7 @@ func GetDefaultConfig() *UserConfig { | ||||
| 				ViewResetOptions:         "D", | ||||
| 				Fetch:                    "f", | ||||
| 				ToggleTreeView:           "`", | ||||
| 				OpenMergeTool:            "M", | ||||
| 				OpenMergeOptions:         "M", | ||||
| 				OpenStatusFilter:         "<c-b>", | ||||
| 				ConfirmDiscard:           "x", | ||||
| 				CopyFileInfoToClipboard:  "y", | ||||
|   | ||||
| @@ -124,8 +124,8 @@ func (gui *Gui) getRandomTip() string { | ||||
| 			formattedKey(config.Universal.Remove), | ||||
| 		), | ||||
| 		fmt.Sprintf( | ||||
| 			"If you need to pull out the big guns to resolve merge conflicts, you can press '%s' in the files panel to open 'git mergetool'", | ||||
| 			formattedKey(config.Files.OpenMergeTool), | ||||
| 			"If you need to pull out the big guns to resolve merge conflicts, you can press '%s' in the files panel to open merge options", | ||||
| 			formattedKey(config.Files.OpenMergeOptions), | ||||
| 		), | ||||
| 		fmt.Sprintf( | ||||
| 			"To revert a commit, press '%s' on that commit", | ||||
|   | ||||
| @@ -98,7 +98,7 @@ func (gui *Gui) resetHelpersAndControllers() { | ||||
| 		Bisect:          bisectHelper, | ||||
| 		Suggestions:     suggestionsHelper, | ||||
| 		Files:           helpers.NewFilesHelper(helperCommon), | ||||
| 		WorkingTree:     helpers.NewWorkingTreeHelper(helperCommon, refsHelper, commitsHelper, gpgHelper), | ||||
| 		WorkingTree:     helpers.NewWorkingTreeHelper(helperCommon, refsHelper, commitsHelper, gpgHelper, rebaseHelper), | ||||
| 		Tags:            helpers.NewTagsHelper(helperCommon, commitsHelper, gpgHelper), | ||||
| 		BranchesHelper:  helpers.NewBranchesHelper(helperCommon, worktreeHelper), | ||||
| 		GPG:             helpers.NewGpgHelper(helperCommon), | ||||
|   | ||||
| @@ -178,10 +178,13 @@ func (self *FilesController) GetKeybindings(opts types.KeybindingsOpts) []*types | ||||
| 			Description:       self.c.Tr.OpenDiffTool, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Key:         opts.GetKey(opts.Config.Files.OpenMergeTool), | ||||
| 			Handler:     self.c.Helpers().WorkingTree.OpenMergeTool, | ||||
| 			Description: self.c.Tr.OpenMergeTool, | ||||
| 			Tooltip:     self.c.Tr.OpenMergeToolTooltip, | ||||
| 			Key:               opts.GetKey(opts.Config.Files.OpenMergeOptions), | ||||
| 			Handler:           self.withItems(self.openMergeConflictMenu), | ||||
| 			Description:       self.c.Tr.ViewMergeConflictOptions, | ||||
| 			Tooltip:           self.c.Tr.ViewMergeConflictOptionsTooltip, | ||||
| 			GetDisabledReason: self.require(self.itemsSelected(self.canOpenMergeConflictMenu)), | ||||
| 			OpensMenu:         true, | ||||
| 			DisplayOnScreen:   true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			Key:         opts.GetKey(opts.Config.Files.Fetch), | ||||
| @@ -1024,6 +1027,34 @@ func (self *FilesController) createStashMenu() error { | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (self *FilesController) openMergeConflictMenu(nodes []*filetree.FileNode) error { | ||||
| 	normalizedNodes := flattenSelectedNodesToFiles(nodes) | ||||
|  | ||||
| 	fileNodesWithConflicts := lo.Filter(normalizedNodes, func(node *filetree.FileNode, _ int) bool { | ||||
| 		return node.File != nil && node.File.HasInlineMergeConflicts | ||||
| 	}) | ||||
|  | ||||
| 	filepaths := lo.Map(fileNodesWithConflicts, func(node *filetree.FileNode, _ int) string { | ||||
| 		return node.GetPath() | ||||
| 	}) | ||||
|  | ||||
| 	return self.c.Helpers().WorkingTree.CreateMergeConflictMenu(filepaths) | ||||
| } | ||||
|  | ||||
| func (self *FilesController) canOpenMergeConflictMenu(nodes []*filetree.FileNode) *types.DisabledReason { | ||||
| 	normalizedNodes := flattenSelectedNodesToFiles(nodes) | ||||
|  | ||||
| 	hasFileNodesWithConflicts := lo.SomeBy(normalizedNodes, func(node *filetree.FileNode) bool { | ||||
| 		return node.File != nil && node.File.HasInlineMergeConflicts | ||||
| 	}) | ||||
|  | ||||
| 	if !hasFileNodesWithConflicts { | ||||
| 		return &types.DisabledReason{Text: self.c.Tr.NoFilesWithMergeConflicts} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (self *FilesController) openCopyMenu() error { | ||||
| 	node := self.context().GetSelected() | ||||
|  | ||||
| @@ -1237,6 +1268,38 @@ func isDescendentOfSelectedNodes(node *filetree.FileNode, selectedNodes []*filet | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // BFS algorithm for expanding directories into their children, | ||||
| // and for collecting the unique file nodes | ||||
| func flattenSelectedNodesToFiles(selectedNodes []*filetree.FileNode) []*filetree.FileNode { | ||||
| 	queue := append(make([]*filetree.FileNode, 0, len(selectedNodes)), selectedNodes...) | ||||
| 	visited := set.New[string]() | ||||
| 	var files []*filetree.FileNode | ||||
|  | ||||
| 	for len(queue) > 0 { | ||||
| 		// pop node from queue | ||||
| 		node := queue[0] | ||||
| 		queue = queue[1:] | ||||
|  | ||||
| 		nodeID := node.ID() | ||||
| 		if visited.Includes(nodeID) { | ||||
| 			continue | ||||
| 		} | ||||
| 		visited.Add(nodeID) | ||||
|  | ||||
| 		if node.File != nil { | ||||
| 			// unique file node -> collect it | ||||
| 			files = append(files, node) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// directory node -> enqueue children | ||||
| 		for _, ch := range node.Children { | ||||
| 			queue = append(queue, &filetree.FileNode{Node: ch}) | ||||
| 		} | ||||
| 	} | ||||
| 	return files | ||||
| } | ||||
|  | ||||
| func someNodesHaveUnstagedChanges(nodes []*filetree.FileNode) bool { | ||||
| 	return lo.SomeBy(nodes, (*filetree.FileNode).GetHasUnstagedChanges) | ||||
| } | ||||
|   | ||||
| @@ -3,21 +3,24 @@ package helpers | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"regexp" | ||||
|  | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/git_commands" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/commands/models" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/config" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/style" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/types" | ||||
| 	"github.com/samber/lo" | ||||
| ) | ||||
|  | ||||
| type WorkingTreeHelper struct { | ||||
| 	c             *HelperCommon | ||||
| 	refHelper     *RefsHelper | ||||
| 	commitsHelper *CommitsHelper | ||||
| 	gpgHelper     *GpgHelper | ||||
| 	c                    *HelperCommon | ||||
| 	refHelper            *RefsHelper | ||||
| 	commitsHelper        *CommitsHelper | ||||
| 	gpgHelper            *GpgHelper | ||||
| 	mergeAndRebaseHelper *MergeAndRebaseHelper | ||||
| } | ||||
|  | ||||
| func NewWorkingTreeHelper( | ||||
| @@ -25,12 +28,14 @@ func NewWorkingTreeHelper( | ||||
| 	refHelper *RefsHelper, | ||||
| 	commitsHelper *CommitsHelper, | ||||
| 	gpgHelper *GpgHelper, | ||||
| 	mergeAndRebaseHelper *MergeAndRebaseHelper, | ||||
| ) *WorkingTreeHelper { | ||||
| 	return &WorkingTreeHelper{ | ||||
| 		c:             c, | ||||
| 		refHelper:     refHelper, | ||||
| 		commitsHelper: commitsHelper, | ||||
| 		gpgHelper:     gpgHelper, | ||||
| 		c:                    c, | ||||
| 		refHelper:            refHelper, | ||||
| 		commitsHelper:        commitsHelper, | ||||
| 		gpgHelper:            gpgHelper, | ||||
| 		mergeAndRebaseHelper: mergeAndRebaseHelper, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -247,3 +252,135 @@ func (self *WorkingTreeHelper) commitPrefixConfigsForRepo() []config.CommitPrefi | ||||
|  | ||||
| 	return self.c.UserConfig().Git.CommitPrefix | ||||
| } | ||||
|  | ||||
| func (self *WorkingTreeHelper) mergeFile(filepath string, strategy string) (string, error) { | ||||
| 	if self.c.Git().Version.IsOlderThan(2, 43, 0) { | ||||
| 		return self.mergeFileWithTempFiles(filepath, strategy) | ||||
| 	} | ||||
|  | ||||
| 	return self.mergeFileWithObjectIDs(filepath, strategy) | ||||
| } | ||||
|  | ||||
| func (self *WorkingTreeHelper) mergeFileWithTempFiles(filepath string, strategy string) (string, error) { | ||||
| 	showToTempFile := func(stage int, label string) (string, error) { | ||||
| 		output, err := self.c.Git().WorkingTree.ShowFileAtStage(filepath, stage) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
|  | ||||
| 		f, err := os.CreateTemp(self.c.GetConfig().GetTempDir(), "mergefile-"+label+"-*") | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		defer f.Close() | ||||
|  | ||||
| 		if _, err := f.Write([]byte(output)); err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
|  | ||||
| 		return f.Name(), nil | ||||
| 	} | ||||
|  | ||||
| 	baseFilepath, err := showToTempFile(1, "base") | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	defer os.Remove(baseFilepath) | ||||
|  | ||||
| 	oursFilepath, err := showToTempFile(2, "ours") | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	defer os.Remove(oursFilepath) | ||||
|  | ||||
| 	theirsFilepath, err := showToTempFile(3, "theirs") | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
| 	defer os.Remove(theirsFilepath) | ||||
|  | ||||
| 	return self.c.Git().WorkingTree.MergeFileForFiles(strategy, oursFilepath, baseFilepath, theirsFilepath) | ||||
| } | ||||
|  | ||||
| func (self *WorkingTreeHelper) mergeFileWithObjectIDs(filepath, strategy string) (string, error) { | ||||
| 	baseID, err := self.c.Git().WorkingTree.ObjectIDAtStage(filepath, 1) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	oursID, err := self.c.Git().WorkingTree.ObjectIDAtStage(filepath, 2) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	theirsID, err := self.c.Git().WorkingTree.ObjectIDAtStage(filepath, 3) | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	return self.c.Git().WorkingTree.MergeFileForObjectIDs(strategy, oursID, baseID, theirsID) | ||||
| } | ||||
|  | ||||
| func (self *WorkingTreeHelper) CreateMergeConflictMenu(selectedFilepaths []string) error { | ||||
| 	onMergeStrategySelected := func(strategy string) error { | ||||
| 		for _, filepath := range selectedFilepaths { | ||||
| 			output, err := self.mergeFile(filepath, strategy) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			if err = os.WriteFile(filepath, []byte(output), 0o644); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		err := self.c.Git().WorkingTree.StageFiles(selectedFilepaths, nil) | ||||
| 		self.c.Refresh(types.RefreshOptions{Mode: types.SYNC, Scope: []types.RefreshableView{types.FILES}}) | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	cmdColor := style.FgBlue | ||||
| 	return self.c.Menu(types.CreateMenuOptions{ | ||||
| 		Title: self.c.Tr.MergeConflictOptionsTitle, | ||||
| 		Items: []*types.MenuItem{ | ||||
| 			{ | ||||
| 				LabelColumns: []string{ | ||||
| 					self.c.Tr.UseCurrentChanges, | ||||
| 					cmdColor.Sprint("git merge-file --ours"), | ||||
| 				}, | ||||
| 				OnPress: func() error { | ||||
| 					return onMergeStrategySelected("--ours") | ||||
| 				}, | ||||
| 				Key: 'c', | ||||
| 			}, | ||||
| 			{ | ||||
| 				LabelColumns: []string{ | ||||
| 					self.c.Tr.UseIncomingChanges, | ||||
| 					cmdColor.Sprint("git merge-file --theirs"), | ||||
| 				}, | ||||
| 				OnPress: func() error { | ||||
| 					return onMergeStrategySelected("--theirs") | ||||
| 				}, | ||||
| 				Key: 'i', | ||||
| 			}, | ||||
| 			{ | ||||
| 				LabelColumns: []string{ | ||||
| 					self.c.Tr.UseBothChanges, | ||||
| 					cmdColor.Sprint("git merge-file --union"), | ||||
| 				}, | ||||
| 				OnPress: func() error { | ||||
| 					return onMergeStrategySelected("--union") | ||||
| 				}, | ||||
| 				Key: 'b', | ||||
| 			}, | ||||
| 			{ | ||||
| 				LabelColumns: []string{ | ||||
| 					self.c.Tr.OpenMergeTool, | ||||
| 					cmdColor.Sprint("git mergetool"), | ||||
| 				}, | ||||
| 				OnPress: self.OpenMergeTool, | ||||
| 				Key:     'm', | ||||
| 			}, | ||||
| 		}, | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -112,10 +112,11 @@ func (self *MergeConflictsController) GetKeybindings(opts types.KeybindingsOpts) | ||||
| 			Tag:         "navigation", | ||||
| 		}, | ||||
| 		{ | ||||
| 			Key:             opts.GetKey(opts.Config.Files.OpenMergeTool), | ||||
| 			Handler:         self.c.Helpers().WorkingTree.OpenMergeTool, | ||||
| 			Description:     self.c.Tr.OpenMergeTool, | ||||
| 			Tooltip:         self.c.Tr.OpenMergeToolTooltip, | ||||
| 			Key:             opts.GetKey(opts.Config.Files.OpenMergeOptions), | ||||
| 			Handler:         self.openMergeConflictMenu, | ||||
| 			Description:     self.c.Tr.ViewMergeConflictOptions, | ||||
| 			Tooltip:         self.c.Tr.ViewMergeConflictOptionsTooltip, | ||||
| 			OpensMenu:       true, | ||||
| 			DisplayOnScreen: true, | ||||
| 		}, | ||||
| 		{ | ||||
| @@ -320,6 +321,11 @@ func (self *MergeConflictsController) onLastConflictResolved() { | ||||
| 	self.c.Refresh(types.RefreshOptions{Mode: types.ASYNC, Scope: []types.RefreshableView{types.FILES}}) | ||||
| } | ||||
|  | ||||
| func (self *MergeConflictsController) openMergeConflictMenu() error { | ||||
| 	filepath := self.context().GetState().GetPath() | ||||
| 	return self.c.Helpers().WorkingTree.CreateMergeConflictMenu([]string{filepath}) | ||||
| } | ||||
|  | ||||
| func (self *MergeConflictsController) withRenderAndFocus(f func() error) func() error { | ||||
| 	return self.withLock(func() error { | ||||
| 		if err := f(); err != nil { | ||||
|   | ||||
| @@ -63,7 +63,6 @@ type TranslationSet struct { | ||||
| 	ToggleTreeViewTooltip                 string | ||||
| 	OpenDiffTool                          string | ||||
| 	OpenMergeTool                         string | ||||
| 	OpenMergeToolTooltip                  string | ||||
| 	Refresh                               string | ||||
| 	RefreshTooltip                        string | ||||
| 	Push                                  string | ||||
| @@ -898,6 +897,13 @@ type TranslationSet struct { | ||||
| 	BreakingChangesTitle                     string | ||||
| 	BreakingChangesMessage                   string | ||||
| 	BreakingChangesByVersion                 map[string]string | ||||
| 	ViewMergeConflictOptions                 string | ||||
| 	ViewMergeConflictOptionsTooltip          string | ||||
| 	NoFilesWithMergeConflicts                string | ||||
| 	MergeConflictOptionsTitle                string | ||||
| 	UseCurrentChanges                        string | ||||
| 	UseIncomingChanges                       string | ||||
| 	UseBothChanges                           string | ||||
| } | ||||
|  | ||||
| type Bisect struct { | ||||
| @@ -1136,7 +1142,6 @@ func EnglishTranslationSet() *TranslationSet { | ||||
| 		ToggleTreeViewTooltip:                "Toggle file view between flat and tree layout. Flat layout shows all file paths in a single list, tree layout groups files by directory.\n\nThe default can be changed in the config file with the key 'gui.showFileTree'.", | ||||
| 		OpenDiffTool:                         "Open external diff tool (git difftool)", | ||||
| 		OpenMergeTool:                        "Open external merge tool", | ||||
| 		OpenMergeToolTooltip:                 "Run `git mergetool`.", | ||||
| 		Refresh:                              "Refresh", | ||||
| 		RefreshTooltip:                       "Refresh the git state (i.e. run `git status`, `git branch`, etc in background to update the contents of panels). This does not run `git fetch`.", | ||||
| 		Push:                                 "Push", | ||||
| @@ -1970,6 +1975,13 @@ func EnglishTranslationSet() *TranslationSet { | ||||
| 		CustomCommands:                           "Custom commands", | ||||
| 		NoApplicableCommandsInThisContext:        "(No applicable commands in this context)", | ||||
| 		SelectCommitsOfCurrentBranch:             "Select commits of current branch", | ||||
| 		ViewMergeConflictOptions:                 "View merge conflict options", | ||||
| 		ViewMergeConflictOptionsTooltip:          "View options for resolving merge conflicts.", | ||||
| 		NoFilesWithMergeConflicts:                "There are no files with merge conflicts.", | ||||
| 		MergeConflictOptionsTitle:                "Resolve merge conflicts", | ||||
| 		UseCurrentChanges:                        "Use current changes", | ||||
| 		UseIncomingChanges:                       "Use incoming changes", | ||||
| 		UseBothChanges:                           "Use both", | ||||
|  | ||||
| 		Actions: Actions{ | ||||
| 			// TODO: combine this with the original keybinding descriptions (those are all in lowercase atm) | ||||
|   | ||||
							
								
								
									
										77
									
								
								pkg/integration/tests/conflicts/merge_file_both.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								pkg/integration/tests/conflicts/merge_file_both.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | ||||
| package conflicts | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/config" | ||||
| 	. "github.com/jesseduffield/lazygit/pkg/integration/components" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/integration/tests/shared" | ||||
| ) | ||||
|  | ||||
| func testDataBoth() (original, current, incoming, final string) { | ||||
| 	original = ` | ||||
| 1 | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5 | ||||
| 6 | ||||
| ` | ||||
| 	current = ` | ||||
| 1a | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5a | ||||
| 6 | ||||
| ` | ||||
| 	incoming = ` | ||||
| 1 | ||||
| 2 | ||||
| 3b | ||||
| 4 | ||||
| 5b | ||||
| 6 | ||||
| ` | ||||
| 	final = ` | ||||
| 1a | ||||
| 2 | ||||
| 3b | ||||
| 4 | ||||
| 5a | ||||
| 5b | ||||
| 6 | ||||
| ` | ||||
| 	return original, current, incoming, final | ||||
| } | ||||
|  | ||||
| var MergeFileBoth = NewIntegrationTest(NewIntegrationTestArgs{ | ||||
| 	Description:  "Conflicting file can be resolved to 'union' (both changes) version via merge-file", | ||||
| 	ExtraCmdArgs: []string{}, | ||||
| 	Skip:         false, | ||||
| 	SetupConfig:  func(config *config.AppConfig) {}, | ||||
| 	SetupRepo: func(shell *Shell) { | ||||
| 		original, current, incoming, _ := testDataBoth() | ||||
| 		shared.CreateMergeConflictFileForMergeFileTests(shell, original, current, incoming) | ||||
| 	}, | ||||
| 	Run: func(t *TestDriver, keys config.KeybindingConfig) { | ||||
| 		_, _, _, expected := testDataBoth() | ||||
|  | ||||
| 		t.Views().Files(). | ||||
| 			IsFocused(). | ||||
| 			Lines( | ||||
| 				Contains("file").IsSelected(), | ||||
| 			) | ||||
|  | ||||
| 		t.GlobalPress(keys.Files.OpenMergeOptions) | ||||
|  | ||||
| 		t.ExpectPopup().Menu(). | ||||
| 			Title(Equals("Resolve merge conflicts")). | ||||
| 			Select(Contains("Use both")). // merge-file --union | ||||
| 			Confirm() | ||||
|  | ||||
| 		t.Common().ContinueOnConflictsResolved("merge") | ||||
|  | ||||
| 		t.Views().Files().IsEmpty() | ||||
|  | ||||
| 		t.FileSystem().FileContent("file", Equals(expected)) | ||||
| 	}, | ||||
| }) | ||||
							
								
								
									
										76
									
								
								pkg/integration/tests/conflicts/merge_file_current.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								pkg/integration/tests/conflicts/merge_file_current.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| package conflicts | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/config" | ||||
| 	. "github.com/jesseduffield/lazygit/pkg/integration/components" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/integration/tests/shared" | ||||
| ) | ||||
|  | ||||
| func testDataCurrent() (original, current, incoming, final string) { | ||||
| 	original = ` | ||||
| 1 | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5 | ||||
| 6 | ||||
| ` | ||||
| 	current = ` | ||||
| 1 | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5a | ||||
| 6 | ||||
| ` | ||||
| 	incoming = ` | ||||
| 1b | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5b | ||||
| 6 | ||||
| ` | ||||
| 	final = ` | ||||
| 1b | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5a | ||||
| 6 | ||||
| ` | ||||
| 	return original, current, incoming, final | ||||
| } | ||||
|  | ||||
| var MergeFileCurrent = NewIntegrationTest(NewIntegrationTestArgs{ | ||||
| 	Description:  "Conflicting file can be resolved to 'ours' (current changes) version via merge-file", | ||||
| 	ExtraCmdArgs: []string{}, | ||||
| 	Skip:         false, | ||||
| 	SetupConfig:  func(config *config.AppConfig) {}, | ||||
| 	SetupRepo: func(shell *Shell) { | ||||
| 		original, current, incoming, _ := testDataCurrent() | ||||
| 		shared.CreateMergeConflictFileForMergeFileTests(shell, original, current, incoming) | ||||
| 	}, | ||||
| 	Run: func(t *TestDriver, keys config.KeybindingConfig) { | ||||
| 		_, _, _, expected := testDataCurrent() | ||||
|  | ||||
| 		t.Views().Files(). | ||||
| 			IsFocused(). | ||||
| 			Lines( | ||||
| 				Contains("file").IsSelected(), | ||||
| 			) | ||||
|  | ||||
| 		t.GlobalPress(keys.Files.OpenMergeOptions) | ||||
|  | ||||
| 		t.ExpectPopup().Menu(). | ||||
| 			Title(Equals("Resolve merge conflicts")). | ||||
| 			Select(Contains("Use current changes")). // merge-file --ours | ||||
| 			Confirm() | ||||
|  | ||||
| 		t.Common().ContinueOnConflictsResolved("merge") | ||||
|  | ||||
| 		t.Views().Files().IsEmpty() | ||||
|  | ||||
| 		t.FileSystem().FileContent("file", Equals(expected)) | ||||
| 	}, | ||||
| }) | ||||
							
								
								
									
										76
									
								
								pkg/integration/tests/conflicts/merge_file_incoming.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								pkg/integration/tests/conflicts/merge_file_incoming.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| package conflicts | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/config" | ||||
| 	. "github.com/jesseduffield/lazygit/pkg/integration/components" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/integration/tests/shared" | ||||
| ) | ||||
|  | ||||
| func testDataIncoming() (original, current, incoming, final string) { | ||||
| 	original = ` | ||||
| 1 | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5 | ||||
| 6 | ||||
| ` | ||||
| 	current = ` | ||||
| 1a | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5a | ||||
| 6 | ||||
| ` | ||||
| 	incoming = ` | ||||
| 1 | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5b | ||||
| 6 | ||||
| ` | ||||
| 	final = ` | ||||
| 1a | ||||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5b | ||||
| 6 | ||||
| ` | ||||
| 	return original, current, incoming, final | ||||
| } | ||||
|  | ||||
| var MergeFileIncoming = NewIntegrationTest(NewIntegrationTestArgs{ | ||||
| 	Description:  "Conflicting file can be resolved to 'theirs' (incoming changes) version via merge-file", | ||||
| 	ExtraCmdArgs: []string{}, | ||||
| 	Skip:         false, | ||||
| 	SetupConfig:  func(config *config.AppConfig) {}, | ||||
| 	SetupRepo: func(shell *Shell) { | ||||
| 		original, current, incoming, _ := testDataIncoming() | ||||
| 		shared.CreateMergeConflictFileForMergeFileTests(shell, original, current, incoming) | ||||
| 	}, | ||||
| 	Run: func(t *TestDriver, keys config.KeybindingConfig) { | ||||
| 		_, _, _, expected := testDataIncoming() | ||||
|  | ||||
| 		t.Views().Files(). | ||||
| 			IsFocused(). | ||||
| 			Lines( | ||||
| 				Contains("file").IsSelected(), | ||||
| 			) | ||||
|  | ||||
| 		t.GlobalPress(keys.Files.OpenMergeOptions) | ||||
|  | ||||
| 		t.ExpectPopup().Menu(). | ||||
| 			Title(Equals("Resolve merge conflicts")). | ||||
| 			Select(Contains("Use incoming changes")). // merge-file --theirs | ||||
| 			Confirm() | ||||
|  | ||||
| 		t.Common().ContinueOnConflictsResolved("merge") | ||||
|  | ||||
| 		t.Views().Files().IsEmpty() | ||||
|  | ||||
| 		t.FileSystem().FileContent("file", Equals(expected)) | ||||
| 	}, | ||||
| }) | ||||
| @@ -157,3 +157,20 @@ var CreateMergeConflictFileMultiple = func(shell *Shell) { | ||||
|  | ||||
| 	shell.RunCommandExpectError([]string{"git", "merge", "--no-edit", "second-change-branch"}) | ||||
| } | ||||
|  | ||||
| var CreateMergeConflictFileForMergeFileTests = func(shell *Shell, originalFileContent string, currentChangeFileContent string, incomingChangeFileContent string) { | ||||
| 	shell. | ||||
| 		NewBranch("original-branch"). | ||||
| 		EmptyCommit("one"). | ||||
| 		CreateFileAndAdd("file", originalFileContent). | ||||
| 		Commit("original"). | ||||
| 		NewBranch("current-change-branch"). | ||||
| 		UpdateFileAndAdd("file", currentChangeFileContent). | ||||
| 		Commit("first change"). | ||||
| 		Checkout("original-branch"). | ||||
| 		NewBranch("incoming-change-branch"). | ||||
| 		UpdateFileAndAdd("file", incomingChangeFileContent). | ||||
| 		Commit("second change"). | ||||
| 		Checkout("current-change-branch"). | ||||
| 		RunCommandExpectError([]string{"git", "merge", "--no-edit", "incoming-change-branch"}) | ||||
| } | ||||
|   | ||||
| @@ -150,6 +150,9 @@ var tests = []*components.IntegrationTest{ | ||||
| 	config.NegativeRefspec, | ||||
| 	config.RemoteNamedStar, | ||||
| 	conflicts.Filter, | ||||
| 	conflicts.MergeFileBoth, | ||||
| 	conflicts.MergeFileCurrent, | ||||
| 	conflicts.MergeFileIncoming, | ||||
| 	conflicts.ResolveExternally, | ||||
| 	conflicts.ResolveMultipleFiles, | ||||
| 	conflicts.ResolveNoAutoStage, | ||||
|   | ||||
| @@ -1115,7 +1115,7 @@ | ||||
|           "type": "string", | ||||
|           "default": "`" | ||||
|         }, | ||||
|         "openMergeTool": { | ||||
|         "openMergeOptions": { | ||||
|           "type": "string", | ||||
|           "default": "M" | ||||
|         }, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user