git-rebase显示已在后续提交中解决的冲突



我一直遇到这样的情况:我有一个PR,需要合并来自master的更改,并在新的提交中修复冲突(而不是重新基准,因为其他人可能在同一分支工作(,然后一旦PR获得批准,我就需要压缩并重新基准提交。

这是我想要压缩/重新基本的提交布局

commit1: Merge branch master into this branch. Manually resolved conflicts in foo/file0 
commit0: Added feature.

当在master上重新建立基础时,它会到达commit0并检测到存在冲突,即使这些冲突在commit1中得到了解决。注意

#git rebase -i master
pick commit0 Initial commit
Could not apply commit0... Initial commit
Auto-merging foo/file0
CONFLICT (content): Merge conflict in foo/file0

我试着接受我每次的改变,而不是来自大师的改变;尽管在这样做之后,我留下了已经在commit1中解决的冲突。

在不需要重复我在commit1中所做的手动冲突解决的情况下,压缩和重新设置提交的最佳方法是什么?

可以使用git rerere,也许正如eftshift0在评论中所建议的那样,但我认为先压缩,然后重新调整基础更简单。

根据你希望它有多花哨/聪明/可靠,它可能会变得有点棘手。我对此很好奇,于是写了下来。这是非常轻微的测试。不过,它可能没有你想象的那么有用。将其保存为名为git-squashbase的文件,位于$PATH中的某个位置,并在将master合并到当前功能中并解决冲突后运行git squashbase master

#! /bin/sh
OPTIONS_KEEPDASHDASH=
OPTIONS_STUCKLONG=
OPTIONS_SPEC="git squashbase [options] upstream
Using the given upstream, first squash this branch, then rebase it.
--
i,interactive  run git rebase --interactive instead of just making one commit
onto=          passed to final rebase
"
# parse options (defined above) and obtain "die" function etc.
. git-sh-setup
# Turn anything acceptable to git rev-parse into a commit hash ID.
# If it's not a commit hash ID, or unacceptable, quit now.
get_commit()
{
local hash=$(git rev-parse --verify "$1") || exit
hash=$(git rev-parse "${hash}^{commit}") || exit
echo $hash
}
interactive=false
onto=false; target=
while :; do
case "$1" in
--) shift; break;;
-i) interactive=true; shift;;
--onto) onto=true; target=$(get_commit "$2"); shift 2;;
esac
done
case $# in 1) ;; *) usage; esac
# Require a clean working tree and index.
require_clean_work_tree squashbase
# Everybody remember where we parked...
orig=$(git rev-parse HEAD) || exit
have_sym_orig=true
sym_orig=$(git symbolic-ref -q --short HEAD 2>/dev/null) || have_sym_orig=false
# ... and where we're going.
upstream_orig="$1"
upstream=$(get_commit "$1")
$onto || target=$upstream
# Return to where we started.
go_home()
{
if $have_sym_orig; then
git checkout -q $sym_orig || exit
else
git checkout -q $orig || exit
fi
}
# Bind original branch name to the current (HEAD) hash ID.
# Assumes index and working tree are in order.
set_home_here()
{
if $have_sym_orig; then
git checkout -q -B $sym_orig HEAD || exit
fi
}
# Set ORIG_HEAD to whatever HEAD had at the start of the whole
# thing.
set_orig_head()
{
git update-ref -m "squashbase" ORIG_HEAD $orig
}
# We have some series of commits:
#
#            A--B--C   <-- topic (HEAD)
#           /
#  ...--o--*--o--o--o   <-- upstream
#
# We must locate commit `*`, i.e., the commit that is the
# parent of commit A.  Commit A must be an ordinary commit,
# not a merge commit.  Also, we must forbid a case like this:
#
#              A-_
#             /   
#            /  B--C
#           /  /
#  ...--o--*--*--o--o   <-- upstream
#
# But we must *allow* this case:
#
#           A--B--C---M   <-- topic (HEAD)
#          /         /
#  ...--o--*--o--o--*   <-- upstream
#
# where the current commit is a merge with the upstream, and one
# of its parents *is* the upstream.
#
# I've attempted to do this with --boundary though I am not sure
# this catches every case.  It does work for simple cases, though.
set -- $(git rev-list --boundary $upstream..HEAD |
sed -n -e "/^-$upstream$/d" -e 's/^-//p')
case $# in
0) die "no boundary commits with $upstream_orig";;
1) ;;
*) die "multiple boundary commits with $upstream_orig";;
esac
# Detach HEAD now before we start fussing, so that branch name $sym_orig
# does not get extra reflog updates.  And, if something goes wrong after
# this point, use quit_early to attempt to put everything back.
quit_early()
{
go_home
exit 1
}
git checkout -q --detach || exit
# XXX this signal trapping is ugly and not well tested
trap : 1 2 3 15
if $interactive; then
git rebase -i $1 || quit_early
# At this point we have whatever commits the user left
# us.  We will assume that these are the "right" commits
# to rebase now.
else
git checkout -q --detach $1 || quit_early
# This merge *must* work, but --squash implies -n ...
git merge --squash $orig || quit_early
# ... so commit it now, with --edit
git commit --edit || quit_early
# At this point we have a single commit (from git merge --squash).
# This is the "right" commit to rebase now.
fi
# We always use a non-interactive rebase here.  Note that this
# step may fail, or stop in the middle, so we must first re-attach
# HEAD as appropriate.  This is a bit unfortunate as we'll get one
# extra reflog entry, but there's no way around that.
set_home_here
git rebase --onto $target $upstream || {
status=$?
echo "You will have to finish the rebase yourself now."
set_orig_head
exit $status
}
# The rebase worked.  We already re-attached HEAD so the only thing
# we really want to do now is override the ORIG_HEAD that rebase set:
# we want the one from before the squashbase operation.
set_orig_head
exit 0

最新更新