学会 Git, 看这篇就够了(4)

Learn Git the hard way 4

Posted by Chinsyo on June 2, 2019

在前文中,我们围绕 Git 中最重要的概念之一 —— 分支,探讨了分支的作用及查看、新建、删除、合并的方式。

截止目前,本系列文章介绍的仍然只是 Git 的冰山一角,本文将会介绍 Git 中的特殊文件,查看文件差异和提交内容的方式,以及如何回退、撤销、储藏变更和其他相关知识。

Git 作为一个版本控制系统,在初始化时会在仓库根目录生成名为 .git 的隐藏目录,删除该目录后再次执行 Git 命令会提示当前「not a repository」。现阶段,请避免手动对这个目录任何增删改操作。

忽略文件

Git 默认会对仓库内所有文件进行管理,但有三类文件我们通常不希望加入版本管理

  • 服务器地址、密钥信息等安全相关文件
  • 个人配置、调试信息等因人而异的文件
  • 编译结果、外部依赖等拖慢下载的文件

对于这些文件,我们可以在仓库根目录创建 .gitignore 隐藏文件声明忽略规则。.gitignore 使用类似正则表达式的语法,可以使用 git help ignore 查看具体的规则,下面是一张常见语法的贴图。

针对常见的开发语言和平台,GitHub 托管了常用的 .gitignore 配置仓库,访问 https://github.com/github/gitignore/ 查看。

通过 git check-ignore 子命令检查配置忽略的文件。

1
git status --ignored

忽略的文件仍然可以通过下面的命令强制添加,不过我更建议你仔细检查和修改 .gitignore 文件。

1
git add -f IGNORED_FILE

保持文件

默认情况下,Git 会忽略没有文件的空目录,但空目录有其特定的存在场景,如 makefile 通常将编译生成的文件置入 ./bin 目录,但编译结果文件被列入上述的忽略文件规则中,导致其他用户在使用 makefile 时由于 ./bin 目录不存在造成编译失败。在该目录下创建文件(即便是空文件)可以解决这个问题,约定俗成的做法是在要保持的目录下创建名为 .gitkeep 的空文件。

显示文件差异

在 学会 Git,看这篇就够了(2) 一文解决合并冲突的章节,简要解释了 diff 格式。diff 是由自由软件基金会(Free Software Foundation, 简称 FSF)开发的命令行软件,大部分操作系统都内置了这一软件,diff 结果有正常格式、上下文格式和合并格式,git diff 子命令默认采用合并格式。

在显示文件差异之前,我们先对工作区文件人为的制造一些差异。

1
echo "# author: Chenxiao" >> dev.txt

这时查看工作区状态,如我们所料,dev.txt 被修改。

使用命令查看 dev.txt 修改了哪些内容。

1
git diff HEAD dev.txt

同时,我们可以显示工作区所有文件的修改内容。

显示提交内容

git log 给予了我们查看提交日志的能力,在工程实践中,常常需要对某次提交的具体内容进行查阅。比如在完成某个需求之后需要进行代码评审,按照 Git 的理念每个 commit 对应一个需求中可量化的子模块,git show 命令赋予了我们查看提交之间变更的能力。

使用方式和输出结果如下。

git show 除了能够查看提交之外,还能查看标签、文件树和数据对象。

储藏和恢复未提交的变更

通常,Git 每次提交需要是一次相对独立的完整改动,以方便代码评审和管理。如果你的提交混合两个相对独立的改动,比如修改单词拼写错误和修改支付逻辑,可能在测试过程中发现修改支付逻辑的代码引入新的缺陷,需要回退。

开发过程中,我们需要经常性的拉取代码仓库的更新。此时,如果本地有未提交(commit)的变更会发生错误,git stash 可以在这种场景下派上用场。

git stash 主要应用于储藏和恢复未提交的变更。基础用法为,在 git pull 之前先执行 git stash 储藏当前未提交的变更,之所以这么做而不是提交本地变更是为了保证每次提交是一次相对独立的完整改动。

在拉取远程变更之后,执行 git stash pop 将储藏的变更恢复到工作区。除此之外还可以通过 git stash list 查看储藏的列表,通过 git stash drop 丢弃储藏的内容。

撤销提交

本系列文章的开头花了很大的篇幅阐释版本控制的意义,在真实的使用场景中,撤销提交和回退提交是最能体现版本控制意义的操作。

设想如果没有 Git,我们对某项修改的内容产生异议,最直接有效的做法就是做相反的操作,如某次提交新增的两行代码经过评审不允许上线,那么我们使用一个提交来删除这两行代码即可。

这就是 git revert 操作,对比回退提交,他的做法是在当前 HEAD 指针后追加一个相反的提交,保留有非常完整的历史。

回退提交

回退提交和撤销提交的做法不同,回退提交是指将 HEAD 移动至某个需要回退的提交。git reset 的基础用法通常是 git reset MODE COMMIT,即将工作区回退到某次提交,这里主要介绍三种模式,分别是缺省项混合模式(–mixed),软模式(–soft)和硬模式(–hard)。

软模式仅修改 HEAD 指针到指定提交,该提交之后的内容会当作待提交的改动保留,可以通过 git status 看到这些保留的改动。

混合模式修改 HEAD 指针到指定提交,该提交之后的内容会当作未加入工作区的改动保留,可以通过 git status 看到这些保留的改动。

硬模式修改 HEAD 指针到指定提交,该提交之后的内容会被丢弃。

如果你不清楚自己需要哪个模式,请极力避免使用包含 –force 和 –hard 的操作,尽管 Git 中几乎所有操作都是有迹可循的,但并不是所有操作都可以很容易的找到和恢复。关于 –hard 的使用,阮一峰老师有一条微博曾引起不小的争议,起因就是其将 git reset –hard 当作技巧进行传播。

清理工作区

git clean,顾名思义为清理工作区,即删除工作区没有被追踪的文件。

假设你为了完成某个需求修改了很多文件,不幸的发现添加这些修改后项目无法编译,趁着还没提交不如删了这些文件。

通常我会选择使用以下两个命令。

1
2
git clean -df
git reset --hard

这操作是指删除工作区没有被追踪的文件,并重置 HEAD 指针。

当然,你也可以通过 git clean -n 并不删除文件,而是仅仅查看 clean 命令会影响的文件。

重复一遍,如果你还不清楚 –hard 的作用,请放弃使用并仔细查阅文档。

番外

在最近的使用过程中,遇到一个非常有趣的场景分享给大家。在某次云主机的促销活动中我购买了一台并搭建了 golang 开发环境,在该环境创建项目并运行,通过 nginx 提供服务,在我查看到网页运行正常后决定将代码提交到 GitHub。

由于疏忽,创建 Git 仓库时我还没有执行 git config –global 配置用户名和邮箱,直到通过网页查看提交历史我才注意到 initial 提交是一个陌生的头像。

通过 git log 查看提交日志发现,创建项目的初次提交由于没有配置用户名和邮箱。采用的是 ubuntu 系统默认用户名 ubuntu 和默认邮箱

[email protected]

最终通过一番搜索,以下 bash 脚本解决了我的问题。

转载请注明原始出处 学会 Git, 看这篇就够了(4) © 晨晓 | Chinsyo