Skip to content

Commit da0bb9a

Browse files
committed
Fix (modify/delete) conflicts in GitAlg.mergeTheirs
`merge --strategy-option=theirs` can fail if a file has been modified on `HEAD` but deleted on the other branch. Git reports these conflicts as `CONFLICT (modify/delete)`. This PR resolves them by just removing all unmerged files which should be the same files that were deleted on the other branch. Closes #1393.
1 parent 26dab29 commit da0bb9a

File tree

2 files changed

+14
-10
lines changed

2 files changed

+14
-10
lines changed

modules/core/src/main/scala/org/scalasteward/core/git/GitAlg.scala

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,19 @@ object GitAlg {
163163
for {
164164
before <- latestSha1(repo, Branch.head)
165165
repoDir <- workspaceAlg.repoDir(repo)
166-
sign = if (config.signCommits) List("--gpg-sign") else List.empty[String]
166+
sign = if (config.signCommits) List("--gpg-sign") else List.empty
167167
_ <- exec(Nel.of("merge", "--strategy-option=theirs") ++ (sign :+ branch.name), repoDir)
168+
.handleErrorWith { throwable =>
169+
// Resolve CONFLICT (modify/delete) by deleting unmerged files:
170+
for {
171+
unmergedFiles <- exec(Nel.of("diff", "--name-only", "--diff-filter=U"), repoDir)
172+
_ <- Nel.fromList(unmergedFiles.filter(_.nonEmpty)) match {
173+
case Some(files) => files.traverse(file => exec(Nel.of("rm", file), repoDir))
174+
case None => F.raiseError(throwable)
175+
}
176+
_ <- commitAll(repo, "Remove unmerged files")
177+
} yield List.empty
178+
}
168179
after <- latestSha1(repo, Branch.head)
169180
} yield Option.when(before =!= after)(Commit())
170181

modules/core/src/test/scala/org/scalasteward/core/git/GitAlgTest.scala

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ class GitAlgTest extends AnyFunSuite with Matchers {
125125
p.unsafeRunSync() shouldBe ((true, false, false, true))
126126
}
127127

128-
test("mergeTheirs2") {
128+
test("mergeTheirs: CONFLICT (modify/delete)") {
129129
val repo = Repo("merge", "theirs2")
130130
val p = for {
131131
repoDir <- workspaceAlg.repoDir(repo)
@@ -171,20 +171,13 @@ object GitAlgTest {
171171
_ <- processAlg.exec(Nel.of("git", "add", "file2"), repoDir)
172172
_ <- processAlg.exec(Nel.of("git", "commit", "-m", "Modify file2 on conflicts-yes"), repoDir)
173173
_ <- processAlg.exec(Nel.of("git", "checkout", "master"), repoDir)
174-
// work on conflicts-yes (file removed on base branch)
175-
_ <- processAlg.exec(Nel.of("git", "checkout", "-b", "conflicts-yes-file-removed"), repoDir)
176-
_ <- fileAlg.writeFile(repoDir / "file2", "file2, line1\nfile2, line2 on conflicts-yes")
177-
_ <- processAlg.exec(Nel.of("git", "add", "file2"), repoDir)
178-
_ <- processAlg.exec(Nel.of("git", "commit", "-m", "Modify file2 on conflicts-yes"), repoDir)
179-
_ <- processAlg.exec(Nel.of("git", "checkout", "master"), repoDir)
180174
// work on master
181175
_ <- fileAlg.writeFile(repoDir / "file2", "file2, line1\nfile2, line2 on master")
182176
_ <- processAlg.exec(Nel.of("git", "add", "file2"), repoDir)
183177
_ <- processAlg.exec(Nel.of("git", "commit", "-m", "Modify file2 on master"), repoDir)
184178
} yield ()
185179

186-
def createGitRepoWithConflictFileRemovedOnMaster[F[_]](repoDir: File)(
187-
implicit
180+
def createGitRepoWithConflictFileRemovedOnMaster[F[_]](repoDir: File)(implicit
188181
fileAlg: FileAlg[F],
189182
processAlg: ProcessAlg[F],
190183
F: Monad[F]

0 commit comments

Comments
 (0)