目录

你将花费你生命中的若干小时来阅读有关 Git 的相关内容。让我们用几分钟时间来介绍下我们将给你讲解的内容。 下面是本书正文十章和附录三章的快速总结。

第一章,我们将介绍版本控制系统(VCSs)和 Git 的基本概念——不涉及技术内容,仅仅是什么是 Git, 为什么它会成为 VCSs 大家庭中的一员,它与其它 VCSs 的区别,以及为什么那么多人都在使用 Git。然后,我们将介绍如何下载 Git 以及如果你的系统没有安装 Git,如何为第一次运行做准备。

第二章,我们将阐述 Git 的基本使用——包含你在使用 Git 时可能遇到的 80% 的情形。通过阅读本章,你应该 能够克隆仓库、查看项目历史、修改文件和贡献更改。如果本书在此刻自燃,你应该已经能够使用已经学到的漂亮 有用的 Git 知识获取到另外一份拷贝。

第三章关注于 Git 的分支模型。分支模型通常被认为是 Git 的杀手级特性。这里,你将学习到究竟是什么让 Git与众不同。学习完本章,你可能需要一段时间来思考,在 Git 分支成为你的生活的一部分之前,你到底是如何生活的。

第四章关注于服务器端的 Git。本章面向那些希望在你自己的组织或个人服务器搭建用于合作的 Git 的读者。如果你希望让别人处理这些事务,我们也会探讨一些托管选项。

第五章将阐述多种分布式工作流的细节,以及如何使用 Git 实现它们。学习完本章,你应该能够在多个远程仓库 之间游刃有余,通过电子邮件使用 Git,熟练地处理多个远程分支和合作者贡献的补丁。

第六章介绍 GitHub 托管服务以及深层次的工具。我们将涵盖注册与账户管理,创建和使用 Git 仓库,贡献项目的 普通工作流以及接受他人的贡献,GitHub 的可编程接口和那些能够让你的生活变得更简单的小技巧。

第七章关于 Git 的高级命令。你将学习到一些高级主题,诸如掌握可怕的“reset”命令,使用二分搜索识别错误,编辑 历史,细节版本选择等等。本章的介绍将丰富你的 Git 知识,让你成为一个真正的大师。

第八章关于 Git 环境的自定义配置,包括设置用于增强或促进自定义策略的钩子脚本以及按照你所需要的方式进行 工作的环境配置。我们还会介绍构建你自己的脚本集,以增强自定义提交策略。

第九章对比 Git 和其它 VCSs,包括在 Subversion(SVN)的世界使用 Git 以及从其它 VCSs 迁移到 Git。很多组织 仍在使用 SVN,并且也没有计划改变,此时,你将了解到 Git 不可思议的能力——本章将展示,在你不得不 使用 SVN 服务器 的时候如何协同合作。我们还将介绍如何从不同系统导入项目,以便你能够全身心投入 Git 的怀抱。

第十章深入 Git 阴暗而漂亮的实现细节。现在,你已经知道所有有关 Git 的知识,能够熟练运用 Git 的强大优雅的功能。 接下来,你可以继续学习 Git 如何存储对象、Git 的对象模型是怎样的、打包文件的细节、服务器协议等更多知识。 本书自始至终都将引用本章的内容,以便你能够在当时就可以深入了解。但是,如果你像我们一样希望深入学习技术细节, 你可能想先阅读第十章。我们将选择权交给你。

附录 A,我们学习多个在特定环境中使用 Git 的实例。我们涵盖多个你可能会使用 Git 的多个 GUI 和 IDE 编程环境, 这些都可以由你自己选择。如果你想在 shell、Visual Studio 或 Eclipse 中使用 Git,请阅读本章。

附录 B,我们探讨通过类似 libgit2 和 JGit 的工具编写 Git 脚本、扩展 Git。如果你对编写复杂、快速的自定义工具感兴趣, 需要了解 Git 的底层访问,本章就是你所需要了解的。

最后在附录 C,我们一次性浏览 Git 的所有主要命令,复习在本书中介绍的内容,回忆我们能够使用这些命令做什么。 如果你需要知道本书中我们使用了哪些特定 Git 命令,你可以在这里查阅。

安装Git

Git基本设置

1. 设置全局用户信息

当安装完 Git 应该做的第一件事就是设置你的用户名称与邮件地址。 这样做很重要,因为每一个 Git 的提交都会使用这些信息,并且它会写入到你的每一次提交中,不可更改:

  1. git config --global user.name "yongzhilu"
  2. git config --global user.email "yongzhi1u@163.com"

2. 设置本地用户信息

如果不想用全局的用户信息,且想给当前项目设置设置用户,可以设置本地用户信息

  1. cd my-project
  2. # 不使用global选项,则设置的用户信息为本地用户信息
  3. git config user.name "yongzhilu"
  4. git config user.email "yongzhi1u@163.com"


3. 查看git配置信息

  1. git config --list

注:

  1. 如果全局访问git config —list,则会列出全局下的所有git配置信息
  2. 如果在当前项目目录下访问git config —list,则会列出全局和本地的所有配置信息(你可能会看到重复的变量名,因为 Git 会从不同的文件中读取同一个配置(例如:/etc/gitconfig 与 ~/.gitconfig)。 这种情况下,Git 会使用它找到的每一个变量的最后一个配置。)
  1. # 你可以通过输入 git config <key>: 来检查 Git 的某一项配置
  2. # 如果某一个选项(例如:user.email)在全局和本地都有配置,可以用下面的命令查看当前项目生效的配置值是什么
  3. git config user.email

本地Git仓库

  1. 配置并初始化仓库
  2. 开始/停止跟踪(track)文件
  3. 暂存(stage)或提交(commit)更改
  4. 忽略文件
  5. 撤销操作
  6. 浏览历史版本
  7. 对比版本差异
  8. 向远程仓库推送
  9. 从远程仓库拉取文件

1. 获取Git仓库

1.1 在现有目录中初始化仓库

如果你打算使用 Git 来对现有的项目进行管理,你只需要进入该项目目录并输入:

  1. git init

该命令将创建一个名为 .git 的子目录,这个子目录含有你初始化的 Git 仓库中所有的必须文件,这些文件是
Git 仓库的骨干。 但是,在这个时候,我们仅仅是做了一个初始化的操作,你的项目里的文件还没有被跟踪。

1.2 克隆现有的仓库

如果你想获得一份已经存在了的 Git 仓库的拷贝,比如说,你想为某个开源项目贡献自己的一份力,这时就要用到 git clone 命令。
当你执行 git clone 命令的时候,默认配置下远程 Git 仓库中的每一个文件的每一个版本都将被拉取下来。

克隆仓库的命令格式是 git clone

  1. git clone https://github.com/libgit2/libgit2

如果你想在克隆远程仓库的 时候,自定义本地仓库的名字,你可以使用如下命令:

  1. # git clone <url> <name>
  2. git clone https://github.com/libgit2/libgit2 mylibgit

Git 支持多种数据传输协议。 上面的例子使用的是 https:// 协议,不过你也可以使用 git:// 协议或者使用
SSH 传输协议,比如 user@server:path/to/repo.git 。

1.3 文件跟踪

你可通过 git add 命令来实现对指定文件的跟踪

  1. git add *
  2. git add a.txt

git add 命令使用文件或目录的路径作为参数;如果参数是目录的路径,该 命令将递归地跟踪该目录下的所有文件。

  1. git add package

2. 本地Git仓库状态管理

工作目录中除已跟踪文件以外的所有其它文件都属于未跟踪文件,它们既不存在于上次快照的记 录中,也没有放入暂存区 初次克隆某个仓库的时候,工作目录中的所有文件都属于已跟踪文件,并处于未修 改状态。

image.png

2.1 检查当前文件状态

要查看哪些文件处于什么状态,可以用 git status 命令。
该命令还显示了当前所在 分支,并告诉你这个分支同远程服务器上对应的分支没有偏离。

  1. git status
  1. nothing to commit, working directory clean: Staged

这说明你现在的工作目录相当干净。换句话说,所有已跟踪文件在上次提交后都未被更改过。
image.png

  1. Untracned files: Untracked

说明有未跟踪的文件(未被git管理的文件)
image.png

  1. Changes not staged for commit : Unmodified

文件 README.md 出现在 Changes not staged for commit 这行下面,说明已跟踪文件的内容发生了变化,但还没有放到暂存区。
要暂存这次更新,需要运行 git add 命令。
image.png

  1. Changes to be commited : Modified

说明有已修改待提交的文件
image.png

2.2 一种特殊的情况

image.png

2.3 状态简览

image.png

??:还没有被跟踪,没有 add,陌生文件
A:新添加到暂存区的文件/直接 add 进来的新文件/但是没有 commit
左M:工作区的文件,修改了并且已经 add 进暂存区,但是没有 commit
右M:工作区的文件,修改了但是没有 add 进暂存区
MM:工作区的文件,commit 到暂存区后又在工作区修改过
右D:从工作区删除,但是没有 add 进暂存区
左D:从工作区删除,并且 add 进暂存区,但是没有 commit

3. 忽略文件 .gitignore

一般我们总会有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。
通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件模式。
要养成一开始就设置好 .gitignore 文件的习惯,以免将来误提交这类无用的文件。

3.1 格式规范

• 所有空行或者以 # 开头的行都会被 Git 忽略。 • 可以使用标准的 glob 模式匹配。 • 匹配模式可以以(/)开头防止递归。 • 匹配模式可以以(/)结尾指定目录。 • 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。

所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。
① 星号( * )匹配零个或多个任意字符;
[abc] 匹配 25任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);
③ 问号( ? )只匹配一个任意字符;
④ 如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配 (比如 [0-9] 表示匹配所有 0 到 9 的数字)
⑤ 使用两个星号( ** ) 表示匹配任意中间目录,比如a/**/z 可以匹配 a/z, a/b/z 或 a/b/c/z等。

3.2 .gitignore举例

image.png

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

4. 查看diff

你想知道具体修改了什么地方,可以用 git diff 命令。
① git diff 比较的是 工作目录中当前文件 暂存区域快照 之间的差异, 也就是修改之后还没有暂存起来的变化内容。
② 如果想知道 暂存区快照 本地仓库 之间的差异,可以使用 git diff —staged 命令
image.png

5. 提交更新

git commit
这种方式会启动文本编辑器以便输入本次提交的说明。 (默认会启用 shell 的环境变量 $EDITOR 所指定的软件,一般都是 vim 或 emacs。当然也可以按照 起步 介绍的方式,使用 git config —global core.editor 命令设定你喜欢的编辑软件。)

git commit -v
如果想要更详细的对修改了哪些内容的提示,可以用 -v 选项,这会将你所做的改变的 diff 输出放到编辑器中从而使你知道本次提交具体做了哪些修改。
退出编辑器时,Git 会丢掉注释行,用你输入提交附带信息生成一次提交。
image.png

git commit -m “提交信息”
你也可以在 commit 命令后添加 -m 选项,将提交信息与命令放在同一行

注:每一次运行提交操作,都是对你项目作一次快照,以后可以回到这个状态,或者进行比较。

6. 移除文件

6.1 移除文件的两个步骤:

  1. 从暂存区移除

git rm a.txt
注:该操作也会将该文件从本地工作目录中删除。

  1. 提交

git commit
注:如果该文件没有在仓库中,那么就不用commit

6.2 手动删除文件

如果只是简单地从工作目录中手工删除文件,运行 git status 时就会在 “Changes not staged for commit”,此时需要再运行 git rm 记录此次移除文件的操作,然后 git commit 提交

6.3 强制删除文件

git rm -f
如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项 -f(译注:即 force 的首字母)。 这是一种安全特性,用于防止误删还没有添加到快照的数据, 这样的数据不能被 Git 恢复。
image.png

6.4 批量删除文件

git rm 命令后面可以列出文件或者目录的名字,也可以使用 glob 模式。

git rm log/*.log
注意到星号 * 之前的反斜杠 \, 因为 Git 有它自己的文件模式扩展匹配方式,所以我们不用 shell 来帮忙展开。
此命令删除 log/ 目录下扩展名为 .log 的所有文件。

git rm *~
删除以 ~ 结尾的所有文件

git rm -r /f
删除f目录以及f目录下的所有文件

7. 移动文件/重命名文件

git mv

其实,运行 git mv 就相当于运行了下面三条命令: mv README.md README git rm README.md git add README

如此分开操作,Git 也会意识到这是一次改名,所以不管何种方式结果都一样。
两者唯一的区别是,mv 是一条命令而另一种方式需要三条命令,直接用 git mv 轻便得多。
不过有时候用其他工具批处理改名的话,要记得在提交前删除老的文件名,再添加新的文件名。

注:如果file_to的文件夹不存在的话,会mv失败,因为git mv不会自动创建目录,此时需要先创建目录,在进行文件移动

image.png

8. 查看提交历史

8.1 一些常用的选项

git log
image.png

git log 会按提交时间列出所有的更新,最近的更新排在最上面。
正如你所看到的, 这个命令会列出每个提交的 SHA-1 校验和、作者的名字和电子邮件地址、提交时间以及提交说明。

git log -p -2
选项 -p,用来显示每次提交的内容差异。 你也可以加上 -2 来仅显示最近两次提交:
该选项除了显示基本信息之外,还附带了每次 commit 的变化。
当进行代码审查,或者快速浏览某个搭档提交的 commit 所带来的变化的时候,这个参数就非常有用了。
image.png

git log —stat -2
如果你想看到每次提交的简略的统计信息,你可以使用 —stat 选项
—stat 选项在每次提交的下面列出所有被修改过的文件、有多少文件被修改了以及被修改过的文件的哪些行被移除或是添加了。 在每次提交的最后还有一个总结
image.png

git log —pretty=oneline/short/full/fuller
另外一个常用的选项是 —pretty。 这个选项可以指定使用不同于默认格式的方式展示提交历史。 这个选项有一些内建的子选项供你使用。 比如用 oneline 将每个提交放在一行显示,查看的提交数很大时非常有用。 另外还有 short,full 和 fuller 可以用,展示的信息或多或少有些不同,

git log —pretty=format:”%h - %an, %ar : %s”
—pretty=format,可以定制要显示的记录格式。 这样的输出对后期提取分析格外有用—因为你知道输出的格式不会随着 Git 的更新而发生改变:
image.png

git log —pretty=oneline —graph
当 oneline 或 format 与另一个 log 选项 —graph 结合使用时尤其有用。 这个选项添加了一些ASCII字符串来形象地展示你的分支、合并历史:
image.png

8.2 git log选项

image.png

8.3 限制输出长度

除了定制输出格式的选项之外,git log 还有许多非常实用的限制输出长度的选项,也就是只输出部分提交信息。

git log -
其中的 n 可以是任何整数,表示仅显示最近的若干条提交。

git log —since=2.weeks
列出所有最近两周内的提交
image.png

git log —until=2022-5-26
列出截止到2022.5.26的所有提交

git log —author=yongzhilu
—author 选项显示指定作者的提交
—author=user.name或者user.email都可以
image.png

git log —grep=asd
用 —grep 选项搜索提交说明中的关键字。
其中asd为message中包含的内容
image.png

git log -2 —author=yongzhilu —grep=asdasd —all-match
请注意,如果要得到同时满足这两个选项搜索条件的提交,就必须用 —all-match 选项。否则,满足任意一个条件的提交都会被匹配出来

git log -S function_name
另一个非常有用的筛选选项是 -S,可以列出那些 添加 或 移除 了某些字符串的提交。
image.png

git log — path
最后一个很实用的 git log 选项是路径(path), 如果只关心某些文件或者目录的历史提交,可以在 git log选项的最后指定它们的路径。 因为是放在最后位置上的选项,所以用两个短划线(—)隔开之前的选项和后面限定的路径名。
image.png

9. 撤销操作

注意,有些撤消操作是不可逆的。

9.1 修改提交操作

git commit -m “modified commit” —amend
有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 —amend 选项的提交命令尝试重新提交。
这个命令会将暂存区中的文件提交。 如果自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了此命令),那么快照会保持不变,而你所修改的只是提交信息。

例如,你提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:

  1. git commit -m 'initial commit'
  2. git add forgotten_file
  3. git commit -m 'modified commit' --amend

最终你只会有一个提交 - 第二次提交将代替第一次提交的结果。

9.2 取消暂存的文件

git reset HEAD
从暂存区中取出文件
image.png

9.3 撤销对文件的修改

git checkout —
撤消修改 - 将它还原成上次提交时的样子。
image.png

9.4 补充

记住,在 Git 中任何 已提交的(committed) 东西几乎总是可以恢复的。 甚至那些被删除的分支中的提交或使用 —amend 选项覆盖的提交也可以恢复(阅读 数据恢复 了解数据恢复)。 然而,任何你未提交的东西丢失后很可能再也找不到了。

远程Git仓库

与他人协作涉及管理远程仓库以及根据需要推送或拉取数据。 管理远程仓库包括了解如何添加远程仓库、移除无效的远程仓库、管理不同的远程分支并定义它们是否被跟踪等等

1. 查看远程仓库列表

git remote
如果想查看你已经配置的远程仓库服务器,可以运行 git remote 命令。
它会列出你指定的每一个远程服务器的简写。 如果你已经克隆了自己的仓库,那么至少应该能看到 origin - 这是 Git 给你克隆的仓库服务器的默认名字
image.png

git remote -v
选项 -v,会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL。
image.png

查看远程所有仓库
image.png

2. 添加远程仓库

git remote add
添加一个新的远程 Git 仓库,同时指定一个你可以轻松引用的简写,你可以在命令行中使用字符串 blog 、gl 来代替整个 URL

  1. C:\Users\zsf\Desktop\ttt>git init
  2. Reinitialized existing Git repository in C:/Users/zsf/Desktop/ttt/.git/
  3. C:\Users\zsf\Desktop\ttt>git remote add blog https://gitee.com/yongzhilu1/blog.git
  4. C:\Users\zsf\Desktop\ttt>git remote add gl https://gitee.com/yongzhilu1/git-learning.git
  5. C:\Users\zsf\Desktop\ttt>git remote
  6. blog
  7. gl
  8. C:\Users\zsf\Desktop\ttt>git remote -v
  9. blog https://gitee.com/yongzhilu1/blog.git (fetch)
  10. blog https://gitee.com/yongzhilu1/blog.git (push)
  11. gl https://gitee.com/yongzhilu1/git-learning.git (fetch)
  12. gl https://gitee.com/yongzhilu1/git-learning.git (push)

3. 拉取远程仓库代码

git fetch
这个命令会访问远程仓库,从中拉取所有你还没有的数据。 执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。

注: git fetch 命令会将数据拉取到你的本地仓库 - 它并不会自动合并或修改你当前的工作。 当准备好时你必须手动将其合并入你的工作。

git clone
如果你使用 clone 命令克隆了一个仓库,命令会自动将其添加为远程仓库并默认以 “origin” 为简写。
默认情况下,git clone 命令会自动设置本地 master 分支跟踪克隆的远程仓库的 master 分支(或不管是什么名字的默认分支)

git pull
如果你有一个分支设置为跟踪一个远程分支(阅读下一节与 Git 分支 了解更多信息),可以使用 git pull 命令来自动的抓取然后合并远程分支到当前分支。 这对你来说可能是一个更简单或更舒服的工作流程;
运行 git pull 通常会从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支。

4. 推送到远程仓库

git push
git push origin master:将 master 分支推送到 origin 服务器时(再次说明,克隆时通常会自动帮你设置好那两个名字)

注:只有当你有所克隆服务器的写入权限,并且之前没有人推送过时,这条命令才能生效。 当你和其他人在同一时间克隆,他们先推送到上游然后你再推送到上游,你的推送就会毫无疑问地被拒绝。 你必须先将他们的工作拉取下来并将其合并进你的工作后才能推送。

5. 查看远程仓库详细信息

git remote show
如果想要查看某一个远程仓库的更多信息

  1. // 它告诉你正处于 master 分支,并且如果运行 git pull,就会抓取所有的远程引用,然后将远程 master 分支合并到本地 master 分支。
  2. // 它也会列出拉取到的所有远程引用
  3. C:\Users\zsf\Desktop\ttt\git-learning>git remote
  4. origin
  5. C:\Users\zsf\Desktop\ttt\git-learning>git remote show origin
  6. * remote origin
  7. Fetch URL: https://gitee.com/yongzhilu1/git-learning.git
  8. Push URL: https://gitee.com/yongzhilu1/git-learning.git
  9. HEAD branch: master
  10. Remote branch:
  11. master tracked
  12. Local branch configured for 'git pull':
  13. master merges with remote master
  14. Local ref configured for 'git push':
  15. master pushes to master (up to date)

举例
这个命令列出了当你在特定的分支上执行 git push 会自动地推送到哪一个远程分支。 它也同样地列出了哪些远程分支不在你的本地,哪些远程分支已经从服务器上移除了,还有当你执行 git pull 时哪些分支会自动合 并。

  1. C:\Users\zsf\Desktop\ttt\git-learning> git remote show origin
  2. * remote origin
  3. URL: https://github.com/my-org/complex-project
  4. Fetch URL: https://github.com/my-org/complex-project
  5. Push URL: https://github.com/my-org/complex-project
  6. HEAD branch: master
  7. Remote branches:
  8. master tracked
  9. dev-branch tracked
  10. markdown-strip tracked
  11. issue-43 new (next fetch will store in
  12. remotes/origin)
  13. issue-45 new (next fetch will store in
  14. remotes/origin)
  15. refs/remotes/origin/issue-11 stale (use 'git remote prune' to
  16. remove)
  17. Local branches configured for 'git pull':
  18. dev-branch merges with remote dev-branch
  19. master merges with remote master
  20. Local refs configured for 'git push':
  21. dev-branch pushes to dev-branch
  22. (up to date)
  23. markdown-strip pushes to markdown-strip
  24. (up to date)
  25. master pushes to master
  26. (up to date)

6. 远程仓库重命名

git remote rename

  1. C:\Users\zsf\Desktop\ttt>git remote
  2. gl
  3. C:\Users\zsf\Desktop\ttt>git remote rename gl gl_new
  4. C:\Users\zsf\Desktop\ttt>git remote
  5. gl_new

注:值得注意的是这同样也会修改你的远程分支名字。 那些过去引用 gl/master 的现在会引用 gl_new/master。

7. 移除远程仓库

git remote rm

  1. C:\Users\zsf\Desktop\ttt>git remote
  2. gl_new
  3. C:\Users\zsf\Desktop\ttt>git remote rm gl_new
  4. C:\Users\zsf\Desktop\ttt>git remote
  5. C:\Users\zsf\Desktop\ttt>

标签

像其他版本控制系统(VCS)一样,Git 可以给历史中的某一个提交打上标签,以示重要。 比较有代表性的是人们会使用这个功能来标记发布结点(v1.0 等等)

①如何列出已有的标签 ②如何创建新标签 ③不同类型的标签分别是什么

1. 列出标签

git tag
在 Git 中列出已有的标签是非常简单直观的

  1. C:\Users\zsf\Desktop\ttt> git tag
  2. v0.1
  3. v1.3

注:这个命令以字母顺序列出标签;但是它们出现的顺序并不重要。

git tag -l “模式匹配”

  1. C:\Users\zsf\Desktop\ttt> git tag -l "v1.8.5*"
  2. v1.8.5
  3. v1.8.5-rc0
  4. v1.8.5-rc1
  5. v1.8.5-rc2
  6. v1.8.5-rc3
  7. v1.8.5.1
  8. v1.8.5.2
  9. v1.8.5.3
  10. v1.8.5.4
  11. v1.8.5.5

2. 创建标签

通常建议创建附注标签,这样你可以拥有以上所有信息;但是如果你只是想用一个临时的标签,或者因为某些原因不想要保存那些信息,轻量标签也是可用的。

2.1 轻量标签 lightweight

一个轻量标签很像一个不会改变的分支 - 它只是一个特定提交的引用。
轻量标签本质上是将提交校验和存储到一个文件中 - 没有保存任何其他信息。

git tag v0.1
创建轻量标签,不需要使用 -a、-s 或 -m 选项,只需要提供标签名字:

  1. C:\Users\zsf\Desktop\ttt\git-learning>git tag v0.1
  2. C:\Users\zsf\Desktop\ttt\git-learning>git tag
  3. v0.1

git show v0.1

  1. C:\Users\zsf\Desktop\ttt\git-learning>git show v0.1
  2. commit ae551d5141a65f0521dfa7942a4725d491ca1e04 (HEAD -> master, tag: v1.4, tag: v0.2, tag: v0.1, origin/master, origin/HEAD)
  3. Author: yongzhilu <1046803954@qq.com>
  4. Date: Thu May 26 23:37:52 2022 +0800
  5. 123
  6. diff --git a/demo.js b/demo.js
  7. index 4c9af0d..fa6b466 100644
  8. --- a/demo.js
  9. +++ b/demo.js
  10. @@ -3,8 +3,6 @@
  11. function test(filename) {
  12. // 补全代码
  13. return filename.substr(filename.lastIndexOf('.'), filename.length);
  14. -
  15. -
  16. }
  17. console.log(test('ad.hello.js'))
  18. \ No newline at end of file

2.2 附注标签 annotated

附注标签是存储在 Git 数据库中的一个完整对象。它们是可以被校验的;其中包含打标签者的名字、电子邮件地址、日期时间;还有一个标签信息;并且可以使用 GNU Privacy Guard (GPG)签名与验证。

git tag -a v0.2 -m “create version 0.2”

  1. C:\Users\zsf\Desktop\ttt\git-learning>git tag -a v0.2 -m "create version 0.2"
  2. C:\Users\zsf\Desktop\ttt\git-learning>git tag
  3. v0.1
  4. v0.2

git show v0.2

  1. C:\Users\zsf\Desktop\ttt\git-learning> git show v0.2
  2. tag v0.2
  3. Tagger: yongzhilu <1046803954@qq.com>
  4. Date: Fri May 27 00:20:40 2022 +0800
  5. create version 0.2
  6. commit ae551d5141a65f0521dfa7942a4725d491ca1e04 (HEAD -> master, tag: v1.4, tag: v0.2, origin/master, origin/HEAD)
  7. Author: yongzhilu <1046803954@qq.com>
  8. Date: Thu May 26 23:37:52 2022 +0800
  9. 123
  10. diff --git a/demo.js b/demo.js
  11. index 4c9af0d..fa6b466 100644
  12. --- a/demo.js
  13. +++ b/demo.js
  14. @@ -3,8 +3,6 @@
  15. function test(filename) {
  16. // 补全代码
  17. return filename.substr(filename.lastIndexOf('.'), filename.length);
  18. -
  19. -
  20. }
  21. console.log(test('ad.hello.js'))
  22. \ No newline at end of file

3. 查看标签信息

git show tag-name
如果是 轻量标签 则仅查看提交信息;
如果是 附注标签 则查看标签信息与对应的提交信息

4. 补办标签

git tag -a tag-name checksum

假设提交历史是这样的:

  1. C:\Users\zsf\Desktop\ttt\git-learning> git log --pretty=oneline
  2. 15027957951b64cf874c3557a0f3547bd83b3ff6 Merge branch 'experiment'
  3. a6b4c97498bd301d84096da251c98a07c7723e65 beginning write support
  4. 0d52aaab4479697da7686c15f77a3d64d9165190 one more thing
  5. 6d52a271eda8725415634dd79daabbc4d9b6008e Merge branch 'experiment'
  6. 0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc added a commit function
  7. 4682c3261057305bdd616e23b64b0857d832627b added a todo file
  8. 166ae0c4d3f420721acbb115cc33848dfcc2121a started write support
  9. 9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile # 要对这条历史提交记录进行打标签
  10. 964f16d36dfccde844893cac5b347e7b3d44abbc commit the todo
  11. 8a5cbc430f1a9c3d00faaeffd07798508422908a updated readme
  12. C:\Users\zsf\Desktop\ttt\git-learning> git tag -a v1.2 9fceb02
  13. C:\Users\zsf\Desktop\ttt\git-learning> git show v1.2
  14. tag v1.2
  15. Tagger: Scott Chacon <schacon@gee-mail.com>
  16. Date: Mon Feb 9 15:32:16 2009 -0800
  17. version 1.2
  18. commit 9fceb02d0ae598e95dc970b74767f19372d61af8 # checksum
  19. Author: Magnus Chacon <mchacon@gee-mail.com>
  20. Date: Sun Apr 27 20:43:35 2008 -0700
  21. updated rakefile

5. 推送标签

git push origin
默认情况下,git push 命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上。 这个过程就像共享远程分支一样 - 你可以运行

  1. C:\Users\zsf\Desktop\ttt\git-learning> git push origin v1.5
  2. Counting objects: 14, done.
  3. Delta compression using up to 8 threads.
  4. Compressing objects: 100% (12/12), done.
  5. Writing objects: 100% (14/14), 2.05 KiB | 0 bytes/s, done.
  6. Total 14 (delta 3), reused 0 (delta 0)
  7. To git@github.com:schacon/simplegit.git
  8. * [new tag] v1.5 -> v1.5

git push origin —tags
把所有不在远程仓库服务器上的标签全部传送到那里。
现在,当其他人从仓库中克隆或拉取,他们也能得到你的那些标签。

  1. $ git push origin --tags
  2. Counting objects: 1, done.
  3. Writing objects: 100% (1/1), 160 bytes | 0 bytes/s, done.
  4. Total 1 (delta 0), reused 0 (delta 0)
  5. To git@github.com:schacon/simplegit.git
  6. * [new tag] v1.4 -> v1.4
  7. * [new tag] v1.4-lw -> v1.4-lw

6. 检出标签

image.png

别名

image.pngimage.png
综上,常用的别名设置:

  1. git config --global alias.ck checkout
  2. git config --global alias.br branch
  3. git config --global alias.cmt commit
  4. git config --global alias.sts status
  5. git config --global alias.unstage "reset HEAD --"
  6. git config --global alias.last "log -1"

分支

1. 分支简介

详细内容查看《精通Git 第2版》对应章节

Git 保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照
在进行提交操作时,Git 会保存一个提交对象(commit object),该提交对象会包含一个指向暂存内容快照的指针。

该提交对象还包含了作者的姓名和邮箱、提交时输入的信息以及指向它的父对象的指针。
首次提交产生的提交对象没有父对象,普通提交操作 产生的提交对象有一个父对象,而由多个分支合并产生的提交对象有多个父对象,

暂存操作会为每一个文件计算校验和(使用我们在 起步 中提到的 SHA-1 哈希算法),然后会把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 对象来保存它们),最终将校验和加入到暂存区域等待提交:
当使用 git commit 进行提交操作时,Git 会先计算每一个子目录(本例中只有项目根目录)的校验和,然后在Git 仓库中这些校验和保存为 tree对象。 随后,Git 便会创建一个提交对象,它除了包含上面提到的那些信息外,还包含指向这个树对象(项目根目录)的指针。如此一来,Git 就可以在需要的时候重现此次保存的快照。

git add README test.rb LICENSE git commit -m ‘The initial commit of my project’ 现在,Git 仓库中有五个对象:三个 blob 对象(保存着文件快照)、一个树对象(记录着目录结构和 blob 对象 索引)以及一个提交对象(包含着指向前述树对象的指针和所有提交信息)

image.png

image.png
image.png

image.png

2. 分支创建

git branch

  1. git branch testing

这会在当前所在的提交对象上创建一个分支(指针),该命令仅仅创建 一个新分支,并不会自动切换到新分支中去
image.png

HEAD指针
在 Git 中,它是一个指向分支的指针,指向当前所在的分支(译注:将 HEAD 想象为当前分支的别名)。

master和testing指向历史实体; HEAD指向master或者testing

HEAD 指针会随着提交操作自动向前移动

git log —oneline —decorate
查看当前各个分支所指的提交对象。

  1. C:\Users\zsf\Desktop\ttt>git log --oneline --decorate
  2. 2decbfe (HEAD -> master, testing) asd

当前master和testing分支都指向2decbfe对象,HEAD指向master

git checkout -b
新建一个分支并同时切换到那个分支上

注:这个分支必须是不存在的,才能创建该分支

  1. C:\Users\zsf\Desktop\ttt>git branch
  2. * master
  3. testing
  4. C:\Users\zsf\Desktop\ttt>git checkout -b testing
  5. fatal: A branch named 'testing' already exists.

3. 分支切换

git checkout

  1. git checkout testing

这样 HEAD 就指向 testing 分支了。
image.png
image.pngimage.png
image.png

分支切换注意事项

在你这么做之前,要留意你的工作目录和暂存区里那些还没有被提交的修改,它可能会和你即将检出的分支产生冲突从而阻止 Git 切换到该分支。 最好的方法是,在你切换分支之前,保持好一个干净的状态

有一些法可以绕过这个问题(即,保存进度(stashing) 和 修补提交(commit amending))

git log —oneline —decorate —graph —all

它会输出你的提交历史、各个分支的指向以及项目的分支分叉情况

  1. C:\Users\zsf\Desktop\ttt>git log --oneline --decorate --graph --all
  2. * c2b9e (HEAD, master) made other changes
  3. | * 87ab2 (testing) made a change
  4. |/
  5. * f30ab add feature #32 - ability to add new formats to the
  6. * 34ac2 fixed bug #1328 - stack overflow under certain conditions
  7. * 98ca9 initial commit of my project

4. 分支合并

git merge

  1. git checkout master # 切换到master分支
  2. git merge hotfix # 将hotfix分支合并到当前分支

注:合并分支并不会删除其中任何一个分支,而是将两个分支指向同一个历史对象。

合并分支的几种情况:

  1. fast-forward 直接快进

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

git branch -d hotfix
关于这个紧急问题的解决方案发布之后,你准备回到被打断之前时的工作中。 然而,你应该先删除 hotfix 分支,因为你已经不再需要它了 —— master 分支已经指向了同一个位置。
image.png

注:当 master 分支的问题处理完后,此时iss53分支上并没有包含 master上修改的代码,如果你需要拉取 hotfix 所做的修改,你可以使用 git merge master 命令将 master 分支合并入 iss53 分支,或者你也可以等到 iss53 分支完成其使命,再将其合并回 master 分支

image.png

  1. thirty-party automatic commit 自动合并提交

你的开发历史从一个更早的地方开始分叉开来(diverged)。 因为,master 分支所在提交并不是 iss53 分支所在提交的直接祖先,Git 不得不64做一些额外的工作。 出现这种情况的时候,Git 会使用两个分支的末端所指的快照( C4 和 C5 )以及这两个分支的工作祖先(C2),做一个简单的三方合并。
image.png

和之前将分支指针向前推进所不同的是,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。 这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。
image.png

需要指出的是,Git 会自行决定选取哪一个提交作为最优的共同祖先,并以此作为合并的基础;

既然你的修改已经合并进来了,你已经不再需要 iss53 分支了。 现在你可以在任务追踪系统中关闭此项任务,并删除这个分支。

  1. git branch -d iss53
  1. conflict merge 冲突合并

如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并它们。

5. 分支管理

git branch
如果不加任何参数运行它,会得到当前所有分支的一个列表:

  1. C:\Users\zsf\Desktop\ttt>git branch
  2. * master
  3. testing

注:* 号代表当前所处分支

git branch -v
查看每一个分支的最后一次提交。

  1. C:\Users\zsf\Desktop\ttt>git branch -v
  2. * master 2decbfe asd
  3. testing 2decbfe asd

git branch —merge
这个选项可以过滤出这个列表中已经合并的分支。

注:在这个列表中分支名字前没有 * 号的分支通常可 以使用 git branch -d 删除掉;

git branch —no-merge
这个选项可以过滤出这个列表中未合并的分支。

注:未合并的分支在尝试 git branch -d 删除时会失败;如果真的想要删除分支并丢掉那些工作,如同帮助信息里所指出的,可以使用 -D 选项(小d改写为大D)强制删除它。(git branch -D)

6. 远程分支

远程引用是对远程仓库的引用(指针),包括分支、标签等等。
远程跟踪分支是远程分支状态的引用。 它们是你不能移动的本地引用,当你做任何网络通信操作时,它们会自动移动。 远程跟踪分支像是你上次连接到远程仓库时,那些分支所处状态的书签。

注:即 远程跟踪分支 (例如:origin/master) 是git自动生成并且git自动维护的一种引用,用户无法操作也不用操作。

image.png
image.png
image.png

git ls-remote origin
显式地获得远程引用的完整列表

  1. C:\Users\zsf\Desktop\ttt\git-learning>git ls-remote origin
  2. ae551d5141a65f0521dfa7942a4725d491ca1e04 HEAD
  3. ae551d5141a65f0521dfa7942a4725d491ca1e04 refs/heads/master

git remote show origin
获得远程分支的更多信息。

  1. C:\Users\zsf\Desktop\ttt\git-learning>git remote show origin
  2. * remote origin
  3. Fetch URL: https://gitee.com/yongzhilu1/git-learning.git
  4. Push URL: https://gitee.com/yongzhilu1/git-learning.git
  5. HEAD branch: master
  6. Remote branch:
  7. master tracked
  8. Local branch configured for 'git pull':
  9. master merges with remote master
  10. Local ref configured for 'git push':
  11. master pushes to master (up to date)

7. 多个远程仓库分支管理

image.png
image.pngimage.png

8. 推送

git push origin serverfix
该条命令有两个功能:①推送一条本地分支serverfix到远程服务器origin上,在该服务器上也创建一个serverfix分支;②将本地分支serverfix的代码同步到远程服务器origin的serverfix上

git push origin serverfix:dev
①推送一条本地分支serverfix到远程服务器origin上,在该服务器上也创建一个dev分支;②将本地分支serverfix的代码同步到远程服务器origin的dev分支上

git fetch origin
如果你已经将serverfix推送到服务器,那么其他人在fetch这个服务器时,也会把serverfix(指针)拉取到本地。

注:当抓取到新的远程跟踪分支时,本地不会自动生成一份可编辑的副本(拷贝)。 换一句话说,这种情况下,不会有一个新的 serverfix 分支 - 只有一个不可以修改的 origin/serverfix 指针。

git merge origin/serverfix
将这些工作合并到当前所在的分支。 如果想要在自己的serverfix 分支上工作,可以将其建立在远程跟踪分支之上:

  1. C:\Users\zsf\Desktop\ttt> git checkout -b serverfix origin/serverfix
  2. Branch serverfix set up to track remote branch serverfix from origin.
  3. Switched to a new branch 'serverfix'

这会给你一个用于工作的本地分支,并且起点位于 origin/serverfix。

9. 跟踪分支

从一个远程跟踪分支检出一个本地分支会自动创建一个叫做 “跟踪分支”(有时候也叫做 “上游分支”)
跟踪分支是与远程分支有直接关系的本地分支

git pull
如果在一个跟踪分支上输入 git pull,Git 能自动地识别去哪个服务器上抓取、合并到哪个分支。

git checkout -b [branch] [remotename]/[branch]
当克隆一个仓库时,它通常会自动地创建一个跟踪 origin/master 的 master 分支。 然而,如果你愿意的话可以设置其他的跟踪分支 - 其他远程仓库上的跟踪分支,或者不跟踪 master 分支。

git checkout —track origin/serverfix

  1. C:\Users\zsf\Desktop\ttt> git checkout --track origin/serverfix
  2. Branch serverfix set up to track remote branch serverfix from origin.
  3. Switched to a new branch 'serverfix'

git checkout -b sf origin/serverfix
如果想要将本地分支与远程分支设置为不同名字,你可以轻松地增加一个不同名字的本地分支的上一个命令。
现在,本地分支 sf 会自动从 origin/serverfix 拉取。

  1. C:\Users\zsf\Desktop\ttt> git checkout -b sf origin/serverfix
  2. Branch sf set up to track remote branch serverfix from origin.
  3. Switched to a new branch 'sf'

git branch -u origin/serverfix
设置已有的本地分支跟踪一个刚刚拉取下来的远程分支,或者想要修改正在跟踪的上游分支,你可以在任意时间使用 -u 或 —set-upstream-to 选项运行 git branch 来显式地设置

  1. C:\Users\zsf\Desktop\ttt> git branch -u origin/serverfix
  2. Branch serverfix set up to track remote branch serverfix from origin.

git branch -vv
查看设置的所有跟踪分支
这会将所有的本地分支列出来并且包含更多的信息,如每一个分支正在跟踪哪个远程分支与本地分支是否是领先、落后或是都有。

  1. $ git branch -vv
  2. iss53 7e424c3 [origin/iss53: ahead 2] forgot the brackets
  3. master 1ae2a45 [origin/master] deploying index fix
  4. * serverfix f8674d9 [teamone/server-fix-good: ahead 3, behind 1] this
  5. should do it
  6. testing 5ea463a trying something new

这里可以看到 iss53 分支正在跟踪 origin/iss53 并且 “ahead” 是 2,意味着本地有两个提交还没有推送到服务器上。 也能看到 master 分支正在跟踪 origin/master 分支并且是最新的。 接下来可以看到80serverfix 分支正在跟踪 teamone 服务器上的 server-fix-good 分支并且领先 3 落后1,意味着服务器上有一次提交还没有合并入同时本地有三次提交还没有推送。 最后看到 testing 分支并没有跟踪任何远程分支。

注:这些数字的值来自于你从每个服务器上最后一次抓取的数据。 这个命令并没有连接服务器,它只会告诉你关于本地缓存的服务器数据。如果想要统计最新的领先与落后数字,需要在运行此命令前抓取所有的远程仓库。 可以像这样做:$ git fetch —all; git branch -vv

10. 拉取

当 git fetch 命令从服务器上抓取本地没有的数据时,它并不会修改工作目录中的内容。 它只会获取数据然后让你自己合并。 然而,有一个命令叫作 git pull 在大多数情况下它的含义是一个 git fetch 紧接着一个
git merge 命令。 如果有一个像之前章节中演示的设置好的跟踪分支,不管它是显式地设置还是通过 clone或 checkout 命令为你创建的,git pull 都会查找当前分支所跟踪的服务器与分支,从服务器上抓取数据然
后尝试合并入那个远程分支。

由于 git pull 的魔法经常令人困惑所以通常单独显式地使用 fetch 与 merge 命令会更好一些。

11. 删除分支

假设你已经通过远程分支做完所有的工作了 - 也就是说你和你的协作者已经完成了一个特性并且将其合并到了远程仓库的 master 分支(或任何其他稳定代码分支)。 可以运行带有 —delete 选项的 git push 命令来删
除一个远程分支。 如果想要从服务器上删除 serverfix 分支,运行下面的命令:

  1. C:\Users\zsf\Desktop\ttt>git push origin --delete serverfix
  2. To https://github.com/schacon/simplegit
  3. - [deleted] serverfix

基本上这个命令做的只是从服务器上移除这个指针。 Git 服务器通常会保留数据一段时间直到垃圾回收运行,所以如果不小心删除掉了,通常是很容易恢复的。