Git使用简介 rainzha 2015-03-29
Git入门 直接记录快照,而非记录差异
Git入门 文件的三种状态:已提交(committed),已修改(modified)和已暂 存(staged)
Git入门 基本的 Git 工作流程如下: 1.在工作目录中修改某些文件。 2.对修改后的文件进行快照,然后保存到暂存区域。
Git起步 初次运行前的配置git config —global user.name user.email 在工作目录中初始化新仓库git init 在现有项目中克隆git clone
Git基础-记录每次更新到仓库 检查当前文件状态git status 跟踪新文件git add 忽略某些文件.gitingore 查看已暂存和未暂存的更新git diff,git diff —staged 提交更新git commit -m “更新说明” 跳过使用暂存区域git commit -a 移除文件git rm 移动文件git mv
Git基础-查看提交历史 查看提交历史git log
Git基础-撤销操作 修改最后一次提交git commit —amend 取消已经暂存的文件git reset HEAD <file>… 取消对文件的修改git checkout — <file>…
Git基础-远程仓库的使用 查看当前的远程库git remote 添加远程仓库git remote add 从远程仓库抓取数据git fetch [remote-name] 推送数据到远程仓库git push [remote-name] 查看远程仓库信息git remote show [remote-name] 远程仓库的删除和重命名
Git分支-何谓分支 几乎每一种版本控制系统都以某种形式支持分支。使用分支意味着你可以 从开发主线上分离开来,然后在不影响主线的同时继续工作。在很多版本 控制系统中,这是个昂贵的过程,常常需要创建一个源代码目录的完整副 本,对大型项目来说会花费很长时间。 有人把 Git 的分支模型称为“必杀技特性”,而正是因为它,将 Git 从版本控 制系统家族里区分出来。Git 有何特别之处呢?Git 的分支可谓是难以置信 的轻量级,它的新建操作几乎可以在瞬间完成,并且在不同分支间切换起 来也差不多一样快。和许多其他版本控制系统不同,Git 鼓励在工作流程中 频繁使用分支与合并,哪怕一天之内进行许多次都没有关系。理解分支的 概念并熟练运用后,你才会意识到为什么 Git 是一个如此强大而独特的工 具,并从此真正改变你的开发方式。
Git分支-何谓分支
Git分支-分支的新建与合并 新建分支(注:还有多种方法)git branch <branch> 切换到当前分支git checkout <branch>
Git分支-分支的新建与合并 在分支上提交git commit 切换回master分支git checkout master
Git分支-分支的新建与合并 在master分支上提交git commit
Git分支-分支的新建与合并 这和大多数版本控制系统形成了鲜明对比,它们管理分支大多采取备 份所有项目文件到特定目录的方式,所以根据项目文件数量和大小不 同,可能花费的时间也会有相当大的差别,快则几秒,慢则数分钟。 而 Git 的实现与项目复杂度无关,它永远可以在几毫秒的时间内完成 分支的创建和切换。同时,因为每次提交时都记录了祖先信息(注: 即 parent 对象),将来要合并分支时,寻找恰当的合并基础(注: 即共同祖先)的工作其实已经自然而然地摆在那里了,所以实现起来 非常容易。Git 鼓励开发者频繁使用分支,正是因为有着这些特性作 保障。
Git分支-一个例子 现在让我们来看一个简单的分支与合并的例子,实际工作中大体也会 用到这样的工作流程: 开发某个网站。 为实现某个新的需求,创建一个分支。 在这个分支上开展工作。 假设此时,你突然接到一个电话说有个很严重的问题需要紧急修补, 那么可以按照下面的方式处理: 返回到原先已经发布到生产服务器上的分支。 为这次紧急修补建立一个新分支,并在其中修复问题。 通过测试后,回到生产服务器所在的分支,将修补分支合并进来,然后再推送到生产服务器上。 切换到之前实现新需求的分支,继续工作。
Git分支-一个例子 假设项目当前的提交历史 现在你决定修补问题53,要新建并切换到该分支,运行 git checkout 并加上 -b 参数 接着你开始尝试修复问题,在提交了若干更新后
Git分支-一个例子 现在你就接到了那个网站问题的紧急电话,需要马上修补。有了 Git ,我们就不需要同时发布这个补丁和 iss53 里作出的修改,也不需 要在创建和发布该补丁到服务器之前花费大力气来复原这些修改。唯 一需要的仅仅是切换回 master 分支。 接下来,你得进行紧急修补。我们创建一个紧急修补分支 hotfix 来 开展工作,直到搞定:
Git分支-一个例子 现在有必要作些测试,确保修补是成功的,然后回到 master 分支并把它合并进 来,然后发布到生产服务器。用 git merge 命令来进行合并: 请注意,合并时出现了“Fast forward”的提示。由于当前 master 分支所在的提交 对象是要并入的 hotfix 分支的直接上游,Git 只需把 master 分支指针直接右 移。换句话说,如果顺着一个分支走下去可以到达另一个分支的话,那么 Git 在合 并两者时,只会简单地把指针右移,因为这种单线的历史分支不存在任何需要解 决的分歧,所以这种合并过程可以称为快进(Fast forward)。
Git分支-一个例子 在那个超级重要的修补发布以后,你想要回到被打扰之前的工作。由 于当前 hotfix 分支和 master 都指向相同的提交对象,所以 hotfix 已经完成了历史使命,可以删掉了。使用 git branch 的 -d 选项执行删除操作:
Git分支-一个例子 值得注意的是之前 hotfix 分支的修改内容尚未包含到 iss53 中来。如果需要纳入此次修 补,可以用 git merge master 把 master 分支合并到 iss53;或者等 iss53 完成之后 ,再将 iss53 分支中的更新并入 master。 请注意,这次合并操作的底层实现,并不同于之前 hotfix 的并入方式。因为这次你的开发 历史是从更早的地方开始分叉的。由于当前 master 分支所指向的提交对象(C4)并不是 iss53 分支的直接祖先,Git 不得不进行一些额外处理。就此例而言,Git 会用两个分支的 末端(C4 和 C5)以及它们的共同祖先(C2)进行一次简单的三方合并计算。
Git分支-一个例子 这次,Git 没有简单地把分支指针右移,而是对三方合并后的结果重 新做一个新的快照,并自动创建一个指向它的提交对象(C6)。这 个提交对象比较特殊,它有两个祖先(C4 和 C5)。 值得一提的是 Git 可以自己裁决哪个共同祖先才是最佳合并基础;这 和 CVS 或 Subversion(1.5 以后的版本)不同,它们需要开发者手 工指定合并基础。所以此特性让 Git 的合并操作比其他系统都要简单 不少。
Git分支-一个例子 有时候合并操作并不会如此顺利。如果在不同的分支中都修改了同一 个文件的同一部分,Git 就无法干净地把两者合到一起(注:逻辑上 说,这种问题只能由人来裁决)。 Git 作了合并,但没有提交,它会停下来等你解决冲突。要看看哪些 文件在合并时发生冲突,可以用 git status 查阅。 任何包含未解决冲突的文件都会以未合并(unmerged)的状态列出 。Git 会在有冲突的文件里加入标准的冲突解决标记,可以通过它们 来手工定位并解决这些冲突。可以看到此文件包含类似下面这样的部 分:
Git分支-一个例子 可以看到 ======= 隔开的上半部分,是 HEAD(即 master 分支,在运行 merge 命 令时所切换到的分支)中的内容,下半部分是在 iss53 分支中的内容。解决冲突的办 法无非是二者选其一或者由你亲自整合到一起。比如你可以通过把这段内容替换为下面 这样来解决: 这个解决方案各采纳了两个分支中的一部分内容,而且我还删除了 <<<<<<<, ======= 和 >>>>>>> 这些行。在解决了所有文件里的所有冲突后,运行 git add 将 把它们标记为已解决状态(译注:实际上就是来一次快照保存到暂存区域。)。因为一 旦暂存,就表示冲突已经解决。如果你想用一个有图形界面的工具来解决这些问题,不 妨运行 git mergetool,它会调用一个可视化的合并工具并引导你解决所有冲突。 退出合并工具以后,Git 会询问你合并是否成功。如果回答是,它会为你把相关文件暂 存起来,以表明状态为已解决。
Git分支-利用分支进行开发的工作流程 长期分支:许多使用 Git 的开发者都喜欢用这种方式来开展工作,比如仅在 master 分 支中保留完全稳定的代码,即已经发布或即将发布的代码。与此同时,他们还有一个名 为 develop 的平行分支,专门用于后续的开发,或仅用于稳定性测试 — 当然并不是说 一定要绝对稳定,不过一旦进入某种稳定状态,便可以把它合并到 master 里。这样, 在确保这些已完成的特性分支(短期分支,比如之前的 iss53 分支)能够通过所有测 试,并且不会引入更多错误之后,就可以并到主干分支中,等待下一次的发布。 在任何规模的项目中都可以使用特性(Topic)分支。一个特性分支是指一个短期的, 用来实现单一特性或与其相关工作的分支。可能你在以前的版本控制系统里从未做过类 似这样的事情,因为通常创建与合并分支消耗太大。然而在 Git 中,一天之内建立、使 用、合并再删除多个分支是常见的事。 我们在上节的例子里已经见过这种用法了。我们创建了 iss53 和 hotfix 这两个特性 分支,在提交了若干更新后,把它们合并到主干分支,然后删除。该技术允许你迅速且 完全的进行语境切换 — 因为你的工作分散在不同的流水线里,每个分支里的改变都和 它的目标特性相关,浏览代码之类的事情因而变得更简单了。你可以把作出的改变保持 在特性分支中几分钟,几天甚至几个月,等它们成熟以后再合并,而不用在乎它们建立 的顺序或者进度。
Git分支-利用分支进行开发的工作流程 现在我们来看一个实际的例子。请看图 3-20,由下往上,起先我们在 master 工作 到 C1,然后开始一个新分支 iss91 尝试修复 91 号缺陷,提交到 C6 的时候,又冒 出一个解决该问题的新办法,于是从之前 C4 的地方又分出一个分支 iss91v2,干 到 C8 的时候,又回到主干 master 中提交了 C9 和 C10,再回到 iss91v2 继续工 作,提交 C11,接着,又冒出个不太确定的想法,从 master 的最新提交 C10 处开 了个新的分支 dumbidea 做些试验。
Git分支-利用分支进行开发的工作流程 现在,假定两件事情:我们最终决定使用第二个解决方案,即 iss91v2 中的办法;另外 ,我们把 dumbidea 分支拿给同事们看了以后,发现它竟然是个天才之作。所以接下来, 我们准备抛弃原来的 iss91 分支(实际上会丢弃 C5 和 C6),直接在主干中并入另外两 个分支。最终的提交历史将变成图这样: 请务必牢记这些分支全部都是本地分支,这一点很重要。当你在使用分支及合并的时候, 一切都是在你自己的 Git 仓库中进行的 — 完全不涉及与服务器的交互。
Git分支-远程分支 远程分支(remote branch)是对远程仓库中的分支的索引。它们是 一些无法移动的本地分支;只有在 Git 进行网络交互时才会更新。远 程分支就像是书签,提醒着你上次连接远程仓库时上面各分支的位置 。 我们用 (远程仓库名)/(分支名) 这样的形式表示远程分支。比如我 们想看看上次同 origin 仓库通讯时 master 分支的样子,就应该 查看 origin/master 分支。如果你和同伴一起修复某个问题,但 他们先推送了一个 iss53 分支到远程仓库,虽然你可能也有一个本 地的 iss53 分支,但指向服务器上最新更新的却应该是 origin/iss53 分支。
Git分支-远程分支 假设你们团队有个地址为 git.ourcompany.com 的 Git 服务器。如 果你从这里克隆,Git 会自动为你将此远程仓库命名为 origin,并 下载其中所有的数据,建立一个指向它的 master 分支的指针,在本 地命名为 origin/master,但你无法在本地更改其数据。接着, Git 建立一个属于你自己的本地 master 分支,始于 origin 上 master 分支相同的位置,你可以就此开始工作:
Git分支-远程分支 如果你在本地 master 分支做了些改动,与此同时,其他人向 git.ourcompany.com 推送了他们的更新,那么服务器上的 master 分支就会向前推进,而与此同时,你在本地的提交历史正朝 向不同方向发展。不过只要你不和服务器通讯,你的 origin/master 指针仍然保持原位不会移动。
Git分支-远程分支 可以运行 git fetch origin 来同步远程服务器上的数据到本地。 该命令首先找到 origin 是哪个服务器(本例为 git.ourcompany.com),从上面获取你尚未拥有的数据,更新你 本地的数据库,然后把 origin/master 的指针移到它最新的位置 上。
Git分支-远程分支 为了演示拥有多个远程分支(在不同的远程服务器上)的项目是如何 工作的,我们假设你还有另一个仅供你的敏捷开发小组使用的内部服 务器 git.team1.ourcompany.com。可以用 git remote add 命令把它加为当前项目的远程分支之一。我们把它命名为 teamone ,以便代替完整的 Git URL 以方便使用。
Git分支-远程分支 现在你可以用 git fetch teamone 来获取小组服务器上你还没有 的数据了。由于当前该服务器上的内容是你 origin 服务器上的子集 ,Git 不会下载任何数据,而只是简单地创建一个名为 teamone/master 的远程分支,指向 teamone 服务器上 master 分支所在的提交对象 31b8e。
Git分支-远程分支 跟踪远程分支:从远程分支 checkout 出来的本地分支,称为 跟踪 分支 (tracking branch)。跟踪分支是一种和某个远程分支有直接联系 的本地分支。在跟踪分支里输入 git push,Git 会自行推断应该向 哪个服务器的哪个分支推送数据。同样,在这些分支里运行 git pull 会获取所有远程索引,并把它们的数据都合并到本地分支中来 。 删除远程分支:如果不再需要某个远程分支了,比如搞定了某个特性 并把它合并进了远程的 master 分支(或任何其他存放稳定代码的分 支),可以用这个非常无厘头的语法来删除它:git push [远程名 ] :[分支名]。
Git分支-分支的衍合 把一个分支中的修改整合到另一个分支的办法有两种:merge 和 rebase(注:rebase 的翻译暂定为“衍合”,还有翻译成“变基”)。 请回顾之前有关合并的一节,你会看到开发进程分叉到两个不同分支 ,又各自提交了更新。
Git分支-分支的衍合 之前介绍过,最容易整合分支的方法是 merge 命令,它会把两个分 支最新的快照(C3 和 C4)以及二者最新的共同祖先(C2)进行三 方合并,合并的结果是产生一个新的提交对象(C5),如图所示:
Git分支-分支的衍合 其实,还有另外一个选择:你可以把在 C3 里产生的变化补丁在 C4 的基础上重新打一遍。在 Git 里,这种操作叫做衍合(rebase)。有 了 rebase 命令,就可以把在一个分支里提交的改变移到另一个分支 里重放一遍。
Git分支-分支的衍合 它的原理是回到两个分支最近的共同祖先,根据当前分支(也就是要进行衍合的分支 experiment )后续的历次提交对象(这里只有一个 C3),生成一系列文件补丁,然后以基底分支(也就是主干 分支 master)最后一个提交对象(C4)为新的出发点,逐个应用之前准备好的补丁文件,最后会生 成一个新的合并提交对象(C3'),从而改写 experiment 的提交历史,使它成为 master 分支的 直接下游,如图所示: 现在回到 master 分支,进行一次快进合并,如上图所示: 现在的 C3' 对应的快照,其实和普通的三方合并,即上个例子中的 C5 对应的快照内容一模一样了。 虽然最后整合得到的结果没有任何区别,但衍合能产生一个更为整洁的提交历史。如果视察一个衍合 过的分支的历史记录,看起来会更清楚:仿佛所有修改都是在一根线上先后进行的,尽管实际上它们 原本是同时并行发生的。
Git分支-分支的衍合 一般我们使用衍合的目的,是想要得到一个能在远程分支上干净应用 的补丁 — 比如某些项目你不是维护者,但想帮点忙的话,最好用衍 合:先在自己的一个分支里进行开发,当准备向主项目提交补丁的时 候,根据最新的 origin/master 进行一次衍合操作然后再提交, 这样维护者就不需要做任何整合工作(注:实际上是把解决分支补丁 同最新主干代码之间冲突的责任,化转为由提交补丁的人来解决。) ,只需根据你提供的仓库地址作一次快进合并,或者直接采纳你提交 的补丁。 请注意,合并结果中最后一次提交所指向的快照,无论是通过衍合, 还是三方合并,都会得到相同的快照内容,只不过提交历史不同罢了 。衍合是按照每行的修改次序重演一遍修改,而合并是把最终结果合 在一起。
Git分支-分支的衍合 衍合的风险:一旦分支中的提交对象发布到公共仓库,就千万不要对 该分支进行衍合操作。 如果把衍合当成一种在推送之前清理提交历史的手段,而且仅仅衍合 那些尚未公开的提交对象,就没问题。如果衍合那些已经公开的提交 对象,并且已经有人基于这些提交对象开展了后续开发工作的话,就 会出现叫人沮丧的麻烦。
Git分支-参考资料 git官网 http://git-scm.com 基于git的源代码管理模型—git flow http://www.ituring.com.cn/article/56870