From e00f248cf72785429e2bafc4bcd7bf39528163a5 Mon Sep 17 00:00:00 2001
From: Ryooooooga <eial5q265e5@gmail.com>
Date: Tue, 27 Dec 2022 00:23:39 +0900
Subject: [PATCH] feat: support for `push --force-if-includes`

---
 pkg/commands/git_commands/deps_test.go |  6 +++++
 pkg/commands/git_commands/sync.go      |  6 ++++-
 pkg/commands/git_commands/sync_test.go | 32 +++++++++++++++++++++++++-
 3 files changed, 42 insertions(+), 2 deletions(-)

diff --git a/pkg/commands/git_commands/deps_test.go b/pkg/commands/git_commands/deps_test.go
index df2c96253..bcf36b168 100644
--- a/pkg/commands/git_commands/deps_test.go
+++ b/pkg/commands/git_commands/deps_test.go
@@ -15,6 +15,7 @@ import (
 type commonDeps struct {
 	runner     *oscommands.FakeCmdObjRunner
 	userConfig *config.UserConfig
+	gitVersion *GitVersion
 	gitConfig  *git_config.FakeGitConfig
 	getenv     func(string) string
 	removeFile func(string) error
@@ -48,6 +49,11 @@ func buildGitCommon(deps commonDeps) *GitCommon {
 		gitCommon.Common.UserConfig = config.GetDefaultConfig()
 	}
 
+	gitCommon.version = deps.gitVersion
+	if gitCommon.version == nil {
+		gitCommon.version = &GitVersion{2, 0, 0, ""}
+	}
+
 	gitConfig := deps.gitConfig
 	if gitConfig == nil {
 		gitConfig = git_config.NewFakeGitConfig(nil)
diff --git a/pkg/commands/git_commands/sync.go b/pkg/commands/git_commands/sync.go
index fb1aa9648..fc0d12459 100644
--- a/pkg/commands/git_commands/sync.go
+++ b/pkg/commands/git_commands/sync.go
@@ -29,7 +29,11 @@ func (self *SyncCommands) PushCmdObj(opts PushOpts) (oscommands.ICmdObj, error)
 	cmdStr := "git push"
 
 	if opts.Force {
-		cmdStr += " --force-with-lease"
+		if self.version.IsOlderThan(2, 30, 0) {
+			cmdStr += " --force-with-lease"
+		} else {
+			cmdStr += " --force-with-lease --force-if-includes"
+		}
 	}
 
 	if opts.SetUpstream {
diff --git a/pkg/commands/git_commands/sync_test.go b/pkg/commands/git_commands/sync_test.go
index 9c33381fe..f94117adc 100644
--- a/pkg/commands/git_commands/sync_test.go
+++ b/pkg/commands/git_commands/sync_test.go
@@ -10,6 +10,7 @@ import (
 func TestSyncPush(t *testing.T) {
 	type scenario struct {
 		testName string
+		version  *GitVersion
 		opts     PushOpts
 		test     func(oscommands.ICmdObj, error)
 	}
@@ -17,6 +18,7 @@ func TestSyncPush(t *testing.T) {
 	scenarios := []scenario{
 		{
 			testName: "Push with force disabled",
+			version:  &GitVersion{2, 29, 3, ""},
 			opts:     PushOpts{Force: false},
 			test: func(cmdObj oscommands.ICmdObj, err error) {
 				assert.Equal(t, cmdObj.ToString(), "git push")
@@ -25,14 +27,25 @@ func TestSyncPush(t *testing.T) {
 		},
 		{
 			testName: "Push with force enabled",
+			version:  &GitVersion{2, 29, 3, ""},
 			opts:     PushOpts{Force: true},
 			test: func(cmdObj oscommands.ICmdObj, err error) {
 				assert.Equal(t, cmdObj.ToString(), "git push --force-with-lease")
 				assert.NoError(t, err)
 			},
 		},
+		{
+			testName: "Push with force enabled (>= 2.30.0)",
+			version:  &GitVersion{2, 30, 0, ""},
+			opts:     PushOpts{Force: true},
+			test: func(cmdObj oscommands.ICmdObj, err error) {
+				assert.Equal(t, cmdObj.ToString(), "git push --force-with-lease --force-if-includes")
+				assert.NoError(t, err)
+			},
+		},
 		{
 			testName: "Push with force disabled, upstream supplied",
+			version:  &GitVersion{2, 29, 3, ""},
 			opts: PushOpts{
 				Force:          false,
 				UpstreamRemote: "origin",
@@ -45,6 +58,7 @@ func TestSyncPush(t *testing.T) {
 		},
 		{
 			testName: "Push with force disabled, setting upstream",
+			version:  &GitVersion{2, 29, 3, ""},
 			opts: PushOpts{
 				Force:          false,
 				UpstreamRemote: "origin",
@@ -58,6 +72,7 @@ func TestSyncPush(t *testing.T) {
 		},
 		{
 			testName: "Push with force enabled, setting upstream",
+			version:  &GitVersion{2, 29, 3, ""},
 			opts: PushOpts{
 				Force:          true,
 				UpstreamRemote: "origin",
@@ -69,8 +84,23 @@ func TestSyncPush(t *testing.T) {
 				assert.NoError(t, err)
 			},
 		},
+		{
+			testName: "Push with force enabled, setting upstream (>= 2.30.0)",
+			version:  &GitVersion{2, 30, 0, ""},
+			opts: PushOpts{
+				Force:          true,
+				UpstreamRemote: "origin",
+				UpstreamBranch: "master",
+				SetUpstream:    true,
+			},
+			test: func(cmdObj oscommands.ICmdObj, err error) {
+				assert.Equal(t, cmdObj.ToString(), `git push --force-with-lease --force-if-includes --set-upstream "origin" "master"`)
+				assert.NoError(t, err)
+			},
+		},
 		{
 			testName: "Push with remote branch but no origin",
+			version:  &GitVersion{2, 29, 3, ""},
 			opts: PushOpts{
 				Force:          true,
 				UpstreamRemote: "",
@@ -87,7 +117,7 @@ func TestSyncPush(t *testing.T) {
 	for _, s := range scenarios {
 		s := s
 		t.Run(s.testName, func(t *testing.T) {
-			instance := buildSyncCommands(commonDeps{})
+			instance := buildSyncCommands(commonDeps{gitVersion: s.version})
 			s.test(instance.PushCmdObj(s.opts))
 		})
 	}