git rebase

本文详细介绍了Git的变基(rebase)操作在三种常见场景中的应用:开发分支与主干分支同步、本地分支落后于远程分支以及多条commit合并。通过实例演示,解释了rebase如何保持提交历史的整洁,并避免合并冲突。同时,提供了在多人协作环境中,如何使用`git pull --rebase`来避免混乱的merge提交记录。
摘要由CSDN通过智能技术生成

目录

一:开发分支落后于主干分支(个人修复用的分支落后于被修复分支)

模拟环境

开始rebase操作

二:本地分支落后于远程分支(多人共用一个分支的情况下其他人有提交)

在本地模拟环境:(熟悉的可以跳过,比较啰嗦)

详细步骤:

 开始模拟情景:

 解决:user2 push之前,用命令#git pull --rebase

三:多条commit合并


对rebase,三个常见的场景

rebase 它实际的操作原理,官方推荐:Git - 变基

我们可以使用git 命令的help:#git help rebase
git-rebase - 在另一个基础提示之上重新应用提交

原理:它的原理是首先找到这两个分支(即当前分支 topic、变基操作的目标基底分支 master)的最近共同祖 先 E,然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件,然后将当前分支指向目 标基底 E, 最后以此将之前另存为临时文件的修改依序应用 (从上诉官方推荐书籍摘取)


一:开发分支落后于主干分支(个人修复用的分支落后于被修复分支)

#git checkout branch_01
#git rebase master //将branch_01 变基到master最新

        从master分支拉出一个feat分支,用于开发新功能,开发完后准备将这个feat的修改合并到master,(如果使用的是gitlab作为代码审核的话,就是一个MR请求),但是master已经有其他开发者往上合入了其他的代码,甚至有可能和feat分支修改了同样的文件,所以,这个时候需要把master额外的修改也先合入到feat,对feat进行"变基",然后再提MR.  (当然,这种情况也可以不使用变基,而是把master的修改往当前feat分支的当前位置merge,不过这样log会难看,多出一条和feat分支修改无关的log)

模拟环境

  1. 首先为了确保安全,如果之前没有使用过rebase或者对这个场景不太熟悉,最好还是先在本地搞个环境模拟一遍。参考
    ubuntu 上搭建Gerrit_Canok-CSDN博客ubuntu20.04.03 64bit官网安装文档:Quickstart for Installing Gerrit on Linux1.0 Befor you start按照说明,需要先有Java SE Runtime Environment version 11 and up装个openjdk,官方:OpenJDK: Download and install#sudo apt-get installopenjdk-8-jre运行java -version 有提示:java ..https://blog.csdn.net/u012459903/article/details/122722997
  2. 或者简单起见,我们只用本地git仓库模拟rebase,不进行对远程仓库的push操作。

#mkdir git_test  && cd git_test //准备在这个目录里面模拟
#git init
#git add test_file_1
#git commit -m "xxxx"//然后建一个文件add commit上去。此时在默认的master分支做了一个提交.  这个时候从当前的comiit节点新建一个分支branch_01
#git checkout -b branch_01   //等同于 git branch branch_01 && git checkout branch_01 ,新建分支branch_01 并且checkout到这个branch_01

接下来,我们让branch_01和master同时往前,在给自的分支上都提交代码,即在branch_01里面改动test_file_1 然后commit,切会到master上也对test_file_1修改然后提交,这个时候是这样子的:
#git log --graph --oneline --all

rebase之后是这样子

 图解一下:

开始rebase操作

我们开发的分支branch_01已经和master分支有了分叉,这个时候我们想把master分支新的提交修改6b43598 都合入到我们的branch_01,但是我们不想用merge 把这个6b43598提交插入到ec3d8f3的后面。 现在开始变基操作:图解:

执行:
#git checkout branch_01
#git rebase master

上面即对branch_01进行了变基操作,他的分叉点由ce71a92变成了6b43598, 如其名”变基“
好处:我们在branch_01上做的修改提交: ec3d8f3 ....都不会被打乱,干净。
不过如果这个分叉后的修改 ec3d8f3 和6b43598如果改了相同的文件,rebase的时候就会有冲突,需要手动解决冲突,然后#git add XXX(冲突的文件) , #git rebase --continue

➜  git_test git:(branch_01) git rebase master
Auto-merging test_file_1
CONFLICT (content): Merge conflict in test_file_1
error: could not apply ec3d8f3... branch_01 commit
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply ec3d8f3... branch_01 commit
➜  git_test git:(6b43598) ✗ vi test_file_1
➜  git_test git:(6b43598) ✗ git add test_file_1
➜  git_test git:(6b43598) ✗ git rebase --continue

rebase完成之后,本地的仓库都已经做了修改,需要推送到远端,不过push的时候可能报错,rebase 之后基本要 -f 覆盖,  有风险,注意理解 Git - 变基icon-default.png?t=N7T8https://git-scm.com/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%8F%98%E5%9F%BA

二:本地分支落后于远程分支(多人共用一个分支的情况下其他人有提交)

        #git pull --rebase

        如果是多人在同一个分支上开发,我们修改了代码,并且add commit了,结果当需要push的时候,发现其他人已经推入了新的修改。

我们本地的分支是落后的,但是却在这个基础上有本地的commit,然后push失败:出现
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

这样,但是问题是已经commit了,我之前比较笨的做法是reset本地(恢复这个本地commit),stash存储,然后pull到最新,再赶紧stash pop出来,add+commit 将本地的commit基于最新的分支,非常繁琐风险还不小。
如果不reset回去再pull+commit, 而是已经commit了,这个时候执行了 git pull, 完蛋,这个时候的pull 命令里面会merge远程的修改,并且这个merge是在你当前的commit之后的基础上进行merge的。  你可以看到 git log,  会有一个merge的commit记录,再push上去也会把你这个记录也一并push上去,这就很混乱了。

所以,这个时候:执行 git pull —rebase

神奇之处是这个rebase参数会将这次的pull 里面的merge基础,是本地的上一次push的位置,这样咱们的commit就是在已经pull之后的新代码之后了。

在本地模拟环境:(熟悉的可以跳过,比较啰嗦)

参照:Git - 协议
我们这里使用 local 本地协议,分别在不同的目录clone出仓库,然后在本地仓库目录配置不同的用户名和邮箱(本地目录的配置会覆盖掉global的配置,这样可以实现不同的目录是不同的用户。这一点是我们模拟多用户的关键),模拟不同的用户提交。

详细步骤:

1. 为了方便新建一个空目录,下面在建三个子目录,一个是创建存储仓库用的,另两个是分别模拟两个用户从刚才的“远程”仓库clone自己的工作目录。
1.1 本地初始化个仓库,加个文件
#mkdir test_prj
#cd test_prj && git init
#vi init.txt //给这个文件随便写点啥
#git add init.txt 
#git commit -m "init a file"  //如果没有git config --global过,这里commit之前就需要配置用户信息,可以不加--global 选项,只让这个用户信息作用于本目录
1.2 新建个用户1的目录,去克隆刚才的“远程”仓库  。//克隆的时候就使用本地file协议,就不需要http ssh等等远程的协议去从远程服务器上克隆
#mkdir user1_dir && cd user1_dir
#git clone file:///home/can/work/local_git_learn/test_prj/.git ./

然后我们在这个user1_dir 目录下面给他配置本地仓库下的用户信息,user1

配置完用 #git config user.email  #git config user.name 就可以查看当前user信息,后续进行commit操作都会是以刚才的user信息作为记录。
2. 同样,再新建个用户2的目录,克隆一个仓库下来,然后配置用户二的信息

 环境搭建好了,现在可以去user1_dir 目录用 user1用户的身份进行comit, pull, push代码,去user2_dir 目录用user2用户的身份进行comit, pull, push代码。
整体目录情况如下:

 开始模拟情景:

用user1提交个修改user1_commit2,并且push到远程。
同时user2没有pull更新的情况下,已经在本地做了个 user2_commit1,并且已经commit准备push发现err,需要同步user1的user1_commit1, 所以可以用命令:#git pull --rebase
1. 去user1_dir 让用户1新拉个分支,并且push到远程。
    注意,如果直接在默认的master分支上修改,push, 会报错,如下:

所以先从master分支上新建成一个分支用来测试。
#cd user1_dir
#git checkout -b branch_01
#vi init.txt    
#git commit -a -m "user1_commit1"   //-a 的意思是省去了 commit之前 add的操作,会自动把已经跟踪的文件添加上去
#git push --set-upstream origin branch_01   //会将本地的分支推送关联到远程的 branch_01

 2. 去user2_dir用户目录,也切换到branch_01   (checkout branch_01的时候,已经把user1的commit1更新下来的。 所以还需要去user1那里再整一个新的提交 user1_commit2, 让user2不同步)
#cd ../user2_dir
#git pull
#git checkout banch_01

让用户2做个本地修改,commit,但是还没有push 
#vi init.txt
#git commit -a -m "user2 commit1"

 3. 回到user1_dir, 以用户1做个修改,并且push到远程仓库。这个时候如果user2才开始push,就会失败了,因为他的本地以及落后了,这个时候就可以用git pull --rebase ,将user2本地的修改变基到user1之后
#user1_dir:
#vi init.txt
#git commit -a -m "user1_commit2"
#git push

#user2_dir
#git push //因为用户user1 已经push推送更新了远程仓库,导致user2已经落后于远程分支,push失败

 
解决:user2 push之前,用命令#git pull --rebase

 其内部的操作细节,我这个中文版的Ubuntu详细输出了流程信息,如上图

首先,回退分支以便在上面重放您的工作...
应用:user2 commit1
使用索引来重建一个(三方合并的)基础目录树...
M    init.txt
回落到基础版本上打补丁及进行三方合并...
自动合并 init.txt
 

三:多条commit合并

Git - 重写历史icon-default.png?t=N7T8https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%87%8D%E5%86%99%E5%8E%86%E5%8F%B2

     从别的分支其它人的修改 cherry-pick修改过来,推送去之后显示author是其它人,想将author改为自己:
        git commit --amend --reset-author

  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值