首页 > 代码库 > 《Pro Git》学习笔记

《Pro Git》学习笔记

1.Git远程模型示意图

70369b664f92cba09bf720bf6f22d160

  • Remote:远程仓库

  • Repository:本地仓库

  • Index:暂存区

  • workspace:当前工作区

2.取得Git仓库

2.1 初始化新仓库

    git init

2.2 从现有仓库克隆

    git clone git://github.com/xxx/xxx.git [dirName]

3.记录每次更新到仓库

3.1 文件状态变化周期

Image

3.2 检查当前文件的状态

    git status

3.3 跟踪新文件

    git add <fileName>

3.4 更新已放到暂存区但是之后被修改的文件

    git add <fileName>

3.5 查看已暂存和未暂存的更新

    git diff 查看工作区与暂存区的差异

    git diff 已暂存的文件与上次提交之间的差别

3.6 提交更新

    git commit 或者 git commit -m "具体说明"

3.7 跳过暂存区直接更新

    git commit -a -m "更新说明"

3.8 删除文件

    git rm <文件名>

    如果删除之前已经修改过并且放到了暂存区,那么使用-f选项来强制删除暂存区中的文件。该参数用来防止误删文件后丢失修改的内容。

3.9 移除对文件的跟踪但是不删除文件

    git rm --cached <文件名>

    删除的时候如果在“*”号前面加“\”那么就会递归删除当前目录下所有匹配的文件,例如:git rm \*.tmp。

3.10 移动文件

    git mv <文件1> <文件2> 相当于先删除文件1,然后增加文件2

4.查看提交的历史

   git log

   常见用法:

4.1 展开内容差异

    git log -p -2 (-p 展开每次提交的内容差异,用-2表示仅显示最近的两次更新)

4.2 仅显示简要的增改行数统计

    git log --stat

4.3 指定格式展示提交历史

    git log --pretty=oneline (将每个提交放在一行显示,还有short、full、fuller可以用)

4.4 定制记录格式

    git log --pretty=format:"%h - %an, %ar : %s" ,常用格式占位符的写法及意义为:

选项 说明
%H 提交对象(commit)的完整哈希字串
%h 提交对象的简短哈希字串
%T 树对象(tree)的完整哈希字串
%t 树对象的简短哈希字串
%P 父对象(parent)的完整哈希字串
%p 父对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用 -date= 选项定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者(committer)的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明

 

4.5 限制筛选条件

    git log --[n|since|after|until|author|committer] ,选项说明:

选项 说明
-(n) 仅显示最近的 n 条提交
--since, --after 仅显示指定时间之后的提交
--until, --before 仅显示指定时间之前的提交
--author 仅显示指定作者相关的提交
--committer 仅显示指定提交者相关的提交

5、撤销操作

5.1 修改最后一次提交

    git commit -m "initial commit"

    git add forgotten_file

    git commit --amend

5.2 取消已暂存的文件

    git reset HEAD <文件名>

5.3 取消对文件的修改

    git checktout -- <文件名>

6.远程仓库

6.1 查看当前远程仓库

    git remote [-v]

6.2 添加远程仓库

    git remote add <远程仓库名> <远程仓库地址>

6.3 从远程仓库抓取数据

    git fetch <远程仓库名>

    如果是克隆了一个仓库,此命令会自动将远程仓库归于 origin 名下。所以,git fetch origin 会抓取从你上次克隆以来别人上传到此远程仓库中的所有更新(或是上次 fetch 以来别人提交的更新)。有一点很重要,需要记住,fetch 命令只是将远端的数据拉到本地仓库,并不自动合并到当前工作分支,只有当你确实准备好了,才能手工合并。

    如果设置了某个分支用于跟踪某个远端仓库的分支(参见下节及第三章的内容),可以使用 git pull 命令自动抓取数据下来,然后将远端分支自动合并到本地仓库中当前分支。在日常工作中我们经常这么用,既快且好。实际上,默认情况下git clone 命令本质上就是自动创建了本地的 master 分支用于跟踪远程仓库中的 master 分支(假设远程仓库确实有 master 分支)。所以一般我们运行git pull,目的都是要从原始克隆的远端仓库中抓取数据后,合并到工作目录中的当前分支。

6.4 推送数据到远程仓库

    git push [远程仓库名] [分支名]

6.5 查看远程仓库信息

    git remote show [远程仓库名]

6.6 远程仓库的删除和重命名

    git remote rename <旧仓库名> <新仓库名>

7.打标签

    在发布某个软件版本的时候使用Git对某一时间点上的版本打上标签。

7.1 查看已有标签

    git tag [-l <标签模式>]

    例如:git tag -l ‘v1.4.2.*‘

7.2 新建标签

    Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。轻量级标签就像是个不会变化的分支,实际上它就是个指向特 定提交对象的引用。而含附注标签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证。一般我们都建议使用含附注型的标签,以便保留相关信息;当然,如果只是临时性加注标签,或者不需要旁注额外信息,用轻量级标签也没问题。

  含附注的标签:

    git tag -a <标签名> [-m <标签说明>]

    -a选项指定含附注类型,-m选项指定标签说明。

  轻量级标签:

    git tag <标签名>

7.3 签署标签

    利用私钥和GPG来签署标签。

    git tag -s <标签名> [-m <标签说明>]

7.4 验证标签

    git tag -v <标签名>

    需要有签署者的公钥存放在keyring中。

7.5 后期加注标签

    git tag -v <标签名> <校验和前几位>

7.6 分享标签

    git push <远程仓库名> <标签名>

8.分支理论

    commit对象指向包含各个文件blob对象索引的tree对象。Git分支的本质是个指向commit对象的可变指针,Git使用master作为分支的默认名字。Git通过创建一个新的分支指针来创建新的分支:git branch <分支名>。

    HEAD是一个指向正在工作中的本地分支的指针,可以将其当做当前分支的别名。

8.1 将HEAD切换到其他分支

    git checktout <分支名>

    切换分支的时候最好保持一个清洁的工作区域。

8.2 分支的新建与切换

    git checkout -b <新分支名>

    相当于:

    git branch <新分支名>

    git checkout <新分支名>

8.3 分支的合并

    假设要将分支hotfix合并到分支master,首先检出master分支,然后合并分支。

    git checkout master

    git merge hotfix

8.4 删除分支

    git branch -d <分支名>

8.5 查看所有分支

    git branch [-v|--merged|--on-merged]

    -v选项用来查看各分支最后一个commit的信息,--merged用来查看哪些分支已经并入当前分支。

8.6 利用分支来进行开发工作

    长期分支(master):例如master分支,保留完全稳定的代码,稳定的分支总是比较老旧。

    开发分支(develop):与master平行的专门用于后续开发或者稳定性测试的分支。

    特性分支(topic):短期的用来实现单一特性的分支。

Image

8.7 远程分支

    远程分支(remote branch)是对远程仓库中分支的索引。它们是无法移动的本地分支,只有在Git进行网络交互时才会更新。远程分支一般用<远程仓库名>/<分支名>这样的形式来表示。

    一次Git克隆会建立本地分支master和远程分支origin/master,它们都指向origin/master分支的最后一次提交。

    (1)同步远程服务器上的数据:git fetch origin。该命令首先找到origin服务器,从上面获取数据然后把origin/master的指针移动到最新的位置。

    (2)推送本地分支:git push <远程仓库名> <本地分支名>[:<远程分支名>]。如果不指定<远程分支名>,那么会使用与本地分支相同的名字。

    (3)值得注意的是,即使用fetch操作获取了新的远程分支,任然无法在本地编辑远程仓库的分支,如果要把远程分支的内容合并到当前分支,可以使用:git merge <远程仓库名>/<远程分支名>。

    (4)如果想要自己的可编辑分支,可以在远程分支的基础上分化出一个新的分支来:git checkout -b <新分支名> <远程仓库名>/<远程分支名>。从远程分支中checkout出来的本地分支称为跟踪分支,跟踪分支是一种和某个远程分支有直接联系的本地分支。在跟踪分支里输入 git push,Git会自行推断该向哪个服务器的哪个分支推送数据。同样在跟踪分支里运行 git pull 会获取远程索引并把它们的数据都合并到本地分支中来。

    (5)删除远程分支:git push <远程仓库名> :<远程分支名>

8.8 分支的衍合

    把一个分支整合到另一个分支的办法有两种:merge 和 rebase(暂译为衍合)。merge的原理是将两个分支最新的快照以及二者最新的共同祖先进行三方合并,然后生成一个新的提交对象。rebase的原理是:假设有两个分支 experiment 和 master,experiment是我们要衍合的分支。先回到两者的共同祖先,依据 experiment 后续的历次提交产生一系列补丁,然后以基底分支 master 的最后一个提交对象为新的出发点,逐个应用之前准备好的补丁文件,最后会产生一个新的合并提交对象,改写experiment的提交历史。其命令表示为:

    git checkout experiment

    git rebase master

Image

    rebase 和 merge 最后得到的快照内容是相同的,但 rebase 能够提供更为简洁的提交历史。一般我们使用衍合的目的,是想要得到一个能在远程分支上干净应用的补丁 — 比如某些项目你不是维护者,但想帮点忙的话,最好用衍合:先在自己的一个分支里进行开发,当准备向主项目提交补丁的时候,根据最新的 origin/master 进行一次衍合操作然后再提交,这样维护者就不需要做任何整合工作(译注:实际上是把解决分支补丁同最新主干代码之间冲突的责任,化转为由提交补丁的人来解决),只需根据你提供的仓库地址作一次快进合并,或者直接采纳你提交的补丁。

    衍合也可以放到其他分支进行,并不一定非得根据分化之前的分支。以图 3-31 的历史为例,我们为了给服务器端代码添加一些功能而创建了特性分支 server,后提交 C3 和 C4。然后又从 C3 的地方再增加一个 client 分支来对客户端代码进行一些相应修改,所以提交了 C8 和 C9。最后,又回到 server 分支提交了 C10。

Image

图 3-31. 从一个特性分支里再分出一个特性分支的历史。

    假设在接下来的一次软件发布中,我们决定先把客户端的修改并到主线中,而暂缓并入服务端软件的修改(因为还需要进一步测试)。这个时候,我们就可以把基于 server 分支而非 master 分支的改变(即 C8 和 C9),跳过 server 直接放到 master 分支中重演一遍,但这需要用 git rebase--onto 选项指定新的基底分支 master

    $ git rebase --onto master server client

    这好比在说:“取出 client 分支,找出 client 分支和 server 分支的共同祖先之后的变化,然后把它们在 master 上重演一遍”。是不是有点复杂?不过它的结果如图 3-32 所示,非常酷(译注:虽然 client 里的 C8, C9 在 C3 之后,但这仅表明时间上的先后,而非在 C3 修改的基础上进一步改动,因为 serverclient 这两个分支对应的代码应该是两套文件,虽然这么说不是很严格,但应理解为在 C3 时间点之后,对另外的文件所做的 C8,C9 修改,放到主干重演。):

Image

图 3-32. 将特性分支上的另一个特性分支衍合到其他分支。

  现在可以快进 master 分支了(见图 3-33):

    $ git checkout master
$ git merge client

Image

图 3-33. 快进 master 分支,使之包含 client 分支的变化。

    现在我们决定把 server 分支的变化也包含进来。我们可以直接把 server 分支衍合到 master,而不用手工切换到 server 分支后再执行衍合操作 — git rebase [主分支] [特性分支] 命令会先取出特性分支 server,然后在主分支 master 上重演:

    $ git rebase master server

    于是,server 的进度应用到 master 的基础上,如图 3-34 所示:

Image

图 3-34. 在 master 分支上衍合 server 分支。

    然后就可以快进主干分支 master 了:

    $ git checkout master
$ git merge server

    现在 clientserver 分支的变化都已经集成到主干分支来了,可以删掉它们了。最终我们的提交历史会变成图 3-35 的样子:

    $ git branch -d client    $ git branch -d server

Image

图 3-35. 最终的提交历史

    rebase的风险:一旦分支中的提交对象发布到公共仓库,就千万不要对该分支进行rebase操作。

《Pro Git》学习笔记