我试图实现一个方法与pygit2
,删除一个给定的提交。
git rebase --onto SHA^ SHA
但是库中没有这样的方法。
根据pygit2文档,merge_trees
可以用于此目的。到目前为止,我得到了:
def deleteCommit(repo, commitId):
delete_commit = repo[commitId]
head = repo.head
base = repo.merge_base(delete_commit.id, head.target)
delete_commit_tree = repo.get(delete_commit).tree
head_tree = repo.get(head.target).tree
base_tree = repo.get(base).tree
index = repo.merge_trees(base_tree, head_tree, delete_commit_tree)
tree_id = index.write_tree(repo)
从我在文档中查找的内容来看,这可能是最直接的方式,除了做一些我无法真正理解的repo.walk()
东西。
我使用repo.walk()
提出了这个新的部分解决方案:
def deleteCommit(repo, commitId):
# Get the branch to be rewritten
branch = repo.lookup_branch(repo.head.shorthand)
branch_ref = repo.lookup_reference(branch.name)
# Get the commit history up to the branch tip
commits = list(repo.walk(branch.target, GIT_SORT_TOPOLOGICAL))
# Create a new tree with the desired changes
tree = repo.TreeBuilder().write()
for commit in commits:
if commitId != str(commit.id):
commitOid = git.Oid(hex=commitId)
if commitOid in commit.parent_ids:
commit.parent_ids.remove(commitOid)
repo.create_commit(
branch_ref.name, # no reflog message
commit.author, # use the default signature
commit.committer, # use the default signature
commit.message, # commit message
tree, # use the new tree
commit.parent_ids,
)
else:
repo.references.delete(f'refs/commits/{commit.hex}')
# Point the branch to the new commit
branch_ref.set_target(tree)
但是这会产生错误pygit2.GitError: failed to create commit: current tip is not the first parent
我找到了一个将repo.walk()
与cherry-pick
和merge_commits
结合起来的解决方案:
def removeCommitFromHistory(repo, commit_id):
repo.reset(repo.head.target, GIT_RESET_HARD)
previous_branch_ref = repo.branches[repo.head.shorthand]
previous_branch_shorthand = previous_branch_ref.shorthand
old_branch_name = f"remove_commit_{commit_id}"
previous_branch_ref.rename(old_branch_name)
old_branch_iter = repo.walk(
previous_branch_ref.target, GIT_SORT_REVERSE).__iter__()
initial_commit = next(old_branch_iter)
new_branch_ref = repo.branches.local.create(
previous_branch_shorthand, initial_commit)
repo.checkout(new_branch_ref)
remove_commit_parents = None
for commit in old_branch_iter:
if commit.hex == commit_id:
remove_commit_parents = commit.parent_ids[:]
continue
index = repo.index
if remove_commit_parents:
index = repo.merge_commits(
commit.id, remove_commit_parents[0], favor="ours")
else:
repo.cherrypick(commit.id)
parents = remove_commit_parents if str(
commit.parent_ids[0]) == commit_id else [repo.head.target]
tree_id = index.write_tree()
repo.create_commit(repo.head.name, commit.author, commit.committer,
commit.message, tree_id, parents)
repo.state_cleanup()
repo.branches[old_branch_name].delete()
基本上,我正在重新创建整个历史,逐个提交,使用cherry-pick
和使用merge_commits
将我想要删除的提交与其父提交合并,保留父提交。