变基的风险以及如何用变基解决变基

转载请标明出处:http://blog.csdn.net/xx326664162/article/details/50724116 文章出自:薛瑄的博客

你也可以查看我的其他同类文章,也会让你有一定的收货!

奇妙的变基也并非完美无缺,要用它得遵守一条准则:

不要对在你的仓库外有副本的分支执行变基。

如果你遵循这条金科玉律,就不会出差错。 否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。

变基操作的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。

如果你已经将提交推送至某个仓库,而其他人也已经从该仓库拉取提交并进行了后续工作,此时,如果你用 git rebase 命令重新整理了提交并再次推送,你的同伴因此将不得不再次将他们手头的工作与你的提交进行整合,如果接下来你还要拉取并整合他们修改过的提交,事情就会变得一团糟。

示例:

让我们来看一个在公开的仓库上执行变基操作所带来的问题。 假设你从一个中央服务器克隆然后在它的基础上进行了一些开发。 你的提交历史如图一所示:

图一
图一

示例仓库中只有一个文件:README.md

1、team1 本地路径:/f/Git/one/rebase2

he@he-PC MINGW64 /f/Git/one
$ git clone https://git.oschina.net/xuexuan/rebase2.git
Cloning into 'rebase2'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
Checking connectivity... done.

2、My Computer 本地路径:/f/Git/two

he@he-PC MINGW64 /f/Git/two
$ git clone https://git.oschina.net/xuexuan/rebase2.git
Cloning into 'rebase2'...
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
Checking connectivity... done.

3、team1 本地路径:/f/Git/one/rebase2

改变README.md为:

#rebase2
c1

4、执行git commit命令

he@he-PC MINGW64 /f/Git/one/rebase2 (master)
$ git commit -am "c1"
[master c95de34] c1
 1 file changed, 1 insertion(+)

5、推送更改到远程仓库,执行git push

he@he-PC MINGW64 /f/Git/one/rebase2 (master)
$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 244 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://git.oschina.net/xuexuan/rebase2.git
   15df2ce..c95de34  master -> master

6、My Computer 本地路径:/f/Git/two

执行命令:git pull

he@he-PC MINGW64 /f/Git/two/rebase2 (master)
$ git pull origin master
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://git.oschina.net/xuexuan/rebase2
 * branch            master     -> FETCH_HEAD
   15df2ce..c95de34  master     -> origin/master
Updating 15df2ce..c95de34
Fast-forward
 README.md | 1 +
 1 file changed, 1 insertion(+)

7、修改文件README.md为

#rebase
c2
c3

8、提交更改到master分支

he@he-PC MINGW64 /f/Git/two/rebase2 (master)
$ git commit -am "c2 c3"
[master 41230f3] c2 c3
 1 file changed, 2 insertions(+)

然后,某人又向中央服务器提交了一些修改,其中还包括一次合并。 你抓取了这些在远程分支上的修改,并将其合并到你本地的开发分支,然后你的提交历史就会变成这样:

图2

图2

9、team1 本地路径:/f/Git/one/rebase2

修改文件README.md为:

#rebase
c1
c4

10、提交更改到本地,执行命令git commit

he@he-PC MINGW64 /f/Git/one/rebase2 (master)
$ git commit -am "c4"
[master 9904817] c4
 1 file changed, 1 insertion(+)

11、在c1提交上检出新的分支test

he@he-PC MINGW64 /f/Git/one/rebase2 (master)
$ git log
commit 9904817413affcf53f32f7b523c7b8f71d54d977
Author: xuexuan <384324069@qq.com>
Date:   Tue Feb 23 15:23:19 2016 +0800

    c4

commit c95de3454624f5acb863855bbe4c8c0761315264
Author: xuexuan <384324069@qq.com>
Date:   Tue Feb 23 15:06:29 2016 +0800

    c1

commit 15df2cebbdae72f6c9623e74b8af4c48ebacd2df
Author: xuexuan <326664162@qq.com>
Date:   Tue Feb 23 14:36:19 2016 +0800

    Initial commit



he@he-PC MINGW64 /f/Git/one/rebase2 (master)
$ git checkout -b test c95de34
Switched to a new branch 'test'

12、修改文件README.md为:

#rebase
c1
c5

13、提交更改到分支test

he@he-PC MINGW64 /f/Git/one/rebase2 (test)
$ git commit -am "c5"
[test 085a8b0] c5
 1 file changed, 1 insertion(+)

14、切换分支到master

he@he-PC MINGW64 /f/Git/one/rebase2 (test)
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

15、合并分支test到分支master,执行命令 git merge

he@he-PC MINGW64 /f/Git/one/rebase2 (master)
$ git merge test --no-ff
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

第15步产生冲突,需要解决冲突

16、修改文件README.md为:

#rebase
c1
c4
c5

17、提交更改到暂存区git add .,提交到版本库git commit

下面共有4条命令语句,只需要执行2、4步即可,1、3步是我在实验中的错误过程


he@he-PC MINGW64 /f/Git/one/rebase2 (master|MERGING)
$ git merge test --no-ff
error: merge is not possible because you have unmerged files.
hint: Fix them up in the work tree, and then use 'git add/rm <file>'
hint: as appropriate to mark resolution and make a commit.
fatal: Exiting because of an unresolved conflict.


he@he-PC MINGW64 /f/Git/one/rebase2 (master|MERGING)
$ git add .

he@he-PC MINGW64 /f/Git/one/rebase2 (master|MERGING)
$ git merge test --no-ff
fatal: You have not concluded your merge (MERGE_HEAD exists).
Please, commit your changes before you merge.


he@he-PC MINGW64 /f/Git/one/rebase2 (master|MERGING)
$ git commit -m "merge test and master"
[master b816b40] merge test and master

18、提交更改到远程仓库

he@he-PC MINGW64 /f/Git/one/rebase2 (master)
$ git push origin master
Counting objects: 9, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (9/9), 657 bytes | 0 bytes/s, done.
Total 9 (delta 1), reused 0 (delta 0)
To https://git.oschina.net/xuexuan/rebase2.git
   c95de34..b816b40  master -> master

19、My Computer 本地路径:/f/Git/two

拉取远程仓库的更新,执行命令git fetch,此时远程仓库更新的内容,是刚刚team1 本地路径:/f/Git/one/rebase2的提交

he@he-PC MINGW64 /f/Git/two/rebase2 (master)
$ git fetch origin master
remote: Counting objects: 9, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 9 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (9/9), done.
From https://git.oschina.net/xuexuan/rebase2
 * branch            master     -> FETCH_HEAD
   c95de34..b816b40  master     -> origin/master

20、抓取别人的提交,合并到自己的开发分支

he@he-PC MINGW64 /f/Git/two/rebase2 (master)
$ git merge origin/master --no-ff
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

20步,合并过程中,有冲突,需要解决冲突

21、修改文件README.md为:

#rebase
c1
c2
c3
c4
c5

22、冲突解决后,需要提交到暂存区并提交到版本库

he@he-PC MINGW64 /f/Git/two/rebase2 (master|MERGING)
$ git commit -am "merge c7"
[master 6394dc8] merge c7

接下来,team1又决定把合并操作回滚,改用变基;继而又用 git push –force 命令覆盖了服务器上的提交历史。 之后你从服务器抓取更新,会发现多出来一些新的提交。

这里写图片描述
图3

23、team1 本地路径:/f/Git/one/rebase2,撤销c6的提交

he@he-PC MINGW64 /f/Git/one/rebase2 (master)
$ git reset --hard HEAD^
HEAD is now at 9904817 c4

24、将分支master(c4提交)变基到分支test(c5提交),执行命令git checkout,git rebase


he@he-PC MINGW64 /f/Git/one/rebase2 (master)
$ git rebase test
First, rewinding head to replay your work on top of it...
Applying: c4
Using index info to reconstruct a base tree...
M       README.md
Falling back to patching base and 3-way merge...
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
error: Failed to merge in the changes.
Patch failed at 0001 c4
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".

24步有冲突,需要解决冲突

25、修改文件README.md为:

#rebase
c1
c5
c4

26、冲突解决后,执行git add . 和 git rebase - -continuem命令

he@he-PC MINGW64 /f/Git/one/rebase2 (test|REBASE 1/1)
$ git rebase --continue
README.md: needs merge
You must edit all merge conflicts and then
mark them as resolved using git add

he@he-PC MINGW64 /f/Git/one/rebase2 (master|REBASE 1/1)
$ git add .

he@he-PC MINGW64 /f/Git/one/rebase2 (master|REBASE 1/1)
$ git status
rebase in progress; onto 085a8b0
You are currently rebasing branch 'master' on '085a8b0'.
  (all conflicts fixed: run "git rebase --continue")

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   README.md


he@he-PC MINGW64 /f/Git/one/rebase2 (master|REBASE 1/1)
$ git rebase --continue
Applying: c4

he@he-PC MINGW64 /f/Git/one/rebase2 (master)
$

变基成功后,分支master变基到分支test上后,如果再在分支master上执行,git reset - - hard HEAD^ 撤回上次提交,则会在现在的基础上回滚,即和test在同一位置。

27、使用了命令git push –force,强制改变远程库的分支信息

he@he-PC MINGW64 /f/Git/one/rebase2 (master)
$ git push --force
warning: push.default is unset; its implicit value has changed in
Git 2.0 from 'matching' to 'simple'. To squelch this message
and maintain the traditional behavior, use:

  git config --global push.default matching

To squelch this message and adopt the new behavior now, use:

  git config --global push.default simple

When push.default is set to 'matching', git will push local branches
to the remote branches that already exist with the same name.

Since Git 2.0, Git defaults to the more conservative 'simple'
behavior, which only pushes the current branch to the corresponding
remote branch that 'git pull' uses to update the current branch.

See 'git help config' and search for 'push.default' for further information.
(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode
'current' instead of 'simple' if you sometimes use older versions of Git)

Counting objects: 3, done.
Writing objects: 100% (3/3), 255 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://git.oschina.net/xuexuan/rebase2.git
 + b816b40...b505742 master -> master (forced update)

图4

图4

28、My Computer 本地路径:/f/Git/two
拉取远程仓库的分支信息,需要解决一个冲突

he@he-PC MINGW64 /f/Git/two/rebase2 (master)
$ git pull
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://git.oschina.net/xuexuan/rebase2
 + b816b40...b505742 master     -> origin/master  (forced update)
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

29、修改文件README.md为:

#rebase
c1
c2
c3
c5
c4

30、解决冲突后,执行命令 git commit -am,完成合并

he@he-PC MINGW64 /f/Git/two/rebase2 (master|MERGING)
$ git status
On branch master
Your branch and 'origin/master' have diverged,
and have 4 and 1 different commit each, respectively.
  (use "git pull" to merge the remote branch into yours)
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add <file>..." to mark resolution)

        both modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")

he@he-PC MINGW64 /f/Git/two/rebase2 (master|MERGING)
$ git commit -am "git pull and slove conflict"
[master 2b13599] git pull and slove conflict

he@he-PC MINGW64 /f/Git/two/rebase2 (master)
$

此时如果你执行 git log 命令,你会发现有两个提交的作者、日期、日志居然是一样的,这会令人感到混乱。 此外,如果你将这一堆又推送到服务器上,你实际上是将那些已经被变基抛弃的提交又找了回来,这会令人感到更加混乱。 很明显对方并不想在提交历史中看到 C4 和 C6,因为之前就是他们把这两个提交通过变基丢弃的。

用变基解决变基

如果你 真的 遭遇了类似的处境,Git 还有一些高级魔法可以帮到你。 如果团队中的某人强制推送并覆盖了一些你所基于的提交,你需要做的就是检查你做了哪些修改,以及他们覆盖了哪些修改。

实际上,Git 除了对整个提交计算 SHA-1 校验和以外,也对本次提交所引入的修改计算了校验和—— 即 “patch-id”。

如果你拉取被覆盖过的更新并将你手头的工作基于此进行变基的话,一般情况下 Git 都能成功分辨出哪些是你的修改,并把它们应用到新分支上。

举个例子

如果遇到前面提到的 图 3 那种情境,如果我们不是执行合并,而是执行 git rebase teamone/master, Git 将会:

  • 检查哪些提交是我们的分支上独有的(C2,C3,C4,C6,C7)

  • 检查其中哪些提交不是合并操作的结果(C2,C3,C4)

  • 检查哪些提交在对方覆盖更新时并没有被纳入目标分支(只有 C2 和 C3,因为 C4 其实就是 C4’)

  • 把查到的这些提交应用在 teamone/master 上面

从而我们将得到与 图4 中不同的结果,如图5所示。

图5

图5

31、My Computer 本地路径:/f/Git/two执行撤销,回退到图3的状态

he@he-PC MINGW64 /f/Git/two/rebase2 (master)
$ git reset --hard HEAD^
HEAD is now at 6394dc8 merge c7

32、使用命令git rebase origin/master,用变基解决变基,解决2个冲突,完成变基,最后结果如图5所示。

he@he-PC MINGW64 /f/Git/two/rebase2 (master)
$ git rebase origin/master
First, rewinding head to replay your work on top of it...
Applying: c2 c3
Using index info to reconstruct a base tree...
M       README.md
.git/rebase-apply/patch:8: trailing whitespace.
c2
.git/rebase-apply/patch:9: trailing whitespace.
c3
warning: 2 lines add whitespace errors.
Falling back to patching base and 3-way merge...
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
error: Failed to merge in the changes.
Patch failed at 0001 c2 c3
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".


he@he-PC MINGW64 /f/Git/two/rebase2 (master|REBASE 1/2)
$ git add .

he@he-PC MINGW64 /f/Git/two/rebase2 (master|REBASE 1/2)
$ git rebase --continue
Applying: c2 c3
Applying: c4
Using index info to reconstruct a base tree...
M       README.md
Falling back to patching base and 3-way merge...
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
error: Failed to merge in the changes.
Patch failed at 0002 c4
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".


he@he-PC MINGW64 /f/Git/two/rebase2 (master|REBASE 2/2)
$ git add .

he@he-PC MINGW64 /f/Git/two/rebase2 (master|REBASE 2/2)
$ git rebase --continue
Applying: c4

要想上述方案有效,还需要对方在变基时确保 C4’ 和 C4 是几乎一样的。 否则变基操作将无法识别,并新建另一个类似 C4 的补丁(而这个补丁很可能无法整洁的整合入历史,因为补丁中的修改已经存在于某个地方了)。

在本例中另一种简单的方法是使用 git pull –rebase 命令而不是直接 git pull。 又或者你可以自己手动完成这个过程,先 git fetch,再 git rebase teamone/master。

如果你习惯使用 git pull ,同时又希望默认使用选项 –rebase,你可以执行这条语句 git config –global pull.rebase true 来更改 pull.rebase 的默认配置。

总结:

只要你把变基命令当作是在推送前清理提交使之整洁的工具,并且只在从未推送至共用仓库的提交上执行变基命令,你就不会有事。 假如你在那些已经被推送至共用仓库的提交上执行变基命令,并因此丢弃了一些别人的开发所基于的提交,那你就有大麻烦了,你的同事也会因此鄙视你。

如果你或你的同事在某些情形下决意要这么做,请一定要通知每个人执行 git pull –rebase 命令,这样尽管不能避免伤痛,但能有所缓解。

关注我的公众号,轻松了解和学习更多技术
这里写图片描述

©️2020 CSDN 皮肤主题: 撸撸猫 设计师:设计师小姐姐 返回首页