如何在不合并或重定基的情况下解决分支上的git冲突

gev0vcfq  于 2022-12-10  发布在  Git
关注(0)|答案(4)|浏览(166)

我通常的特性/bug分支工作流程是这样的:

  • 分支
  • 进行更改
  • 重定基准到主图形
  • 推送和制作github拉取请求(PR)
  • 进行更多更改
  • 其他人检查代码并单击github合并按钮

让我们假设PR上的合并按钮不能被点击,因为我的功能分支现在与master有冲突。此时,我通常希望解决与master的冲突,并且我希望 * 在我的功能分支 * 上这样做,这样我就可以让正在审阅我的代码的人看到:
1.一个很好的比较,没有合并提交和/或来自master的随机更改
1.我的冲突解决方案
1.他们可以单击的合并按钮
但是,我可能不想重定基,因为代码审查已经在进行中了(有时我还是要重定基,但其他时候我想避免)。
我怎样才能可靠而有效地使用git来实现这一点呢?
我目前所做的是这些事情的混合:

  • “只是知道”什么需要挑选或改变来解决冲突(有时...)
  • 试合并(git merge master;(查看哪个文件有冲突,然后git注解以找到相关提交); git reset --hard origin/my-feature-branch; git cherry-pick <some commit>(或者可以手动进行一些更改);(重复))

有时候这样做不起作用,因为冲突是空的,所以我不知道该注解什么才能找到正确的提交。事实上,在空冲突的情况下,我认为不进行合并或重定基是不可能解决的(但在其他情况下是可能的--参见下面的示例)。
当它工作的时候,看起来git可能会以一种更自动化的方式帮助我做很多工作。
我也尝试过git-imerge--它似乎并不完全是为此目的而设计的,它也以一个未处理的异常退出。
下面是一个具体的工作示例,因为这里的答案中存在怀疑,即有时可能解决分支上的冲突,正如我在这里描述的那样,而不需要合并或重定基(注意,这里没有显示上面工作流的每一步,只演示了“解决分支上的冲突,而不需要合并或重定基”部分):

$ mkdir -p conflict-example/upstream
$ cd conflict-example/upstream
$ git init .
Initialised empty Git repository in /tmp/conflict-example/upstream/.git/
$ echo 'changed_only_upstream before' > changed_only_upstream
$ echo 'changed_only_downstream before' > changed_only_downstream
$ echo 'changed_in_both before' > changed_in_both
$ git add .
$ git commit -m 'initial'
[master (root-commit) 23040ea] initial
 3 files changed, 3 insertions(+)
 create mode 100644 changed_in_both
 create mode 100644 changed_only_downstream
 create mode 100644 changed_only_upstream
$ cd ..
$ git clone upstream downstream
Cloning into 'downstream'...
done.
$ cd downstream
$ git checkout -b downstream
Switched to a new branch 'downstream'
$ vim changed_in_both
$ vim changed_only_downstream
$ cat changed_in_both
changed_in_both before
downstream
$ cat changed_only_downstream
changed_only_downstream before
downstream
$ git commit -am 'downstream'
[downstream 6ead47f] downstream
 2 files changed, 2 insertions(+)
$ cd ../upstream
$ vim changed_in_both
$ vim changed_only_upstream
$ cat changed_in_both
changed_in_both before
upstream
$ cat changed_only_upstream
changed_only_upstream before
upstream
$ git commit -m 'upstream conflict' changed_in_both
[master e9ec7c5] upstream conflict
 1 file changed, 1 insertion(+)
$ git commit -m 'upstream non-conflict' changed_only_upstream
[master d4057e0] upstream non-conflict
 1 file changed, 1 insertion(+)
$ cd ../downstream/
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git pull
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 6 (delta 2), reused 0 (delta 0)
Unpacking objects: 100% (6/6), done.
From /tmp/conflict-example/upstream
   23040ea..d4057e0  master     -> origin/master
Updating 23040ea..d4057e0
Fast-forward
 changed_in_both       | 1 +
 changed_only_upstream | 1 +
 2 files changed, 2 insertions(+)
$ git checkout downstream
Switched to branch 'downstream'
$ git merge master
Auto-merging changed_in_both
CONFLICT (content): Merge conflict in changed_in_both
Recorded preimage for 'changed_in_both'
Automatic merge failed; fix conflicts and then commit the result.
$ git merge --abort
$ git log --all --graph --pretty=oneline --abbrev-commit --decorate
* d4057e0 (origin/master, origin/HEAD, master) upstream non-conflict
* e9ec7c5 upstream conflict
| * 6ead47f (HEAD -> downstream) downstream
|/
* 23040ea initial
$ git cherry-pick e9ec7c5
error: could not apply e9ec7c5... upstream conflict
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'
$ vim changed_in_both
$ cat changed_in_both
changed_in_both before
upstream
$ git add changed_in_both
$ git commit
Recorded resolution for 'changed_in_both'.
[downstream 7a4f7a7] upstream conflict
 Date: Sat Aug 27 14:41:13 2016 +0100
 1 file changed, 1 insertion(+), 1 deletion(-)
$ git log --all --graph --pretty=oneline --abbrev-commit --decorate
* 7a4f7a7 (HEAD -> downstream) upstream conflict
* 6ead47f downstream
| * d4057e0 (origin/master, origin/HEAD, master) upstream non-conflict
| * e9ec7c5 upstream conflict
|/
* 23040ea initial
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git merge downstream
Merge made by the 'recursive' strategy.
 changed_only_downstream | 1 +
 1 file changed, 1 insertion(+)
$ git log --all --graph --pretty=oneline --abbrev-commit --decorate
*   c036d60 (HEAD -> master) Merge branch 'downstream'
|\
| * 7a4f7a7 (downstream) upstream conflict
| * 6ead47f downstream
* | d4057e0 (origin/master, origin/HEAD) upstream non-conflict
* | e9ec7c5 upstream conflict
|/
* 23040ea initial

我相信,如果我在cherry-pick中选择了不同的分辨率,我将无法使用该合并命令进行合并(这至少与github merge按钮的功能类似)。在这些情况下,通常,我要么自己进行合并,要么进行重定基--但这不是这个问题的主题(不过,如果有某种方法可以实现合并按钮的可点击性,而不需要在这些情况下合并master或重新建立基础,那将是很有趣的!)

jmo0nnb3

jmo0nnb31#

下面是一个bash脚本来完成这项工作:

#!/bin/bash

declare -a conflicts

echo "Detecting conflicts..."
for rev in `git rev-list HEAD..master`
do
  git cherry-pick --no-commit $rev > /dev/null 2>&1
  if [ $? -eq 1 ]
  then
    conflicts+=($rev)
  fi
  git reset --hard HEAD > /dev/null
done

for rev in ${conflicts[*]}
do
  git cherry-pick --no-commit $rev > /dev/null 2>&1
  echo "Commit $rev cherry-picked."
  read -p "Resolve conflicts, then press any key to continue: "
done

echo "Done cherry-picking! Commit your changes now!"

运行此脚本,每次出现提示时,在文本编辑器中解决所有冲突,然后从另一个窗口执行git add。完成后,可以执行git commit(按照提示)。
到目前为止,在我的测试过程中,我发现了此脚本的两个问题:
1.当我将feature分支合并回master时,我会遇到一些小冲突。这些冲突比从master合并到feature分支要小得多。实际上,它们是这样的,你可以这样做:

git checkout master
git merge --no-ff feature/my-feature -x theirs

但是,这可能意味着GitHub的合并按钮不起作用,我不认为有办法告诉GitHub使用-x theirs
我不确定这是否仅仅取决于所做的相对更改,所以这可能只是由我的特定测试存储库引起的问题。
1.例如,如果你有一个依赖于aaa的提交bbb,两者都依赖于master,那么bbbcherry-pick将被检测为冲突。我的测试表明,你是否保留cherry-pick中的这样一个更改或丢弃它并不重要。(它似乎也不影响问题#1。
我正在寻找这两个问题的解决方案,但这应该足以让你开始。

w41d8nur

w41d8nur2#

冲突的出现仅仅是因为(master分支上的)更改包含在你的feature分支中。所以你不能解决feature分支上的冲突,除非从master分支引入这些更改。因为它们根本就不存在。
因此,即使有一个很好的方法来推动一个简单的补丁,让你得到一个很好的历史记录后,它甚至不工作的概念,因为你不能修复问题,还没有。
所以你实际上只有两种选择:合并或者重定基,来引入那些会导致冲突的改变到你的特性分支中。根据你的拉取请求的复杂性,一种方法可能比另一种更好,比如当你重定基的时候,历史记录更容易查看。而合并则保留历史记录(包括发生冲突的信息)。因此,请选择对您最有意义的内容,或者询问项目所有者您应该做什么。在大多数开源项目中,如果所有者自己不合并更改,他们通常希望您根据当前母版重新设置更改的基础。

pw136qt2

pw136qt23#

这里有一个可能的解决方案。它确实包括一个合并提交,但只有一个,那就是包含冲突解决方案的提交。主要问题是你将从master拉入不相关的更改。
应执行:

git checkout my-feature
git merge --no-ff master

而不是在那一点上寻找东西cherry-pick,只是解决任何冲突和提交。
这会使你的历史变得复杂,但是你会有一个解决冲突的提交,并且这个提交会在你的特性分支上。
然后,审阅者应该能够使用GitHub的一键合并按钮,而不会出现问题。
您的历史记录将类似于以下内容:

*---*---*---B [master]
 \       \ /
  *---*---A [feature]

其中A是您解决冲突的提交,B是GitHub创建的合并提交。

yvgpqqbh

yvgpqqbh4#

在我的例子中,我的fork中有两个分支,一个是特性分支,另一个是主分支。我在github repo页面上同步了fork主分支和原来的主分支,然后在我的主分支上做了一个git pull
这使得所有的修改都进入了主分支,并且与基础repo同步。然后在我的特性分支中,我从本地主分支进行了合并。在解决了所有的冲突后,我进行了一次提交,并推送到我拥有PR的特性分支远程。
下面是执行此操作的命令:(当前在分叉回购协议中)

git checkout main
git pull

git checkout feature-branch-with-PR
git merge main

[Resolve all the conflicts in VSCode]

git commit -am "Synced changes with remote"
git push

相关问题