Git分支管理

本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2021-02-26

创建与合并分支

以下四步及动图解释创建与合并分支的流程:

1、master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点

2、当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上

3、在dev上新提交一次后,dev指针往前移动一步,而master指针不变

4、切换到master分支,将dev分支合并到master分支

创建与合并分支

操作

# 查看分支
git branch 

# 创建分支
git branch <name>

# 切换分支
git checkout <name> 

# 创建并切换分支
git checkout -b <name> 

# 根据远程分支创建本地分支
git checkout -b dev origin/dev

# 将本地dev分支推送到origin并创建origin/dev分支 (远程没有dev分支)
git push origin dev:dev 

# 合并某分支到当前分支
git merge <name> 

# 删除分支
git branch -d <name> 

切换分支的命令checkout与撤销修改的命令过于接近,所以更建议习惯使用switch来切换分支。

当使用git merge时合并分支时,会看到合并方式的信息,其中有一种叫Fast-forward(快进模式),就是我们上图中描述的合并,直接把master指向dev的当前提交,所以合并速度非常快。

在git merge 也可以使用 -m参数,将描述添加进去,

git mrege -m "this is merge"  <brachName>

合并的另一种模式:禁用快进模式

git merge --no-ff -m "merge info" dev

当使用快进模式合并时,如果被合并进来的分支走到了前面,就会直接将当前分支的指针移到合并进来的分支的最新提交,合并之后的commit_id就是这个最新提交的保持一次,在这种模式下,删除分支后,会丢掉分支信息,并且看不出来曾经有过合并。

而禁用快进模式,会在当前分支创建一个新的commit(新的commit_id),自己的版本向前增加去应用这次合并。

解决冲突

当你在不同的分支上分别有了新的提交,如下图:

分支

这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突。

查看存在冲突的文件,会发现Git用<<<<<<<=======>>>>>>>标记出不同分支的内容。<<<<<<<=======之间是当前分支的代码,=======>>>>>>>是要合并过来的分支的代码。

当存在冲突时,需要手动解决冲突后(整理好代码并删除分隔符),重新add commit。

最后,所有分支情况就变成了下图,feature1分支就会落后master分支一个版本,如果需要在feature1上继续开发并之后还会合并到master就需要在feature1分支拉取master分支上的代码保持同步。

解决冲突

Bug分支

场景:当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它。

如果当前有未提交的工作,未开发完成或不想提交,之后可以使用 git stash将改动储藏起来,恢复到和版本库一模一样的状态,之后可以恢复到stash前的状态。

git stash save 'stash message'

1、之后的操作

# 在哪个分支上修复bug,就切到哪个分支上去创建临时分支
git checkout master 
git checkout -b issue-101

# 在新分支上修复bug后提交这次修复
git add .
git commit -m 'fix bug'

# 切换到master分支,合并这个bug分支,完成master分支上bug的修复
git checkout master
git merge --no-ff -m "merged bug fix" issue-101

2.1、切换到之前自己工作的分支,将储藏的修改弹出。

# 查看储藏的信息列表
git stash list
stash@{0}: WIP on dev: f52c633 add merge

# 存储当前改动
git stash save "message"

# 应用最新储藏的,但是会删除这一次stash的信息
git stash pop

# 应用指定储藏(默认应用第一次储藏stash@{0}),不会删除stash的信息
git stash apply stash@{x}

# 删除指定储藏
git stash drop stash@{x}

# 删除所有储藏
git stash clear

2.2、切换到之前自己工作的分支,同时想要在这个分支上也应用这次bug修复

找到master修复bug的那次提交的commit_id,使用以下命令复制这个提交。

需要注意的是,在master上合并这个bug分支时,没有产生新的commit_id,依然是commit ‘fix bug’这次提交产生的commit_id

# 在当前分支上 应用某次修改
git cherry-pick <commit_id>

# 应用多次修改 包含B 不包含A, A为时间线较早的commit
git cherry-pick A...B

# 应用多次修改 包含A和B
git cherry-pick A^...B

使用这个命令复制修改后,Git自动给当前分支做一次提交,注意这次提交的commit_id是新的,不同于直接的master合并bug分支。

Feature 分支

场景:添加一个新功能时,你肯定不希望因为一些实验性质的代码,把主分支搞乱了,所以,每添加一个新功能,最好新建一个feature分支,在上面开发,完成后,合并,最后,删除该feature分支。

操作(与bug分支一样):

# 创建新功能分支,并提交
git checkout -b feature
git add .
git commit -m "add feature"

# 切回原分支准备合并
git checkout dev 

# 可是,合并之前新功能被砍了,那么这个新功能分支就必须被删除
git branch -d feature

# 但是你会发现,销毁失败,因为feature分支还没有合并,如果删除会丢失修改
# 这时候就需要使用强制删除 -D参数
git branch -D feature

多人协作

# 可以查看远程库的详细
git remote -v
origin  git@github.com:xxx/xx.git (fetch) // 从哪抓取
origin  git@github.com:xxx/xx.git (push) // 推送到哪,如果没有push的权限,就看不到push的位置

# 添加远程仓库 默认的远程仓库的代称就是origin
git remote add github<该仓库的代称,自己取名> github地址<该仓库的地址>

# 从不同的远程仓库拉取代码
git fetch origin
git fetch github
git fetch --all

推送到远程的不同分支上

git push origin master // 推送到远程master分支
git push origin dev // 推送到远程dev分支

抓取分支

场景:当你clone的时候,默认情况下,只能clone到master上的信息, 这时候使用git branch也只能看到master分支,如果你希望也克隆远程的dev分支

# 在本地创建新分支dev 并关联远程的dev分支 或者说 从远程dev分支上检出本地dev分支
git checkout -b dev origin/dev 

# 修改并推送到远程dev分支
git add .
git commit
git push origin dev # 修改并推送到远程dev分支

# 本地已存在一个分支直接创建并推送远程分支 格式 本地分支:远程分支
git push origin dev:dev

冲突

场景:当多人协作,都往 origin dev上推送修改时,就有可能产生冲突(修改同一个位置),这个时候,后push的人就会push失败。

这个时候,就需要先使用git pull将最新的提交从origin dev上拉取下来,(push到哪个分支产生的冲突,就拉哪个分支上的代码),在本地解决冲突,提交再推送。

需要注意的是,如果本地的dev和origin dev分支没有关联的时候,使用git pull会失败,需要使用git pull origin dev,或者使用以下命令,将其关联之后,在抓取。

如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建

# =号后面的就是指将远程的dev分支和本地的dev分支形成关联
git branch --set-upstream-to=dev origin/dev
git pull

# 解决完冲突后重新push
git add .
git commit 
git push origin dev

Rebase: 变基

场景:多人开发时,目前的版本号是commit_0,A开发完成后,提交了一个commit,生成了版本号commit_A,此时,使用git push推送到远程master分支,发现推送失败,因为远程master版本比你本地的master版本多走了一步,原因B已经在你前面提交了commit_B并成功推动了远程的master分支往前走了一步,这时候,你push自然失败,因为master的分支未能与远程保持同步,所以你需要pull一下(有可能产生冲突,需手动解决冲突),pull之后,因为你本地的master分支和远程的不一样,git会自动产生一次合并形成一个commit_merge。

此时你本地的节点树是这样的, 且是分叉的,如果你将本地的代码再进行push到远程master分支,那么远程master分支的节点树也会出现多余commit_merge。

这种场景下不使用pull rebase,而是直接pull,修改之后push,那么你本地分支一定会有一个多余的commit_merge,使得节点树不够清晰

git5

但是理想状态下,你应该发觉有人在你前面push了,你应该先pull,让本地与远程的master分支保持一次,在commit这次commit_A,之后再push,这样操作,节点树才是正常的一条直线commit_0 - commit_B - commit_A。

事实上,很有可能你在push的时候并不知道(其实也不必知道)有人在你前面push成功了,可以使用rebase的功能,当远程版本领先你本地版本一步,你就需要先 git pull 拉取下来(此时已分叉),然后使用git rebase整理提交历史成为一条直线,或者直接使用命令 git pull -rebase,此后在push,去推动远程master再往前走一步,此时节点树也是正常的流程,commit_0 - commit_B - commit_A。

# 变基为当前dev分支上的HEAD指向,当前改动应用在此HEAD之后;
git pull origin dev --rebase

# 变基为某次commit 将当前的改动应用在此次提交之后 可以用来合并修改 或者修改已经提交了的commit
# 其中-i的意思是--interactive,即弹出交互式的界面让用户编辑完成操作,

# 变基到某一次提交
git rebase -i <commitId>
git rebase -i asd2112
pick asd2112 修改一


# 变基到最近3次提交之前,对这3个commit进行操作
git rebase -i HEAD~3 

pick asd2112 修改一
pick kjk21an 修改二
pick Auk3pan 修改三

# 使用vim模式进行修改 选择pick还是其他操作 然后wq
pick: 保留此次提交
reword: 保留此次提交,但是修改commit message
edit: 保留此次提交 但是需要修改
squash: 压缩此次提交,即合并到上一个的提交里面
fixup: 将该commit和前一个commit合并,但我不要保留该提交的注释信息
exec: 执行shell命令
drop: 丢弃该commit

# 对相关提交操作完之后
git add .
git rebase --continue

# 此时,本地就是整理之后的commit记录
# 如果之前的commit已经提交到远程了,此时需要强推
git push origin dev -f

如果某次你提交test分支向dev分支的MR之后,别人的MR在你前面合进了dev分支,又刚好与你的MR冲突,则可以使用pull rebase来解决冲突。

# 1.pull rebase 拉取远程分支上最新的代码,并变基,使当前分支上改动的代码应用在origin/dev HEAD上。
git pull origin dev --rebase

# 2.1 解决冲突,解决之后
git add .
git rebase --continue

# 3.强推
git push feature/test -f

# 4.如果还有别的分支关联这个test的分支 因为使用强推改变该分支的commit走向 可以使用reset --hard来解决

# 如果与这个feature/test关联的有 feature/test2分支
# 这样 feature/test2 就依然还是与 feature/test 保持一致

git checkout test2
git reset --hard test
git push test2 -f