git - git rebase 显示在后续提交中已经解决的冲突
问题描述
我一直遇到我有 PR 的情况,需要合并来自 master 的更改并在新提交中修复冲突(而不是 rebase,因为其他人可能在同一个分支上工作),然后一旦 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 中执行的手动冲突解决方案的情况下,压缩和 rebase 提交的最佳方法是什么?
解决方案
你可以使用git rerere
,也许,正如eftshift0 在评论中所建议的那样,但我认为先压缩然后变基更简单。
根据您希望它的花哨/聪明/可靠程度,它可能会有点棘手。我对此感到好奇并写了这篇文章。这是非常轻微的测试。不过,它可能没有您想象的那么有用。将它保存为一个名为 的文件git-squashbase
,在你的某个地方,$PATH
例如git squashbase master
,在将 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
推荐阅读
- javascript - 如何使用cheerio和express从instagram获取信息
- acumatica - 字段默认处理程序
- apache-spark - 获取数据框中每一列的最大列长度
- typescript - 上下文类型只是类型推断的另一个术语吗?
- php - 将字符串转换为带有键的数组
- database - 在 wordpress 中合并 2 个数据库
- javascript - 如何在 Jscodeshift 中正确导出 const
- elasticsearch - Elasticsearch 查询交集
- java - 未找到通过 JNDI 从不同的耳朵方法调用的本地 EJB
- python - 将可变长度字符串数组转换为数据帧