jjzjj

Git 详细教程之三: Git 分支操作

PanyCG_pc 2024-04-24 原文

   本篇博客主要讲的是『Git 详细教程之三: Git 分支操作』。如果您是第一次学习 Git ,请先阅读博主的前几篇文章:

一、Git 分支的理解及好处

   几乎所有的版本控制系统都以某种形式支持分支。 使用分支意味着你可以把你的工作从开发主线上分离开来,以免影响开发主线。 在很多版本控制系统中,这是一个略微低效的过程——常常需要完全创建一个源代码目录的副本。对于大项目来说,这样的过程会耗费很多时间。

   Git 的分支模型称为它的“必杀技特性”,其处理分支的方式可谓是难以置信的轻量,创建新分支这一操作几乎能在瞬间完成,并且在不同分支之间的切换操作也是一样便捷。

1.1 Git 处理分支的方式(理解分支)

   为了真正理解 Git 处理分支的方式,我们需要回顾一下 Git 是如何保存数据的。简单来说,Git 保存的不是文件的变化或者差异,而是一系列不同时刻的 快照

   在进行提交操作时,Git 会保存一个提交对象(commit object),该提交对象会包含一个指向暂存内容快照的指针。 但不仅仅是这样,该提交对象还包含了作者的姓名和邮箱提交时输入的信息以及指向它的父对象的指针
   注意: 首次提交产生的提交对象没有父对象,普通提交操作产生的提交对象有一个父对象, 而由多个分支合并产生的提交对象有多个父对象,

   为了更加形象地说明,我们假设现在有一个工作目录,里面包含了三个将要被暂存和提交的文件。 暂存操作会为每一个文件计算校验和( SHA-1 哈希算法),然后会把当前版本的 文件快照 保存到 Git 仓库中 (Git 使用 blob 对象来保存它们),最终将校验和加入到暂存区域等待提交:

$ git add README test.rb LICENSE
$ git commit -m 'The initial commit.'

   当使用 git commit 进行提交操作时,Git 会先计算每一个子目录(本例中只有项目根目录)的校验和, 然后在 Git 仓库中这些校验和保存为树对象。随后,Git 便会创建一个提交对象, 它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。 如此一来,Git 就可以在需要的时候重现此次保存的快照。

   现在,Git 仓库中有五个对象:

  • 三个 blob 对象(保存着文件快照);
  • 一个 树 对象 (记录着目录结构和 blob 对象索引);
  • 一个 提交 对象(包含着指向前述树对象的指针和所有提交信息)。

  做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针。

  以上就是 Git 保存数据的方式。回到 Git 的分支,其实本质上仅仅是指向提交对象的可变指针。Git 的默认分支名字是 master。 在多次提交操作之后,你其实已经有一个指向最后那个提交对象的 master 分支master 分支会在每次提交时自动向前移动。下图可以帮助我们很好地理解 Git 分支的运行原理。


1.2 Git 分支的好处

  同时并行推进多个功能开发,提高开发效率。
  各个分支在开发过程中,如果某一分支开发失败,不会对其他分支造成任何影响。失败的分支删除重新开始即可。


二、Git 分支操作

2.1 查看分支

  分支在 Git 里称之为 branch ,查看分支的基本语法为:

git branch -v

执行上述命令后,我们可以看到,继上篇博文的操作,目前项目中只存在一个 master 分支:


2.2 创建分支

  创建分支的本质只是创建了一个可以移动的新的指针,其基本语法为:

git branch 分支名

  例如,创建一个 testing 分支,我们需要执行 git branch testing 命令:

这会在当前所在的提交对象上创建一个新的指针 testing


  那么,Git 又是怎么知道当前在哪一个分支上呢? 也很简单,它有一个名为 HEAD 的特殊指针,指向当前所在的本地分支,可以将 HEAD 想象为当前分支的别名。 在本例中,你仍然在 master 分支上。 因为 git branch 命令仅仅创建 一个新分支,并不会自动切换到新分支中去

  可以简单地使用 git log --decorate 命令查看各个分支当前所指的对象,可以看到此时, HEAD 指针同时指向了两个分支指针 mastertesting,且指向的是第二次提交的提交对象。


2.3 切换分支

  当我们项目存在多个分支时,如何进行分支间的切换?基本语法为:

git checkout 分支名

  如果我们执行 git checkout testing


此时,HEAD 指针就指向 testing 分支 了。


   注意: 如果需要新建一个新的分支,随后立即切换到该分支,我们可以简写成:

git checkout -b new_branch

它与下面两条命令等价:

git branch new_branch
git checkout new_branch

  为了更好地理解分支的意义,我们对 test.cpp 做一次修改再次提交:

  可以发现,提交过后,我们仍处于 testing 分支,指向的是第三次提交的提交对象;而 master 分支指向的是第二次提交的对象。下图可以帮助我们更好地理解:

  如图所示, testing 分支 向前移动了,但是 master 分支 却没有。由此,我们再切换回 master 分支 ,可以发现,执行 git checkout master 后, HEAD 指针指回 master 分支,同时工作目录恢复成 master 分支 所指向的快照内容。本质上来讲,这就产生了多条开发路线,另外的开发人员可以直接进入到 testing 分支 进行开发,互不影响。这样就实现了多条开发路线的并行工作。


2.4 合并分支

  在实际工作中你可能会碰到以下类似的工作流:

  • 开发某个网站,为实现某个新的用户需求,创建一个分支,并在这个分支上开展工作;
  • 突然,接到一个电话说有个很严重的问题需要紧急修补;
  • 切换到你的线上分支(production branch);
  • 为这个紧急任务新建一个分支,并在其中修复它;
  • 在测试通过之后,切换回线上分支,然后合并这个修补分支,最后将改动推送到线上分支;
  • 切换回你最初工作的分支上,继续工作。

  在开发过程中,以上的情况经常出现,这时就需要我们对多个分支进行合并。合并分支的基本语法为:

git merge 分支名

  该命令实现的是将某一分支合并到现有分支上。继『2.3 切换分支』例子后,我们有两个分支:master 分支和在 master 分支基础上进行修改后的 testing 分支,且项目正处于 master 分支上,当我们执行 git merge testing 后:

  可以看到,合并后,test.cpp 文件出现了更新,且提示 Fast-forward ,当我们再次查看分支信息时,发现现处的 master 分支其实就是 testing 分支,指向的也就是我们第三次提交的内容。这是因为我们第三次提交时,只在第二次提交的基础上添加了一行内容,当把 testing 分支合并到 master 分支后,也就是将改行加上而已,符合合并逻辑。

  注意: Fast-forward 的提示信息指的是快进的意思,由于想要合并的分支 testing 所指向的提交是 master 分支的直接后继, 因此 Git 会直接将指针向前移动到该次提交。换句话说,当你试图合并两个分支时, 如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候, 只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”。

  此时,一般而言,合并后应该删除 testing 分支,因为你已经不再需要它了(master 分支已经指向了同一个位置)。我们可以使用带 -d 选项的 git branch 命令来删除分支:

git branch -d testing


2.5 冲突合并

  有时候合并操作不会如此顺利,会遇到冲突。原因在于:在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改。此时Git 就没法干净的合并它们,在合并它们的时候就会产生合并冲突,必须人为决定新代码内容:

  此时 Git 会暂停下来,等待你去解决合并产生的冲突。此时使用 git status 命令可以查看那些因包含合并冲突而处于未合并(unmerged)状态的文件:

  有上面可以看出,任何因包含合并冲突而有待解决的文件,都会以未合并状态标识出来。 Git 会在有冲突的文件中加入标准的冲突解决标记,如下图所示类似:

  这表示在 <<<<<<<>>>>>>> 之间的内容出现了合并冲突,且现处分支 HEAD 的内容和待合并分支 testing 的内容由 ======= 隔开。

  上述的冲突文件会一直等待人为解决。当我们执行 vim test.cpp 命令,解决了文件里的冲突且 <<<<<<<=======>>>>>>> 这些行被完全删除,再对 test.cpp 文件使用 git add test.cpp 命令可以将其标记为冲突已解决状态。
  注意:只有暂存了这些原本有冲突的文件,Git 才会将它们标记为冲突已解决。

  此后,再次执行 git commit 命令来完成合并提交。


2.6 分支管理

   git branch 命令不只是可以创建与删除分支(-d 选项)。 如果不加任何参数运行它,会得到当前所有分支的一个列表:

   注意: master 分支前的 * 字符:它代表的是现处分支(也就是当前 HEAD 指针所指向的分支)。这意味着如果在这时候提交,master 分支将会随着新的工作向前移动。

  如果需要查看每一个分支的最后一次提交,可以运行 git branch -v 命令:

   --merged--no-merged 这两个有用的选项可以过滤这个列表中已经合并或尚未合并到当前分支的分支。

  • git branch --merged 命令表示查看哪些分支已经合并到当前分支:
  • git branch --no-merged 命令表示查看所有包含未合并工作的分支:

有关Git 详细教程之三: Git 分支操作的更多相关文章

  1. postman接口测试工具-基础使用教程 - 2

    1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,

  2. 在VMware16虚拟机安装Ubuntu详细教程 - 2

    在VMware16.2.4安装Ubuntu一、安装VMware1.打开VMwareWorkstationPro官网,点击即可进入。2.进入后向下滑动找到Workstation16ProforWindows,点击立即下载。3.下载完成,文件大小615MB,如下图:4.鼠标右击,以管理员身份运行。5.点击下一步6.勾选条款,点击下一步7.先勾选,再点击下一步8.去掉勾选,点击下一步9.点击下一步10.点击安装11.点击许可证12.在百度上搜索VM16许可证,复制填入,然后点击输入即可,亲测有效。13.点击完成14.重启系统,点击是15.双击VMwareWorkstationPro图标,进入虚拟机主

  3. git使用常见问题(提交代码,合并冲突) - 2

    文章目录git常用命令(简介,详细参数往下看)Git提交代码步骤gitpullgitstatusgitaddgitcommitgitpushgit代码冲突合并问题方法一:放弃本地代码方法二:合并代码常用命令以及详细参数gitadd将文件添加到仓库:gitdiff比较文件异同gitlog查看历史记录gitreset代码回滚版本库相关操作远程仓库相关操作分支相关操作创建分支查看分支:gitbranch合并分支:gitmerge删除分支:gitbranch-ddev查看分支合并图:gitlog–graph–pretty=oneline–abbrev-commit撤消某次提交git用户名密码相关配置g

  4. hadoop安装之保姆级教程(二)之YARN的配置 - 2

    1.1.1 YARN的介绍 为克服Hadoop1.0中HDFS和MapReduce存在的各种问题⽽提出的,针对Hadoop1.0中的MapReduce在扩展性和多框架⽀持⽅⾯的不⾜,提出了全新的资源管理框架YARN. ApacheYARN(YetanotherResourceNegotiator的缩写)是Hadoop集群的资源管理系统,负责为计算程序提供服务器计算资源,相当于⼀个分布式的操作系统平台,⽽MapReduce等计算程序则相当于运⾏于操作系统之上的应⽤程序。 YARN被引⼊Hadoop2,最初是为了改善MapReduce的实现,但是因为具有⾜够的通⽤性,同样可以⽀持其他的分布式计算模

  5. ruby - 如何使用 Selenium Webdriver 根据 div 的内容执行操作? - 2

    我有一个使用SeleniumWebdriver和Nokogiri的Ruby应用程序。我想选择一个类,然后对于那个类对应的每个div,我想根据div的内容执行一个Action。例如,我正在解析以下页面:https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=puppies这是一个搜索结果页面,我正在寻找描述中包含“Adoption”一词的第一个结果。因此机器人应该寻找带有className:"result"的div,对于每个检查它的.descriptiondiv是否包含单词“adoption

  6. ruby-on-rails - 如何处理 Grape 中特定操作的过滤器之前? - 2

    我正在我的Rails项目中安装Grape以构建RESTfulAPI。现在一些端点的操作需要身份验证,而另一些则不需要身份验证。例如,我有users端点,看起来像这样:moduleBackendmoduleV1classUsers现在如您所见,除了password/forget之外的所有操作都需要用户登录/验证。创建一个新的端点也没有意义,比如passwords并且只是删除password/forget从逻辑上讲,这个端点应该与用户资源。问题是Grapebefore过滤器没有像except,only这样的选项,我可以在其中说对某些操作应用过滤器。您通常如何干净利落地处理这种情况?

  7. ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成? - 2

    在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.

  8. ruby - 在 Ruby 中是否有一种惯用的方法来操作 2 个数组? - 2

    a=[3,4,7,8,3]b=[5,3,6,8,3]假设数组长度相同,是否有办法使用each或其他一些惯用方法从两个数组的每个元素中获取结果?不使用计数器?例如获取每个元素的乘积:[15,12,42,64,9](0..a.count-1).eachdo|i|太丑了...ruby1.9.3 最佳答案 使用Array.zip怎么样?:>>a=[3,4,7,8,3]=>[3,4,7,8,3]>>b=[5,3,6,8,3]=>[5,3,6,8,3]>>c=[]=>[]>>a.zip(b)do|i,j|c[[3,5],[4,3],[7,6],

  9. ruby-on-rails - 如何让 Rails View 返回其关联的操作名称? - 2

    我有一个非常简单的Controller来管理我的Rails应用程序中的静态页面:classPagesController我怎样才能让View模板返回它自己的名字,这样我就可以做这样的事情:#pricing.html.erb#-->"Pricing"感谢您的帮助。 最佳答案 4.3RoutingParametersTheparamshashwillalwayscontainthe:controllerand:actionkeys,butyoushouldusethemethodscontroller_nameandaction_nam

  10. ruby - Dropbox 类似 git 的服务——没有 rsync 和 inotify - 2

    关于如何使用git设置类似Dropbox的服务,您有什么建议吗?您认为git是解决此问题的合适工具吗?我在考虑使用git+rush解决方案,你觉得怎么样? 最佳答案 检查这个开源项目:https://github.com/hbons/SparkleShare来自项目的自述文件:Howdoesitwork?SparkleSharecreatesaspecialfolderonyourcomputer.Youcanaddremotelyhostedfolders(or"projects")tothisfolder.Theseprojec

随机推荐