git知识点总结梳理

一、为什么要版本控制

其实版本控制的重要性不需要过多强调。我们想当然的就能理解他是很重要的。

如果你是位图形或网页设计师,可能会需要保存某一幅图片或页面布局文件的所有修订版本(这或许是你非常渴望拥有的功能),采用版本控制系统(VCS)是个明智的选择。 有了它你就可以将选定的文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态,你可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致怪异问题出现的原因,又是谁在何时报告了某个功能缺陷等等。 使用版本控制系统通常还意味着,就算你乱来一气把整个项目中的文件改的改删的删,你也照样可以轻松恢复到原先的样子。 但额外增加的工作量却微乎其微。

二、如何版本控制

方案一------本地版本控制

当我们考虑到版本控制问题的时候,首先会想到的就是本地版本控制,粗暴一些的手段就是我们把每个版本都保存一遍,做好标注,这是最无脑的,但是这样做消耗的存储空间会很离谱------尤其是到了项目后期,每次保存空间都很大。

为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。记录差异是版本控制的很常见的思路,这类系统(CVS、Subversion、Perforce、Bazaar 等等) 将它们存储的信息看作是一组基本文件和每个文件随时间逐步累积的差异 (它们通常称作 基于差异(delta-based) 的版本控制)。

其中最流行的一种叫做 RCS,现今许多计算机系统上都还看得到它的踪影。 RCS 的工作原理是在硬盘上保存补丁集(补丁是指文件修订前后的变化), 通过应用所有的补丁,可以重新计算出各个版本的文件内容。

方案二------集中化版本控制

我希望大家在学习的过程中要记住一点,git很重要的作用是团队协作,否则你对git的学习就会是浅尝辄止的,你所能做的事情也不过是最简单的本地版本控制就能完成的事情,这对于团队开发来说是完全不够的。比如现在我们思考一个问题,版本控制在本地的话,我们如何在不同设备上协同合作?

为了解决这个问题,**集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)**应运而生。诸如 CVS、Subversion 以及 Perforce 等。这些系统都有一个单一的集中管理的服务器,保存所有文件的修订版本。协同合作的人们通过客户端连接这台服务器,取出最新文件或者提交更新。

这个时候已经有类似仓库的概念了,集中管理的服务器就是一个仓库,大家每次只取出自己要改的文件,改完放回去就行了,其实我们在git里面要做的事情也是这个思路,但略有不同,希望大家能够注意到集中化版本控制系统和git的区别。

事分两面,有好有坏。 这么做最显而易见的缺点是中央服务器的单点故障。 如果宕机一小时,那么在这一小时内,谁都无法提交更新,也就无法协同工作。 如果中心数据库所在的磁盘发生损坏,又没有做恰当备份,毫无疑问你将丢失所有数据——包括项目的整个变更历史,只剩下人们在各自机器上保留的单独快照。 本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。

方案三------分布式版本管理系统

我认为这个问题的解决方法接近于粗暴,既然集中化版本控制可能会翻车,那我就每个人手里面握一份源码不就完了。之前每次都取出新文件来操作,现在直接整个仓库取下来,只要有一个人电脑是好的,我就能恢复代码。

三、git的特性(超级重要)

也许git的特性不影响你简单的使用git,但我觉得如果想真正的吃透,必须要简单了解他的基本特性和原理。

1、直接记录快照,而非差异比较

这个很关键,我觉得很多很多人都不会注意这点。

很多版本控制系统是基于差异的,但git是类似于本地版本控制那种风格,每当你提交更新或保存项目状态时,它基本上就会对当时的全部文件创建一个快照并保存这个快照的索引。Git 对待数据更像是一个 快照流,要注意如果文件没有改变,不会被重新存储,会保留一个链接指向之前保存过的文件,这样的优化能提高效率,这让git也结合了基于差异的思路。

这是 Git 与几乎所有其它版本控制系统的重要区别。 因此 Git 重新考虑了以前每一代版本控制系统延续下来的诸多方面。 Git 更像是一个小型的文件系统,提供了许多以此为基础构建的超强工具,而不只是一个简单的 VCS。 稍后我们在Git 分支讨论 Git 分支管理时,将探究这种方式对待数据所能获得的益处。

2、近乎所有操作都是本地执行

在 Git 中的绝大多数操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息。因为你在本地磁盘上就有项目的完整历史,所以大部分操作看起来瞬间完成。

3、保证完整性

Git 中所有的数据在存储前都计算校验和,然后以校验和来引用。 这意味着不可能在 Git 不知情时更改任何文件内容或目录内容。 这个功能建构在 Git 底层,是构成 Git 哲学不可或缺的部分。 若你在传送过程中丢失信息或损坏文件,Git 就能发现。

Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)

4、git一般只添加数据

你执行的 Git 操作,几乎只往 Git 数据库中 添加 数据。 你很难使用 Git 从数据库中删除数据,也就是说 Git 几乎不会执行任何可能导致文件不可恢复的操作。 同别的 VCS 一样,未提交更新时有可能丢失或弄乱修改的内容。但是一旦你提交快照到 Git 中, 就难以再丢失数据,特别是如果你定期的推送数据库到其它仓库的话。

5、三种状态

现在请注意,如果你希望后面的学习更顺利,请记住下面这些关于 Git 的概念。 Git 有三种状态,你的文件可能处于其中之一: 已提交(committed)已修改(modified)已暂存(staged)

  • 已修改表示修改了文件,但还没保存到数据库中。
  • 已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
  • 已提交表示数据已经安全地保存在本地数据库中。

这会让我们的 Git 项目拥有三个阶段:工作区、暂存区以及 Git 目录。

工作区、暂存区以及 Git 目录。

工作区就是我们平时修改文件的地方,修改之后如果我们可以将修改的内容暂存,这里注意,暂存是选择性的,比方说一些本地的配置文件,我们就不需要暂存。暂存区是一个文件,保存了下次将要提交的文件列表信息,一般在 Git 仓库目录中。 按照 Git 的术语叫做“索引”,不过一般说法还是叫“暂存区”。暂存区的内容可以提交到git仓库目录,提交到仓库目录就是一个新的版本,这里提交到的是本地的仓库。

如果 Git 目录中保存着特定版本的文件,就属于 已提交 状态。 如果文件已修改并放入暂存区,就属于 已暂存 状态。 如果自上次检出后,作了修改但还没有放到暂存区域,就是 已修改 状态。

四、安装git

1、CentOS

$ sudo dnf install git-all

2、Ubuntu

$ sudo apt install git-all

3、macOS

$ git --version

4、Windows

官方版本可以在 Git 官方网站下载。 打开 https://git-scm.com/download/win,下载会自动开始。 要注意这是一个名为 Git for Windows 的项目(也叫做 msysGit),和 Git 是分别独立的项目。

另一个简单的方法是安装 GitHub Desktop。https://desktop.github.com/

五、配置git

如果使用的是GitHub Desktop就不用配置用户信息了,如果是命令行的话可以配置一下用户信息。

$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com

如果是单独修改某个仓库,去掉–global即可

查看现在已经有的所有配置,找个仓库

$ git config --list

查看某项的配置

git config <key>

查看所有配置的来源

$ git config --list --show-origin

查看某个配置的来源

$ git config --show-origin rerere.autoUpdate
file:/home/johndoe/.gitconfig	false

六、git基础

1、获取仓库

1️⃣ 初始化仓库

先切到想初始化为git仓库的文件夹

$ git init

2️⃣ 克隆仓库

获取一个本地的链接库

git clone <url>

3️⃣ 更新仓库

文件状态

首先是两大类,已跟踪文件和未跟踪文件,我们刚初始化仓库的时候所有文件都是未跟踪文件。

可以用 git status 命令查看哪些文件处于什么状态。

$ git status
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

这个输出代表当前工作目录是干净的,就是指,没有新增的未跟踪文件,也没有修改了的已跟踪文件。

跟踪新文件

使用命令 git add 开始跟踪一个文件。

这个命令有两层作用,可以用来跟踪新的文件,也可以用来将已跟踪文件放入暂存区(就是修改之后也要add)。

$ git add README

在新仓库里面可以先执行以下命令跟踪初始的文件。

$ git add *.c
$ git add LICENSE
忽略文件

我们本地的配置文件肯定不希望暂存,因为没必要提交到仓库,但git每次都会提示,所以我们要忽略掉这些文件。这时我们可以创建一个.gitignore文件。

GitHub 有一个十分详细的针对数十种项目及语言的 .gitignore 文件列表, 你可以在 https://github.com/github/gitignore 找到它。

查看修改

① 工作目录中当前文件和暂存区域快照之间的差异。 也就是修改之后还没有暂存起来的变化内容。

$ git diff

② 比对已暂存文件与最后一次提交的文件差异

$ git diff --staged
或
$ git diff --cached

③ 比对当前内容和最近一次提交。

$ git diff HEAD
提交更新

然后暂存区的内容可以提交到本地仓库

git commit -m "<注释>"

注释就是你做的修改

跳过add,自动把所有已跟踪文件add并提交

git commit -a -m "<注释>"
移除文件

已经跟踪的文件如果被删除,会提示“Changes not staged for commit”

使用以下命令将文件移出暂存区

$ git rm --cached README

直接删除文件

$ git rm README

2、查看提交历史

git log

3、撤销

如果你漏添加了几个文件,可以通过这个命令再次提交,会顶替掉上次的提交。

$ git commit --amend

撤销暂存

git reset HEAD <file>..
git revert 

撤销修改,git会用最新的提交的版本覆盖它。

git checkout -- <file>...

🎾Tip 1: git reset、git revert 和 git checkout 有什么区别?

首先,从 commit 层面来说:

  • git reset 可以将一个分支的末端指向之前的一个 commit。然后再下次 git 执行垃圾回收的时候,会把这个 commit 之后的 commit 都扔掉。git reset 还支持三种标记,用来标记 reset 指令影响的范围:

    • –mixed:会影响到暂存区和历史记录区。也是默认选项;

    • –soft:只影响历史记录区;

    • –hard:影响工作区、暂存区和历史记录区。

注意:因为 git reset 是直接删除 commit 记录,从而会影响到其他开发人员的分支,所以不要在公共分支(比如 develop)做这个操作。

  • git checkout 可以将 HEAD 移到一个新的分支,并更新工作目录。因为可能会覆盖本地的修改,所以执行这个指令之前,你需要 stash 或者 commit 暂存区和工作区的更改。

  • git revert 和 git reset 的目的是一样的,但是做法不同,它会以创建新的 commit 的方式来撤销 commit,这样能保留之前的 commit 历史,比较安全。另外,同样因为可能会覆盖本地的修改,所以执行这个指令之前,你需要 stash 或者 commit 暂存区和工作区的更改。

然后,从文件层面来说:

  • git reset 只是把文件从历史记录区拿到暂存区,不影响工作区的内容,而且不支持 –mixed、–soft 和 –hard。
  • git checkout 则是把文件从历史记录拿到工作区,不影响暂存区的内容。
  • git revert 不支持文件层面的操作。

4、远程仓库

远程仓库是托管在网络上的项目的版本库。注意一个本地仓库也可以对应多个远程仓库

1️⃣ 查看仓库

查看已经配置的远程仓库

git remote

查看远程仓库的跟多信息

git remote show <remote>

2️⃣ 添加仓库

有两种方式添加远程仓库,一种是

git clone url

这个命令可以把仓库拉到本地的同时自行添加远程仓库。

还有一种是

git remote add <shortname> <url>

这个命令可以提供一个简写的url,之后可以用shortname代替url。

3️⃣ 抓取和推送

从远程仓库中获取所有本地没有的数据

$ git fetch <remote>

这个命令只会把远程仓库下载到本地仓库,它并不会自动合并或修改你当前的工作。 当准备好时你必须手动将其合并入你的工作。

推送到远程仓库

git push <remote> <branch>

这个命令的前提是你有写入权限并且之前没有人推送过。如果别人已经推送过,你需要先抓取并合并然后再推送。

4️⃣ 重命名和删除

重命名命令

$ git remote rename 当前名字 改成的名字

删除命令

$ git remote remove 仓库名

5、标签

Git 支持两种标签:轻量标签(lightweight)与附注标签(annotated)。

轻量标签很像一个不会改变的分支——它只是某个特定提交的引用。

而附注标签是存储在 Git 数据库中的一个完整对象, 它们是可以被校验的,其中包含打标签者的名字、电子邮件地址、日期时间, 此外还有一个标签信息,并且可以使用 GNU Privacy Guard (GPG)签名并验证。 通常会建议创建附注标签,这样你可以拥有以上所有信息。但是如果你只是想用一个临时的标签, 或者因为某些原因不想要保存这些信息,那么也可以用轻量标签。

1️⃣ 创建标签

创建附注标签

$ git tag -a v1.4 -m 注释

创建轻量标签就是不提供 -a-s-m 选项

$ git tag v1.4-lw

后期打标签需要在命令的末尾指定提交的校验和(或部分校验和)

$ git tag -a v1.2 部分校验和

查看历史提交的校验和

$ git log --pretty=oneline

2️⃣ 查看标签

$ git show 标签

3️⃣ 共享标签

将标签推送到网络共享仓库

git push origin <tagname>

一次性推送多个标签

$ git push origin --tags

4️⃣ 删除标签

git tag -d <tagname>

6、别名

为git命令设置别名

$ git config --global alias.ci commit

这意味着,当要输入 git commit 时,只需要输入 git ci

七、分支

如果你知识自己写一些小东西的话,到前六章就足够你使用了,如果你想来点硬货 ------ come on!

1、分支简介

有人把 Git 的分支模型称为它的“必杀技特性”,也正因为这一特性,使得 Git 从众多版本控制系统中脱颖而出。 为何 Git 的分支模型如此出众呢? Git 处理分支的方式可谓是难以置信的轻量,创建新分支这一操作几乎能在瞬间完成,并且在不同分支之间的切换操作也是一样便捷。 与许多其它版本控制系统不同,Git 鼓励在工作流程中频繁地使用分支与合并,哪怕一天之内进行许多次。 理解和精通这一特性,你便会意识到 Git 是如此的强大而又独特,并且从此真正改变你的开发方式。

Git 的分支,其实本质上仅仅是指向提交对象的可变指针。

检出时 HEAD 随之移动。

项目分叉历史。

2、为什么分支?

想象一个场景,你已经完成了一个项目第一版的开发,他上线了。然后你着手去写第二版,这个工作可能要好几个月。这个时候第一版发现了一个巨大的bug,你怎么解决这个问题?

此时我们可以创建一个新的分支,从第一版那里修改,修改完之后先填上这个bug。

基于  分支的紧急问题分支(hotfix branch)。

3、创建分支

要注意直接创建分支并不会切换分支

$ git branch 分支名

切换分支

$ git checkout 分支名

创建并切换分支

git checkout -b <newbranchname>

我们已经了解了分支就是指针,那我们就可以查看分支指向的对象

$ git log --oneline --decorate

这个命令可以查看分支指向的对象的校验和

4、查看分支

这个命令可以输出你的提交历史、各个分支的指向以及项目的分支分叉情况。

git log --oneline --decorate --graph --all

5、合并分支(重要的)

git merge 合并的分支名

这里有两种情况,一种是两个分支在同一条线上的,比如说刚刚做完修复之后,把修复合并入主分支。这种情况在git里面的原理其实就是指针移动到合并的分支,也就是“快进(fast-forward)”。第二种情况是三方合并如果合并的两个分支不在同一个线上,会和公共祖先一起三线合并。比如我们修复完之后,合并第二版的情况。

冲突解决

有时候合并操作不会如此顺利。 如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并它们。

任何因包含合并冲突而有待解决的文件,都会以未合并状态标识出来。 Git 会在有冲突的文件中加入标准的冲突解决标记,这样你可以打开这些包含冲突的文件然后手动解决冲突。 出现冲突的文件会包含一些特殊区段,看起来像下面这个样子:

<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
 please contact us at support@github.com
</div>
>>>>>>> iss53:index.html

这表示 HEAD 所指示的版本(也就是你的 master 分支所在的位置,因为你在运行 merge 命令的时候已经检出到了这个分支)在这个区段的上半部分(======= 的上半部分),而 iss53 分支所指示的版本在 ======= 的下半部分。 为了解决冲突,你必须选择使用由 ======= 分割的两部分中的一个,或者你也可以自行合并这些内容。 例如,你可以通过把这段内容换成下面的样子来解决冲突:

<div id="footer">
please contact us at email.support@github.com
</div>

6、分支开发工作流

1️⃣ 长期分支

可以用多个分支来开发多个版本,稳定版,测试版等等。

2️⃣ 主题分支

用来实现不同的单一特性,当你有一个新的想法时可以开一个新的主题分支来解决这个问题。

7、远程分支

远程分支和本地分支可以是同名不同内容的,这个很好理解,就是别人更新了内容,这时候合并的话也是三方合并,其实逻辑和之前的合并是一样的。

推送到远程分支

git push <remote> <branch>

拉取远程分支

git pull

删除远程分支

$ git push origin --delete serverfix

🎈Tip 1: Git Flow 基本流程

这个工作流程给我们提供了一个思路,但不是我们必须严格遵守的

Git Flow 是由 Vincent Driessen 提出的一个 git操作流程标准。包含如下几个关键分支:

8、变基

变基在我们使用当中和合并很像,但是最终的历史记录是不同的

$ git rebase 分支名

分叉的提交历史。

将  中的修改变基到  上。

但是在多人合作的时候使用变基可能会影响别人的修改。

总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史, 从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。

八、分布式工作流程

1、工作模式

集中式工作流

这种工作方式类似于集中化版本控制。

集中式工作流。

管理者工作流

这种模式适用于开源项目等

  1. 项目维护者推送到主仓库。
  2. 贡献者克隆此仓库,做出修改。
  3. 贡献者将数据推送到自己的公开仓库。
  4. 贡献者给维护者发送邮件,请求拉取自己的更新。
  5. 维护者在自己本地的仓库中,将贡献者的仓库加为远程仓库并合并修改。
  6. 维护者将合并后的修改推送到主仓库。

集成管理者工作流。

主管与副管工作流

这其实是多仓库工作流程的变种。 一般拥有数百位协作开发者的超大型项目才会用到这样的工作方式

  1. 普通开发者在自己的主题分支上工作,并根据 master 分支进行变基。 这里是主管推送的参考仓库的 master 分支。
  2. 副主管将普通开发者的主题分支合并到自己的 master 分支中。
  3. 主管将所有副主管的 master 分支并入自己的 master 分支中。
  4. 最后,主管将集成后的 master 分支推送到参考仓库中,以便所有其他开发者以此为基础进行变基。

主管与副主管工作流。

2、向一个项目贡献

1️⃣ 提交准则

你的提交不应该包含任何空白错误。

在提交前,运行 git diff --check,它将会找到可能的空白错误并将它们为你列出来。

尝试让每一个提交成为一个逻辑上的独立变更集。

如果可以,尝试让改动可以理解——不要在整个周末编码解决五个问题,然后在周一时将它们提交为一个巨大的提交。 即使在周末期间你无法提交,在周一时使用暂存区域将你的工作最少拆分为每个问题一个提交,并且为每一个提交附带一个有用的信息。 如果其中一些改动修改了同一个文件,尝试使用 git add --patch 来部分暂存文件(在 交互式暂存 中有详细介绍)。 不管你做一个或五个提交,只要所有的改动是在同一时刻添加的,项目分支末端的快照就是独立的,使同事开发者必须审查你的改动时尽量让事情容易些。

优质的提交信息

一般情况下,信息应当以少于 50 个字符(25个汉字)的单行开始且简要地描述变更,接着是一个空白行,再接着是一个更详细的解释。 Git 项目要求一个更详细的解释,包括做改动的动机和它的实现与之前行为的对比——这是一个值得遵循的好规则。 使用指令式的语气来编写提交信息,比如使用“Fix bug”而非“Fixed bug”或“Fixes bug”。

3、维护一个项目

1️⃣ 应用补丁

应用补丁的两个命令

git apply
git am

2️⃣ 检出远程分支

如果贡献者有自己的版本库,可以直接拉取他的分支。

3️⃣ 确定引入的内容

检查master分支尚未包含的提交

$ git log contrib --not master

增加 -p 可以附加差异

比较新拉取的分支和master的差异

$ git diff master

如果master分支前移了,可以对比master的祖先和新分支的差异

$ git diff master...contrib

4️⃣ 合并分支

如果你在进行大量的合并或变基,或维护一个长期的主题分支,Git 提供的一个叫做“rerere”的功能会有一些帮助。

Rerere 是“重用已记录的冲突解决方案(reuse recorded resolution)”的意思——它是一种简化冲突解决的方法。 当启用 rerere 时,Git 将会维护一些成功合并之前和之后的镜像,当 Git 发现之前已经修复过类似的冲突时, 便会使用之前的修复方案,而不需要你的干预。
$ git config --global rerere.enabled true

5️⃣ 为发布发布打标签

$ git tag -s v1.5 -m 'my signed 1.5 tag'

6️⃣ 生成一个构建号

$ git describe master

7️⃣ 发布一个构建

$ git archive master --prefix='project/' --format=zip > `git describe master`.zip

8️⃣ 制作提交简报

这份整洁的总结包括了自 v1.0.1 以来的所有提交,并且已经按照作者分好组,你可以通过电子邮件将其直接发送到列表中。

$ git shortlog --no-merges master --not v1.0.1

九、git工具

1、选择修订版本

1️⃣ 简单的SHA-1

Git 十分智能,你只需要提供 SHA-1 的前几个字符就可以获得对应的那次提交, 当然你提供的 SHA-1 字符数量不得少于 4 个,并且没有歧义——也就是说, 当前对象数据库中没有其它对象以这段 SHA-1 开头。

许多人觉得他们的仓库里有可能出现两个不同的对象其 SHA-1 值相同。 然后呢?

如果你真的向仓库里提交了一个对象,它跟之前的某个 不同 对象的 SHA-1 值相同, Git 会发现该对象的散列值已经存在于仓库里了,于是就会认为该对象被写入,然后直接使用它。 如果之后你想检出那个对象时,你将得到先前那个对象的数据。

但是这种情况发生的概率十分渺小。 SHA-1 摘要长度是 20 字节,也就是 160 位。 2^80 个随机哈希对象才有 50% 的概率出现一次冲突 (计算冲突机率的公式是 p = (n(n-1)/2) * (1/2^160)) )。 2^80 是 1.2 x 10^24,也就是一亿亿亿,这是地球上沙粒总数的 1200 倍。

举例说一下怎样才能产生一次 SHA-1 冲突。 如果地球上 65 亿个人类都在编程,每人每秒都在产生等价于整个 Linux 内核历史(650 万个 Git 对象)的代码, 并将之提交到一个巨大的 Git 仓库里面,这样持续两年的时间才会产生足够的对象, 使其拥有 50% 的概率产生一次 SHA-1 对象冲突, 这比你编程团队的成员同一个晚上在互不相干的意外中被狼袭击并杀死的机率还要小。

2️⃣ 分支引用

如果特定提交是某个分支的顶端提交,可以直接用分支的名称

3️⃣ 引用日志

查看引用日志可以得到当前分支指向的提交历史。引用日志只存在于本地仓库

$ git reflog
734713b HEAD@{0}: commit: fixed refs handling, added gc auto, updated
d921970 HEAD@{1}: merge phedders/rdocs: Merge made by the 'recursive' strategy.
1c002dd HEAD@{2}: commit: added some blame and merge stuff
1c36188 HEAD@{3}: rebase -i (squash): updating HEAD
95df984 HEAD@{4}: commit: # This is a combination of two commits.
1c36188 HEAD@{5}: rebase -i (squash): updating HEAD
7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD

你可以使用 @{n} 来引用 reflog 中输出的提交记录。

$ git show HEAD@{5}

然后就可以玩出花了,比如查看master昨天指向了哪个提交

$ git show master@{yesterday}

4️⃣ 祖先引用

引用的结尾加上^代表是该提交的上一次提交

$ git show HEAD^

在 Windows 的 cmd.exe 中,^ 是一个特殊字符,因此需要区别对待。 你可以双写它或者将提交引用放在引号中:

$ git show HEAD^     # 在 Windows 上无法工作
$ git show HEAD^^    # 可以
$ git show "HEAD^"   # 可以

5️⃣ 提交区间

双点

选出在一个分支而不在另一个分支的提交

在 experiment 分支中而不在 master 分支中的提交

$ git log master..experiment

查看你即将推送到远端的内容

$ git log origin/master..HEAD

以下三个命令是等价的

$ git log refA..refB
$ git log ^refA refB
$ git log refB --not refA

三点

查看被一个分支包含而不被另外一个分支包含的提交

$ git log --left-right master...experiment

2、交互式暂存

这个在命令行里还挺复杂,但是在GitHub desktop里面简直白给,最重要的是要有这个思想。

3、贮藏与清理

有时,当你在项目的一部分上已经工作一段时间后,所有东西都进入了混乱的状态, 而这时你想要切换到另一个分支做一点别的事情。 问题是,你不想仅仅因为过会儿回到这一点而为做了一半的工作创建一次提交。 针对这个问题的答案是 git stash 命令。

1️⃣ 贮藏工作

贮藏

$ git stash

应用贮藏

$ git stash apply

2️⃣ 从贮藏创建一个分支

如果有贮藏的分支你拿来工作了一段时间,可能就在应用的时候会有冲突,这时可以考虑从贮藏创建一个分支然后合并。

git stash branch <new branchname>

3️⃣ 清理工作目录

对于工作目录中一些工作或文件,你想做的也许不是贮藏而是移除。

git clean

清理工作目录有一些常见的原因,比如说为了移除由合并或外部工具生成的东西, 或是为了运行一个干净的构建而移除之前构建的残留。

你需要谨慎地使用这个命令,因为它被设计为从工作目录中移除未被追踪的文件。 如果你改变主意了,你也不一定能找回来那些文件的内容。 一个更安全的选项是运行 git stash --all 来移除每一样东西并存放在栈中。

移除工作目录中所有未追踪的文件以及空的子目录。

git clean -f -d

如果只是想要看看它会做什么,可以使用 --dry-run-n 选项来运行命令, 这意味着“做一次演习然后告诉你 将要 移除什么”。

$ git clean -d -n
Would remove test.o
Would remove tmp/

默认情况下,git clean 命令只会移除没有忽略的未跟踪文件。 任何与 .gitignore 或其他忽略文件中的模式匹配的文件都不会被移除。 如果你也想要移除那些文件,例如为了做一次完全干净的构建而移除所有由构建生成的 .o 文件, 可以给 clean 命令增加一个 -x 选项。

如果不知道 git clean 命令将会做什么,在将 -n 改为 -f 来真正做之前总是先用 -n 来运行它做双重检查。 另一个小心处理过程的方式是使用 -i 或 “interactive” 标记来运行它。

4、签署工作

签署标签与提交很棒,但是如果决定在正常的工作流程中使用它,你必须确保团队中的每一个人都理解如何这样做。 如果没有,你将会花费大量时间帮助其他人找出并用签名的版本重写提交。 在采用签署成为标准工作流程的一部分前,确保你完全理解 GPG 及签署带来的好处。

5、搜索

从提交历史、工作目录、甚至索引中查找一个字符串或者正则表达式。

git grep 搜索内容

行日志搜索是另一个相当高级并且有用的日志搜索功能。 在 git log 后加上 -L 选项即可调用,它可以展示代码中一行或者一个函数的历史。

例如,假设我们想查看 zlib.c 文件中git_deflate_bound 函数的每一次变更, 我们可以执行 git log -L :git_deflate_bound:zlib.c。 Git 会尝试找出这个函数的范围,然后查找历史记录,并且显示从函数创建之后一系列变更对应的补丁。

6、重写历史

修改最近一次提交

$ git commit --amend

上面这条命令会将最后一次的提交信息载入到编辑器中供你修改。 当保存并关闭编辑器后,编辑器会将更新后的提交信息写入新提交中,它会成为新的最后一次提交。

修改多个提交信息

可以通过给 git rebase 增加 -i 选项来交互式地运行变基。

例如,如果想要修改最近三次提交信息,或者那组提交中的任意一个提交信息, 将想要修改的最近一次提交的父提交作为参数传递给 git rebase -i 命令,即 HEAD~2^HEAD~3

$ git rebase -i HEAD~3

需要重点注意的是相对于正常使用的 log 命令,这些提交显示的顺序是相反的。 运行一次 log 命令,会看到类似这样的东西:

$ git log --pretty=format:"%h %s" HEAD~3..HEAD
a5f4a0d added cat-file
310154e updated README formatting and added blame
f7f3f6d changed my name a bit

注意其中的反序显示。 交互式变基给你一个它将会运行的脚本。 它将会从你在命令行中指定的提交(HEAD~3)开始,从上到下的依次重演每一个提交引入的修改。 它将最旧的而不是最新的列在上面,因为那会是第一个将要重演的。

你需要修改脚本来让它停留在你想修改的变更上。 要达到这个目的,你只要将你想修改的每一次提交前面的 ‘pick’ 改为 ‘edit’。 例如,只想修改第三次提交信息,可以像下面这样修改文件:

edit f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

当保存并退出编辑器时,Git 将你带回到列表中的最后一次提交,把你送回命令行并提示以下信息:

$ git rebase -i HEAD~3
Stopped at f7f3f6d... changed my name a bit
You can amend the commit now, with

       git commit --amend

Once you're satisfied with your changes, run

       git rebase --continue

这些指令准确地告诉你该做什么。 输入

$ git commit --amend

修改提交信息,然后退出编辑器。 然后,运行

$ git rebase --continue

这个命令将会自动地应用另外两个提交,然后就完成了。 如果需要将不止一处的 pick 改为 edit,需要在每一个修改为 edit 的提交上重复这些步骤。 每一次,Git 将会停止,让你修正提交,然后继续直到完成。

重新排序提交

也可以使用交互式变基来重新排序或完全移除提交。

如果想要移除 “added cat-file” 提交然后修改另外两个提交引入的顺序,可以将变基脚本从这样:

pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

改为这样:

pick 310154e updated README formatting and added blame
pick f7f3f6d changed my name a bit

当保存并退出编辑器时,Git 将你的分支带回这些提交的父提交,应用 310154e 然后应用 f7f3f6d,最后停止。 事实修改了那些提交的顺序并完全地移除了 “added cat-file” 提交。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值