mirror of
				https://github.com/jesseduffield/lazygit.git
				synced 2025-10-30 23:57:43 +02:00 
			
		
		
		
	Add no-ff merge option (#4966)
This will put whatever git's default merge variant is as the first menu item, and add a second item which is the opposite (no-ff if the default is ff, and vice versa). Which one is the default depends on whether a fast-forward merge is possible, and whether users have set git's `merge.ff` config or lazygit's `git.merging.args` config. If users prefer to always have the same option first no matter whether it's applicable, they can make ff always appear first by setting git's `merge.ff` config to "true" or "only", or by setting lazygit's `git.merging.args` config to "--ff" or "--ff-only"; if they want no-ff to appear first, they can do that by setting git's `merge.ff` config to "false", or by setting lazygit's `git.merging.args` config to "--no-ff". Which of these they choose depends on whether they want the config to also apply to other git clients including the cli, or only to lazygit. Closes #4091.
This commit is contained in:
		| @@ -250,26 +250,51 @@ func (self *BranchCommands) Rename(oldName string, newName string) error { | ||||
| 	return self.cmd.New(cmdArgs).Run() | ||||
| } | ||||
|  | ||||
| type MergeOpts struct { | ||||
| 	FastForwardOnly bool | ||||
| 	Squash          bool | ||||
| } | ||||
| type MergeVariant int | ||||
|  | ||||
| const ( | ||||
| 	MERGE_VARIANT_REGULAR MergeVariant = iota | ||||
| 	MERGE_VARIANT_FAST_FORWARD | ||||
| 	MERGE_VARIANT_NON_FAST_FORWARD | ||||
| 	MERGE_VARIANT_SQUASH | ||||
| ) | ||||
|  | ||||
| func (self *BranchCommands) Merge(branchName string, variant MergeVariant) error { | ||||
| 	extraArgs := func() []string { | ||||
| 		switch variant { | ||||
| 		case MERGE_VARIANT_REGULAR: | ||||
| 			return []string{} | ||||
| 		case MERGE_VARIANT_FAST_FORWARD: | ||||
| 			return []string{"--ff"} | ||||
| 		case MERGE_VARIANT_NON_FAST_FORWARD: | ||||
| 			return []string{"--no-ff"} | ||||
| 		case MERGE_VARIANT_SQUASH: | ||||
| 			return []string{"--squash", "--ff"} | ||||
| 		} | ||||
|  | ||||
| 		panic("shouldn't get here") | ||||
| 	}() | ||||
|  | ||||
| func (self *BranchCommands) Merge(branchName string, opts MergeOpts) error { | ||||
| 	if opts.Squash && opts.FastForwardOnly { | ||||
| 		panic("Squash and FastForwardOnly can't both be true") | ||||
| 	} | ||||
| 	cmdArgs := NewGitCmd("merge"). | ||||
| 		Arg("--no-edit"). | ||||
| 		Arg(strings.Fields(self.UserConfig().Git.Merging.Args)...). | ||||
| 		ArgIf(opts.FastForwardOnly, "--ff-only"). | ||||
| 		ArgIf(opts.Squash, "--squash", "--ff"). | ||||
| 		Arg(extraArgs...). | ||||
| 		Arg(branchName). | ||||
| 		ToArgv() | ||||
|  | ||||
| 	return self.cmd.New(cmdArgs).Run() | ||||
| } | ||||
|  | ||||
| // Returns whether refName can be fast-forward merged into the current branch | ||||
| func (self *BranchCommands) CanDoFastForwardMerge(refName string) bool { | ||||
| 	cmdArgs := NewGitCmd("merge-base"). | ||||
| 		Arg("--is-ancestor"). | ||||
| 		Arg("HEAD", refName). | ||||
| 		ToArgv() | ||||
| 	err := self.cmd.New(cmdArgs).DontLog().Run() | ||||
| 	return err == nil | ||||
| } | ||||
|  | ||||
| // Only choose between non-empty, non-identical commands | ||||
| func (self *BranchCommands) allBranchesLogCandidates() []string { | ||||
| 	return lo.Uniq(lo.WithoutEmpty(self.UserConfig().Git.AllBranchesLogCmds)) | ||||
|   | ||||
| @@ -122,14 +122,14 @@ func TestBranchMerge(t *testing.T) { | ||||
| 	scenarios := []struct { | ||||
| 		testName   string | ||||
| 		userConfig *config.UserConfig | ||||
| 		opts       MergeOpts | ||||
| 		variant    MergeVariant | ||||
| 		branchName string | ||||
| 		expected   []string | ||||
| 	}{ | ||||
| 		{ | ||||
| 			testName:   "basic", | ||||
| 			userConfig: &config.UserConfig{}, | ||||
| 			opts:       MergeOpts{}, | ||||
| 			variant:    MERGE_VARIANT_REGULAR, | ||||
| 			branchName: "mybranch", | ||||
| 			expected:   []string{"merge", "--no-edit", "mybranch"}, | ||||
| 		}, | ||||
| @@ -142,7 +142,7 @@ func TestBranchMerge(t *testing.T) { | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			opts:       MergeOpts{}, | ||||
| 			variant:    MERGE_VARIANT_REGULAR, | ||||
| 			branchName: "mybranch", | ||||
| 			expected:   []string{"merge", "--no-edit", "--merging-args", "mybranch"}, | ||||
| 		}, | ||||
| @@ -155,16 +155,30 @@ func TestBranchMerge(t *testing.T) { | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 			opts:       MergeOpts{}, | ||||
| 			variant:    MERGE_VARIANT_REGULAR, | ||||
| 			branchName: "mybranch", | ||||
| 			expected:   []string{"merge", "--no-edit", "--arg1", "--arg2", "mybranch"}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			testName:   "fast forward only", | ||||
| 			testName:   "fast-forward merge", | ||||
| 			userConfig: &config.UserConfig{}, | ||||
| 			opts:       MergeOpts{FastForwardOnly: true}, | ||||
| 			variant:    MERGE_VARIANT_FAST_FORWARD, | ||||
| 			branchName: "mybranch", | ||||
| 			expected:   []string{"merge", "--no-edit", "--ff-only", "mybranch"}, | ||||
| 			expected:   []string{"merge", "--no-edit", "--ff", "mybranch"}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			testName:   "non-fast-forward merge", | ||||
| 			userConfig: &config.UserConfig{}, | ||||
| 			variant:    MERGE_VARIANT_NON_FAST_FORWARD, | ||||
| 			branchName: "mybranch", | ||||
| 			expected:   []string{"merge", "--no-edit", "--no-ff", "mybranch"}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			testName:   "squash merge", | ||||
| 			userConfig: &config.UserConfig{}, | ||||
| 			variant:    MERGE_VARIANT_SQUASH, | ||||
| 			branchName: "mybranch", | ||||
| 			expected:   []string{"merge", "--no-edit", "--squash", "--ff", "mybranch"}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| @@ -174,7 +188,7 @@ func TestBranchMerge(t *testing.T) { | ||||
| 				ExpectGitArgs(s.expected, "", nil) | ||||
| 			instance := buildBranchCommands(commonDeps{runner: runner, userConfig: s.userConfig}) | ||||
|  | ||||
| 			assert.NoError(t, instance.Merge(s.branchName, s.opts)) | ||||
| 			assert.NoError(t, instance.Merge(s.branchName, s.variant)) | ||||
| 			runner.CheckForMissingCalls() | ||||
| 		}) | ||||
| 	} | ||||
|   | ||||
| @@ -97,6 +97,10 @@ func (self *ConfigCommands) GetRebaseUpdateRefs() bool { | ||||
| 	return self.gitConfig.GetBool("rebase.updateRefs") | ||||
| } | ||||
|  | ||||
| func (self *ConfigCommands) GetMergeFF() string { | ||||
| 	return self.gitConfig.Get("merge.ff") | ||||
| } | ||||
|  | ||||
| func (self *ConfigCommands) DropConfigCache() { | ||||
| 	self.gitConfig.DropCache() | ||||
| } | ||||
|   | ||||
| @@ -381,38 +381,103 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e | ||||
| 		return errors.New(self.c.Tr.CantMergeBranchIntoItself) | ||||
| 	} | ||||
|  | ||||
| 	wantFastForward, wantNonFastForward := self.fastForwardMergeUserPreference() | ||||
| 	canFastForward := self.c.Git().Branch.CanDoFastForwardMerge(refName) | ||||
|  | ||||
| 	var firstRegularMergeItem *types.MenuItem | ||||
| 	var secondRegularMergeItem *types.MenuItem | ||||
| 	var fastForwardMergeItem *types.MenuItem | ||||
|  | ||||
| 	if !wantNonFastForward && (wantFastForward || canFastForward) { | ||||
| 		firstRegularMergeItem = &types.MenuItem{ | ||||
| 			Label:   self.c.Tr.RegularMergeFastForward, | ||||
| 			OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_REGULAR), | ||||
| 			Key:     'm', | ||||
| 			Tooltip: utils.ResolvePlaceholderString( | ||||
| 				self.c.Tr.RegularMergeFastForwardTooltip, | ||||
| 				map[string]string{ | ||||
| 					"checkedOutBranch": checkedOutBranchName, | ||||
| 					"selectedBranch":   refName, | ||||
| 				}, | ||||
| 			), | ||||
| 		} | ||||
| 		fastForwardMergeItem = firstRegularMergeItem | ||||
|  | ||||
| 		secondRegularMergeItem = &types.MenuItem{ | ||||
| 			Label:   self.c.Tr.RegularMergeNonFastForward, | ||||
| 			OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_NON_FAST_FORWARD), | ||||
| 			Key:     'n', | ||||
| 			Tooltip: utils.ResolvePlaceholderString( | ||||
| 				self.c.Tr.RegularMergeNonFastForwardTooltip, | ||||
| 				map[string]string{ | ||||
| 					"checkedOutBranch": checkedOutBranchName, | ||||
| 					"selectedBranch":   refName, | ||||
| 				}, | ||||
| 			), | ||||
| 		} | ||||
| 	} else { | ||||
| 		firstRegularMergeItem = &types.MenuItem{ | ||||
| 			Label:   self.c.Tr.RegularMergeNonFastForward, | ||||
| 			OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_REGULAR), | ||||
| 			Key:     'm', | ||||
| 			Tooltip: utils.ResolvePlaceholderString( | ||||
| 				self.c.Tr.RegularMergeNonFastForwardTooltip, | ||||
| 				map[string]string{ | ||||
| 					"checkedOutBranch": checkedOutBranchName, | ||||
| 					"selectedBranch":   refName, | ||||
| 				}, | ||||
| 			), | ||||
| 		} | ||||
|  | ||||
| 		secondRegularMergeItem = &types.MenuItem{ | ||||
| 			Label:   self.c.Tr.RegularMergeFastForward, | ||||
| 			OnPress: self.RegularMerge(refName, git_commands.MERGE_VARIANT_FAST_FORWARD), | ||||
| 			Key:     'f', | ||||
| 			Tooltip: utils.ResolvePlaceholderString( | ||||
| 				self.c.Tr.RegularMergeFastForwardTooltip, | ||||
| 				map[string]string{ | ||||
| 					"checkedOutBranch": checkedOutBranchName, | ||||
| 					"selectedBranch":   refName, | ||||
| 				}, | ||||
| 			), | ||||
| 		} | ||||
| 		fastForwardMergeItem = secondRegularMergeItem | ||||
| 	} | ||||
|  | ||||
| 	if !canFastForward { | ||||
| 		fastForwardMergeItem.DisabledReason = &types.DisabledReason{ | ||||
| 			Text: utils.ResolvePlaceholderString( | ||||
| 				self.c.Tr.CannotFastForwardMerge, | ||||
| 				map[string]string{ | ||||
| 					"checkedOutBranch": checkedOutBranchName, | ||||
| 					"selectedBranch":   refName, | ||||
| 				}, | ||||
| 			), | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return self.c.Menu(types.CreateMenuOptions{ | ||||
| 		Title: self.c.Tr.Merge, | ||||
| 		Items: []*types.MenuItem{ | ||||
| 			firstRegularMergeItem, | ||||
| 			secondRegularMergeItem, | ||||
| 			{ | ||||
| 				Label:   self.c.Tr.RegularMerge, | ||||
| 				OnPress: self.RegularMerge(refName), | ||||
| 				Key:     'm', | ||||
| 				Tooltip: utils.ResolvePlaceholderString( | ||||
| 					self.c.Tr.RegularMergeTooltip, | ||||
| 					map[string]string{ | ||||
| 						"checkedOutBranch": checkedOutBranchName, | ||||
| 						"selectedBranch":   refName, | ||||
| 					}, | ||||
| 				), | ||||
| 			}, | ||||
| 			{ | ||||
| 				Label:   self.c.Tr.SquashMergeUncommittedTitle, | ||||
| 				Label:   self.c.Tr.SquashMergeUncommitted, | ||||
| 				OnPress: self.SquashMergeUncommitted(refName), | ||||
| 				Key:     's', | ||||
| 				Tooltip: utils.ResolvePlaceholderString( | ||||
| 					self.c.Tr.SquashMergeUncommitted, | ||||
| 					self.c.Tr.SquashMergeUncommittedTooltip, | ||||
| 					map[string]string{ | ||||
| 						"selectedBranch": refName, | ||||
| 					}, | ||||
| 				), | ||||
| 			}, | ||||
| 			{ | ||||
| 				Label:   self.c.Tr.SquashMergeCommittedTitle, | ||||
| 				Label:   self.c.Tr.SquashMergeCommitted, | ||||
| 				OnPress: self.SquashMergeCommitted(refName, checkedOutBranchName), | ||||
| 				Key:     'S', | ||||
| 				Tooltip: utils.ResolvePlaceholderString( | ||||
| 					self.c.Tr.SquashMergeCommitted, | ||||
| 					self.c.Tr.SquashMergeCommittedTooltip, | ||||
| 					map[string]string{ | ||||
| 						"checkedOutBranch": checkedOutBranchName, | ||||
| 						"selectedBranch":   refName, | ||||
| @@ -423,10 +488,10 @@ func (self *MergeAndRebaseHelper) MergeRefIntoCheckedOutBranch(refName string) e | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (self *MergeAndRebaseHelper) RegularMerge(refName string) func() error { | ||||
| func (self *MergeAndRebaseHelper) RegularMerge(refName string, variant git_commands.MergeVariant) func() error { | ||||
| 	return func() error { | ||||
| 		self.c.LogAction(self.c.Tr.Actions.Merge) | ||||
| 		err := self.c.Git().Branch.Merge(refName, git_commands.MergeOpts{}) | ||||
| 		err := self.c.Git().Branch.Merge(refName, variant) | ||||
| 		return self.CheckMergeOrRebase(err) | ||||
| 	} | ||||
| } | ||||
| @@ -434,7 +499,7 @@ func (self *MergeAndRebaseHelper) RegularMerge(refName string) func() error { | ||||
| func (self *MergeAndRebaseHelper) SquashMergeUncommitted(refName string) func() error { | ||||
| 	return func() error { | ||||
| 		self.c.LogAction(self.c.Tr.Actions.SquashMerge) | ||||
| 		err := self.c.Git().Branch.Merge(refName, git_commands.MergeOpts{Squash: true}) | ||||
| 		err := self.c.Git().Branch.Merge(refName, git_commands.MERGE_VARIANT_SQUASH) | ||||
| 		return self.CheckMergeOrRebase(err) | ||||
| 	} | ||||
| } | ||||
| @@ -442,7 +507,7 @@ func (self *MergeAndRebaseHelper) SquashMergeUncommitted(refName string) func() | ||||
| func (self *MergeAndRebaseHelper) SquashMergeCommitted(refName, checkedOutBranchName string) func() error { | ||||
| 	return func() error { | ||||
| 		self.c.LogAction(self.c.Tr.Actions.SquashMerge) | ||||
| 		err := self.c.Git().Branch.Merge(refName, git_commands.MergeOpts{Squash: true}) | ||||
| 		err := self.c.Git().Branch.Merge(refName, git_commands.MERGE_VARIANT_SQUASH) | ||||
| 		if err = self.CheckMergeOrRebase(err); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| @@ -459,6 +524,31 @@ func (self *MergeAndRebaseHelper) SquashMergeCommitted(refName, checkedOutBranch | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Returns wantsFastForward, wantsNonFastForward. These will never both be true, but they can both be false. | ||||
| func (self *MergeAndRebaseHelper) fastForwardMergeUserPreference() (bool, bool) { | ||||
| 	// Check user config first, because it takes precedence over git config | ||||
| 	mergingArgs := self.c.UserConfig().Git.Merging.Args | ||||
| 	if strings.Contains(mergingArgs, "--ff") { // also covers "--ff-only" | ||||
| 		return true, false | ||||
| 	} | ||||
|  | ||||
| 	if strings.Contains(mergingArgs, "--no-ff") { | ||||
| 		return false, true | ||||
| 	} | ||||
|  | ||||
| 	// Then check git config | ||||
| 	mergeFfConfig := self.c.Git().Config.GetMergeFF() | ||||
| 	if mergeFfConfig == "true" || mergeFfConfig == "only" { | ||||
| 		return true, false | ||||
| 	} | ||||
|  | ||||
| 	if mergeFfConfig == "false" { | ||||
| 		return false, true | ||||
| 	} | ||||
|  | ||||
| 	return false, false | ||||
| } | ||||
|  | ||||
| func (self *MergeAndRebaseHelper) ResetMarkedBaseCommit() error { | ||||
| 	self.c.Modes().MarkedBaseCommit.Reset() | ||||
| 	self.c.PostRefreshUpdate(self.c.Contexts().LocalCommits) | ||||
|   | ||||
| @@ -23,11 +23,6 @@ type TranslationSet struct { | ||||
| 	StagedChanges                         string | ||||
| 	StagingTitle                          string | ||||
| 	MergingTitle                          string | ||||
| 	SquashMergeUncommittedTitle           string | ||||
| 	SquashMergeCommittedTitle             string | ||||
| 	SquashMergeUncommitted                string | ||||
| 	SquashMergeCommitted                  string | ||||
| 	RegularMergeTooltip                   string | ||||
| 	NormalTitle                           string | ||||
| 	LogTitle                              string | ||||
| 	LogXOfYTitle                          string | ||||
| @@ -268,8 +263,16 @@ type TranslationSet struct { | ||||
| 	RefreshFiles                          string | ||||
| 	FocusMainView                         string | ||||
| 	Merge                                 string | ||||
| 	RegularMerge                          string | ||||
| 	MergeBranchTooltip                    string | ||||
| 	RegularMergeFastForward               string | ||||
| 	RegularMergeFastForwardTooltip        string | ||||
| 	CannotFastForwardMerge                string | ||||
| 	RegularMergeNonFastForward            string | ||||
| 	RegularMergeNonFastForwardTooltip     string | ||||
| 	SquashMergeUncommitted                string | ||||
| 	SquashMergeUncommittedTooltip         string | ||||
| 	SquashMergeCommitted                  string | ||||
| 	SquashMergeCommittedTooltip           string | ||||
| 	ConfirmQuit                           string | ||||
| 	SwitchRepo                            string | ||||
| 	AllBranchesLogGraph                   string | ||||
| @@ -1108,8 +1111,6 @@ func EnglishTranslationSet() *TranslationSet { | ||||
| 		EasterEgg:                            "Easter egg", | ||||
| 		UnstagedChanges:                      "Unstaged changes", | ||||
| 		StagedChanges:                        "Staged changes", | ||||
| 		SquashMergeUncommittedTitle:          "Squash merge and leave uncommitted", | ||||
| 		SquashMergeCommittedTitle:            "Squash merge and commit", | ||||
| 		StagingTitle:                         "Main panel (staging)", | ||||
| 		MergingTitle:                         "Main panel (merging)", | ||||
| 		NormalTitle:                          "Main panel (normal)", | ||||
| @@ -1352,8 +1353,16 @@ func EnglishTranslationSet() *TranslationSet { | ||||
| 		RefreshFiles:                         `Refresh files`, | ||||
| 		FocusMainView:                        "Focus main view", | ||||
| 		Merge:                                `Merge`, | ||||
| 		RegularMerge:                         "Regular merge", | ||||
| 		MergeBranchTooltip:                   "View options for merging the selected item into the current branch (regular merge, squash merge)", | ||||
| 		RegularMergeFastForward:              "Regular merge (fast-forward)", | ||||
| 		RegularMergeFastForwardTooltip:       "Fast-forward '{{.checkedOutBranch}}' to '{{.selectedBranch}}' without creating a merge commit.", | ||||
| 		CannotFastForwardMerge:               "Cannot fast-forward '{{.checkedOutBranch}}' to '{{.selectedBranch}}'", | ||||
| 		RegularMergeNonFastForward:           "Regular merge (with merge commit)", | ||||
| 		RegularMergeNonFastForwardTooltip:    "Merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}', creating a merge commit.", | ||||
| 		SquashMergeUncommitted:               "Squash merge and leave uncommitted", | ||||
| 		SquashMergeUncommittedTooltip:        "Squash merge '{{.selectedBranch}}' into the working tree.", | ||||
| 		SquashMergeCommitted:                 "Squash merge and commit", | ||||
| 		SquashMergeCommittedTooltip:          "Squash merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}' as a single commit.", | ||||
| 		ConfirmQuit:                          `Are you sure you want to quit?`, | ||||
| 		SwitchRepo:                           `Switch to a recent repo`, | ||||
| 		AllBranchesLogGraph:                  `Show/cycle all branch logs`, | ||||
| @@ -1438,9 +1447,6 @@ func EnglishTranslationSet() *TranslationSet { | ||||
| 		InteractiveRebaseTooltip:             "Begin an interactive rebase with a break at the start, so you can update the TODO commits before continuing.", | ||||
| 		RebaseOntoBaseBranchTooltip:          "Rebase the checked out branch onto its base branch (i.e. the closest main branch).", | ||||
| 		MustSelectTodoCommits:                "When rebasing, this action only works on a selection of TODO commits.", | ||||
| 		SquashMergeUncommitted:               "Squash merge '{{.selectedBranch}}' into the working tree.", | ||||
| 		SquashMergeCommitted:                 "Squash merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}' as a single commit.", | ||||
| 		RegularMergeTooltip:                  "Merge '{{.selectedBranch}}' into '{{.checkedOutBranch}}'.", | ||||
| 		FwdNoUpstream:                        "Cannot fast-forward a branch with no upstream", | ||||
| 		FwdNoLocalUpstream:                   "Cannot fast-forward a branch whose remote is not registered locally", | ||||
| 		FwdCommitsToPush:                     "Cannot fast-forward a branch with commits to push", | ||||
|   | ||||
							
								
								
									
										68
									
								
								pkg/integration/tests/branch/merge_fast_forward.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								pkg/integration/tests/branch/merge_fast_forward.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| package branch | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/config" | ||||
| 	. "github.com/jesseduffield/lazygit/pkg/integration/components" | ||||
| ) | ||||
|  | ||||
| var MergeFastForward = NewIntegrationTest(NewIntegrationTestArgs{ | ||||
| 	Description:  "Merge a branch into another using fast-forward merge", | ||||
| 	ExtraCmdArgs: []string{}, | ||||
| 	Skip:         false, | ||||
| 	SetupConfig: func(config *config.AppConfig) { | ||||
| 		config.GetUserConfig().Git.LocalBranchSortOrder = "alphabetical" | ||||
| 	}, | ||||
| 	SetupRepo: func(shell *Shell) { | ||||
| 		shell.NewBranch("original-branch"). | ||||
| 			EmptyCommit("one"). | ||||
| 			NewBranch("branch1"). | ||||
| 			EmptyCommit("branch1"). | ||||
| 			Checkout("original-branch"). | ||||
| 			NewBranchFrom("branch2", "original-branch"). | ||||
| 			EmptyCommit("branch2"). | ||||
| 			Checkout("original-branch") | ||||
| 	}, | ||||
| 	Run: func(t *TestDriver, keys config.KeybindingConfig) { | ||||
| 		t.Views().Branches(). | ||||
| 			Focus(). | ||||
| 			Lines( | ||||
| 				Contains("original-branch").IsSelected(), | ||||
| 				Contains("branch1"), | ||||
| 				Contains("branch2"), | ||||
| 			). | ||||
| 			SelectNextItem(). | ||||
| 			Press(keys.Branches.MergeIntoCurrentBranch) | ||||
|  | ||||
| 		t.ExpectPopup().Menu(). | ||||
| 			Title(Equals("Merge")). | ||||
| 			TopLines( | ||||
| 				Contains("Regular merge (fast-forward)"), | ||||
| 				Contains("Regular merge (with merge commit)"), | ||||
| 			). | ||||
| 			Select(Contains("Regular merge (fast-forward)")). | ||||
| 			Confirm() | ||||
|  | ||||
| 		t.Views().Commits(). | ||||
| 			Lines( | ||||
| 				Contains("branch1").IsSelected(), | ||||
| 				Contains("one"), | ||||
| 			) | ||||
|  | ||||
| 		// Check that branch2 can't be merged using fast-forward | ||||
| 		t.Views().Branches(). | ||||
| 			Focus(). | ||||
| 			NavigateToLine(Contains("branch2")). | ||||
| 			Press(keys.Branches.MergeIntoCurrentBranch) | ||||
|  | ||||
| 		t.ExpectPopup().Menu(). | ||||
| 			Title(Equals("Merge")). | ||||
| 			TopLines( | ||||
| 				Contains("Regular merge (with merge commit)"), | ||||
| 				Contains("Regular merge (fast-forward)"), | ||||
| 			). | ||||
| 			Select(Contains("Regular merge (fast-forward)")). | ||||
| 			Confirm() | ||||
|  | ||||
| 		t.ExpectToast(Contains("Cannot fast-forward 'original-branch' to 'branch2'")) | ||||
| 	}, | ||||
| }) | ||||
							
								
								
									
										76
									
								
								pkg/integration/tests/branch/merge_non_fast_forward.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								pkg/integration/tests/branch/merge_non_fast_forward.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| package branch | ||||
|  | ||||
| import ( | ||||
| 	"github.com/jesseduffield/lazygit/pkg/config" | ||||
| 	. "github.com/jesseduffield/lazygit/pkg/integration/components" | ||||
| ) | ||||
|  | ||||
| var MergeNonFastForward = NewIntegrationTest(NewIntegrationTestArgs{ | ||||
| 	Description:  "Merge a branch into another using non-fast-forward merge", | ||||
| 	ExtraCmdArgs: []string{}, | ||||
| 	Skip:         false, | ||||
| 	SetupConfig: func(config *config.AppConfig) { | ||||
| 		config.GetUserConfig().Git.LocalBranchSortOrder = "alphabetical" | ||||
| 	}, | ||||
| 	SetupRepo: func(shell *Shell) { | ||||
| 		shell.NewBranch("original-branch"). | ||||
| 			EmptyCommit("one"). | ||||
| 			NewBranch("branch1"). | ||||
| 			EmptyCommit("branch1"). | ||||
| 			Checkout("original-branch"). | ||||
| 			NewBranchFrom("branch2", "original-branch"). | ||||
| 			EmptyCommit("branch2"). | ||||
| 			Checkout("original-branch") | ||||
| 	}, | ||||
| 	Run: func(t *TestDriver, keys config.KeybindingConfig) { | ||||
| 		t.Views().Branches(). | ||||
| 			Focus(). | ||||
| 			Lines( | ||||
| 				Contains("original-branch").IsSelected(), | ||||
| 				Contains("branch1"), | ||||
| 				Contains("branch2"), | ||||
| 			). | ||||
| 			SelectNextItem(). | ||||
| 			Press(keys.Branches.MergeIntoCurrentBranch) | ||||
|  | ||||
| 		t.ExpectPopup().Menu(). | ||||
| 			Title(Equals("Merge")). | ||||
| 			TopLines( | ||||
| 				Contains("Regular merge (fast-forward)"), | ||||
| 				Contains("Regular merge (with merge commit)"), | ||||
| 			). | ||||
| 			Select(Contains("Regular merge (with merge commit)")). | ||||
| 			Confirm() | ||||
|  | ||||
| 		t.Views().Commits(). | ||||
| 			Lines( | ||||
| 				Contains("⏣─╮ Merge branch 'branch1' into original-branch").IsSelected(), | ||||
| 				Contains("│ ◯ * branch1"), | ||||
| 				Contains("◯─╯ one"), | ||||
| 			) | ||||
|  | ||||
| 		// Check that branch2 shows the non-fast-forward option first | ||||
| 		t.Views().Branches(). | ||||
| 			Focus(). | ||||
| 			NavigateToLine(Contains("branch2")). | ||||
| 			Press(keys.Branches.MergeIntoCurrentBranch) | ||||
|  | ||||
| 		t.ExpectPopup().Menu(). | ||||
| 			Title(Equals("Merge")). | ||||
| 			TopLines( | ||||
| 				Contains("Regular merge (with merge commit)"), | ||||
| 				Contains("Regular merge (fast-forward)"), | ||||
| 			). | ||||
| 			Select(Contains("Regular merge (with merge commit)")). | ||||
| 			Confirm() | ||||
|  | ||||
| 		t.Views().Commits(). | ||||
| 			Lines( | ||||
| 				Contains("⏣─╮ Merge branch 'branch2' into original-branch").IsSelected(), | ||||
| 				Contains("│ ◯ * branch2"), | ||||
| 				Contains("⏣─│─╮ Merge branch 'branch1' into original-branch"), | ||||
| 				Contains("│ │ ◯ * branch1"), | ||||
| 				Contains("◯─┴─╯ one"), | ||||
| 			) | ||||
| 	}, | ||||
| }) | ||||
| @@ -48,6 +48,8 @@ var tests = []*components.IntegrationTest{ | ||||
| 	branch.DeleteRemoteBranchWithDifferentName, | ||||
| 	branch.DeleteWhileFiltering, | ||||
| 	branch.DetachedHead, | ||||
| 	branch.MergeFastForward, | ||||
| 	branch.MergeNonFastForward, | ||||
| 	branch.MoveCommitsToNewBranchFromBaseBranch, | ||||
| 	branch.MoveCommitsToNewBranchFromMainBranch, | ||||
| 	branch.MoveCommitsToNewBranchKeepStacked, | ||||
|   | ||||
| @@ -103,7 +103,7 @@ var ModeSpecificKeybindingSuggestions = NewIntegrationTest(NewIntegrationTestArg | ||||
| 			Tap(func() { | ||||
| 				t.ExpectPopup().Menu(). | ||||
| 					Title(Equals("Merge")). | ||||
| 					Select(Contains("Regular merge")). | ||||
| 					Select(Contains("Regular merge (with merge commit)")). | ||||
| 					Confirm() | ||||
|  | ||||
| 				t.Common().AcknowledgeConflicts() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user