根据我最近关于“使用 GitPython 合并分支”的问题,我正在尝试对那里的解决方案进行单元测试。为此,我需要模拟用户打开合并工具,解决冲突并提交结果。 如果我使用 CLI 手动执行此操作,则一切似乎都有效:
echo 'Something to resolve the conflict' > conflicted_file.txt
git add conflicted_file.txt
git commit -m "Conflict resolved"
我尝试使用 GitPython 模拟相同的过程:
filename = 'conflicted_file.txt'
with open(filename, 'w') as f:
f.write('Some combined content which resolves the merge issue')
# repo is a git.Repo instance
repo.index.add([filename])
repo.index.commit("Simulating user resolving a conflict"+filename)
..但这对我来说只是一个例外:
Traceback (most recent call last):
File "C:\Projects\grit\test_Gritter.py", line 240, in test_MergeConflicts
repo.index.commit("Simulating user resolving a conflict"+filename)
File "C:\Python34\lib\site-packages\git\index\base.py", line 934, in commit
tree = self.write_tree()
File "C:\Python34\lib\site-packages\git\index\base.py", line 531, in write_tree
binsha, tree_items = write_tree_from_cache(entries, mdb, slice(0, len(entries)))
File "C:\Python34\lib\site-packages\git\index\fun.py", line 234, in write_tree_from_cache
raise UnmergedEntriesError(entry)
git.exc.UnmergedEntriesError: 100644 fd05580faebf11aee13944da595112eced471664 2 conflicted_file.txt
我还需要将其标记为已解决吗?编辑 - 关于我正在尝试自动化的更多背景知识
因此,为了清楚起见,我希望尽可能轻松地合并已更新到远程功能分支的远程主分支中的更改,从而解决任何合并冲突。
据我所知,正确的方法是:
创建本地功能分支
最终,这成功了:
g = git.Git('my/repo')
g.execute(["git","add","conflicted_file.txt"])
g.execute(["git","commit","-m", "Conflict resolved"])
这里,
workdir
是一些临时目录,
repository_url
是你的存储库URL,source_branch
是我们需要合并的地方,target_branch
是我们需要合并的地方,process_conflicts
是解决冲突的函数(例如,使用正确的内容更新冲突的文件)。注意,这个示例显式创建了合并提交(就像使用 --no-ff
一样),但当然可以根据需要更改为需要,只需在调用
no_ff=True
时删除 repo.heads[target_branch],
和 .merge()
即可。import os
import shutil
import git
from git.exc import GitCommandError
from git.remote import PushInfo, PushInfoList
# cleanup workdir
shutil.rmtree(workdir, ignore_errors=True)
os.makedirs(workdir, exist_ok=True)
# clone
repo: git.Repo = git.Repo.clone_from(
url=repository_url, to_path=workdir, branch=target_branch, filter="blob:none"
)
# warm up working trees
repo.git.checkout(source_branch)
repo.git.checkout(target_branch)
message = "My fancy merge commit message"
# merge
try:
repo.git.merge(
[repo.heads[target_branch], repo.heads[source_branch]], no_ff=True, m=message
)
except GitCommandError as ex:
if not "CONFLICT" in ex.stdout:
raise ex
conflicted_files = [
os.path.join(workdir, line.split(" ")[-1])
for line in ex.stdout.split("\n")
if line.startswith("CONFLICT")
]
# here we process conflict, e.g. write new contents of our files
process_conflicts(conflicted_files)
# now for the magic part
repo.index.reset()
repo.index.add([workdir])
repo.index.commit(
message=message,
parent_commits=[repo.heads[target_branch].commit, repo.heads[source_branch].commit] # type: ignore
)
# all set, push it
push_result: PushInfoList = repo.remote("origin").push(target_branch)
push_result.raise_if_error()