Git分支

分支基础

  • 在不同的分支可以同时进行不同的任务,互相不干扰,任务完成后再合并
  • 分支本质上只是一个指向commit object的一个指针
  • 分支指针一定是指向当前分支最新的commit object
  • 当前处于哪一个分支是由HEAD指针指向哪一个分支指针决定的

新建分支

git branch <branch> 创建名为branch的分支

image-20191120193646252

新建testing分支后示意图(未示意HEAD指针和tag)

image-20191120193804046

切换分支

  • 切换分支后,工作区和暂存区的状态会转换为该分支的最后提交的代码状态
  • git switch <branch>git checkout <branch> 切换到名为branch的分支
  • git switch -c <branch>git checkout -b <branch> 创建并切换分支

查看分支

  • git branch 查看分支
  • git branch -v 分支信息加上该分支最后一次提交的信息
  • git branch --merged 查看已经合并到当前分支的分支信息
  • git branch --no-merged 查看未合并到当前分支的分支信息
  • git diff <first-branch> <second-branch> 查看两个分支的差异

删除分支

  • git branch -d <branch> 删除名为branch的分支
  • git branch -D <branch> 强制删除未被合并的branch分支

合并分支

基础合并

  • git merge <branch> 当前所在的分支合并名为branch的分支
  • git merge -m "<comment>" <branch> 非快进合并加上合并的提交注释

快进合并

  • 快进(fast forward)合并,要合并的分支在当前分支的直接下游,即可使用快进合并,直接移动分支指针完成合并 合并后

  • git merge --no-ff <branch>强制禁止快进合并,则生成一次合并的提交

三方合并

  • 要合并的分支不在当前分支的直接下游,不可使用快进合并的情况下,则使用三方合并 image-20191120194503610
  • 三方合并前后对比示意图,三方合并将生成一次合并提交
  • git merge --no-commit <branch>可以延迟合并的生成,让你可以在合并之后提交之前再作改动

解决合并冲突

  • 合并发生冲突后,使用git status可以查看当前未解决冲突的文件 image-20191124104444139 image-20191124104504014
  • 未解决冲突的文件,Git将自动在冲突的部分加上不同版本的修改信息
  • git checkout --conflict=diff3 <file> 冲突三方比较 image-20191125102434609
  • git checkout --ours <file> git checkout --theirs <file> 使用单方面代码快速解决冲突
  • git merge -Xours <branch> git merge -Xtheirs <branch> 单方面代码快速解决冲突合并
  • git log --oneline --left-right --merge 冲突相关提交日志
  • 解决好冲突后,加入暂存区并提交即可完成合并
  • git merge --abort 中断合并,尝试恢复到你运行合并前的状态

但当运行命令前,在工作目录中有未储藏、未提交的修改时它不能完美处理,除此之外它都工作地很好。

  • git reset --hard HEAD 另一种中断合并的方式
  • git reset --hard HEAD^ 撤销合并
  • git revert -m <n> <commit-id> 增加一个提交用于撤销合并,n=1保留合并前的HEAD,n=2保留另一分支 image-20191120233544617 撤销合并后又要再合并,先撤销之前的撤销合并提交 image-20191120233826404

假合并

  • git merge -s ours <branch-name> 不管有没有冲突,代码全用当前分支的,并没有真正合并,只是标记合并

squash

  • git merge --squash <branch>将branch的所有提交压缩成一个变更集,应用到当前分支,不会自动生成提交,合并后的提交只会有一个父提交 image-20191124111026950 image-20191124111054930

变基

常见变基

  • git rebase <base-branch> 当前分支以base-branch为基础变基 变基前 image-20191120202238429 变基后,可快进合并分支(当前分支experiment 执行git rebase masterimage-20191120202411989
  • git rebase --onto <base-branch> <excluded-branch> <included-branch> image-20191120202655164 执行git rebase --onto master server clientimage-20191120202726885
  • git rebase <base-branch> <rebase-branch> 无需先把rebase-branch切换到当前分支的变基

变基原则

  • 不要对在你的仓库外有副本的分支执行变基 image-20191125100430213 image-20191125100851072 image-20191125101148483

  • 变基解决变基 git fetch <remote>git rebase <remote-branch> git pull --rebase image-20191125102001251

变基 vs. 合并

至此,你已在实战中学习了变基和合并的用法,你一定会想问,到底哪种方式更好。在回答这个问题之前,让我们退后一步,想讨论一下提交历史到底意味着什么。 有一种观点认为,仓库的提交历史即是记录实际发生过什么。它是针对历史的文档,本身就有价值,不能乱改。从这个角度看来,改变提交历史是一种亵渎,你使用谎言掩盖了实际发生过的事情。如果由合并产生的提交历史是一团糟怎么办?既然事实就是如此,那么这些痕迹就应该被保留下来,让后人能够查阅。 另一种观点则正好相反,他们认为提交历史是项目过程中发生的事。 没人会出版一本书的第一版草稿,软件维护手册也是需要反复修订才能方便使用。持这一观点的人会使用rebase及filter-branch 等工具来编写故事,怎么方便后来的读者就怎么写。 现在,让我们回到之前的问题上来,到底合并还是变基好?希望你能明白,这并没有一个简单的答案。Git是一个非常强大的工具,它允许你对提交历史做许多事情,但每个团队、每个项目对此的需求并不相同。既然你已经分别学习了两者的用法,相信你能够根据实际情况作出明智的选择。 总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。

cherry-pick

  • git cherry-pick <commit-id> 把commit id这次提交提取出来,提交到当前分支 image-20191122171838093

变基冲突

  • 变基过程中出现冲突,则需要手动修改冲突,处理完后执行git rebase --continue
  • 若发生冲突后不希望继续进行变基,则执行git rebase --abort