使用 Git 提交以保存修改

内容纲要

查看【Git】专题可浏览更多内容

通过上一篇 Git 入门,你现在应该已经有了一个 Git 本地仓库项目了,如果没有就着手建立一个吧。

使用 .gitignore 排除不想被管理的文件

有一些文件是我们不想被 Git 管理的,可以在文件夹新建一个名为 .gitignore,文件内容示例如下:

Thumbs.db
.DS_Store

每行一个文件名,如果是文件夹则及以 / 结尾,更多可忽略的文件示例可以查看:gitignore

查看状态

现在先来查看一下状态:

git status
位于分支 master

尚无提交

无文件要提交(创建/拷贝文件并使用 "git add" 建立跟踪)

这里的信息告诉我们当前位于 master 分支,没有文件需要提交,工作区是干净的

💡 「分支」和「工作区」是什么?这个不用急,后面都会提到的

一次提交

为了方便理解,我这里使用最简单的 Markdown 文件做演示:

如果你不知道什么是 Markdown 文件,这里做一个简要说明。这是一种非常简单的以 .md 文件名后缀命名的文本格式,如在 Word 中的 1~6 级标题以符号 # 写出,这样在写作时双手不用离开键盘就可以实现各自文本格式,下面是一个例子:

## 我是二级标题

### 我是三级标题

你好,加粗使用星号包裹,例如:**我是粗体**

当然支持的格式不止以上,更多格式可以自行了解一下。

现在添加一个 MD 文件:

touch READMD.md

使用命令 git statsu 查看状态可以得到:

位于分支 master

尚无提交

未跟踪的文件:
  (使用 "git add <文件>..." 以包含要提交的内容)
    .gitignore
    READMD.md

提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)

出现了「未跟踪的文件」,一般情况下在 Git 中文件有两种状态:有被 Git 纳入管理的「未追踪文件 (untracked) 」和被 Git 纳入管理的文件「已追踪文件 (tracked )」

也就是说,不是将文件丢进了 Git 初始化的文件夹,文件就被接管了,这和同步网盘不一样的

我打算将还没有内容的的 READMD.md 作为一个开始,成为第一个历史版本,所以不做修改直接将该文件提交到暂存区:

git add READMD.md

这时再次使用 git status

位于分支 master

尚无提交

要提交的变更:
  (使用 "git rm --cached <文件>..." 以取消暂存)
    新文件:   READMD.md

未跟踪的文件:
  (使用 "git add <文件>..." 以包含要提交的内容)
    .gitignore

READMD.md 提交到了暂存区,但 .gitignore 还没有,怎么办呢?

# 再次提交
git add .gitignore

# 但这样一个个提交也太麻烦了对吧,所以一次提交多个文件可以用空格隔开:
git add READMD.md .gitignore

# 或者直接使用 . 就可以将当前工作区的所有文件都提交到暂存区
git add .

再次使用 git status

位于分支 master

尚无提交

要提交的变更:
  (使用 "git rm --cached <文件>..." 以取消暂存)
    新文件:   .gitignore
    新文件:   READMD.md

好了现在要提交第一次版本了,使用命令

git commit -m "项目成立" -m "添加了空的 MD 文件和 .gitignore"

如果不使用 -m 选项会弹出一个交互式界面让你填写提交信息,使用两次 -m 则对应着提交信息的标题及内容

再次使用 git status

位于分支 master
无文件要提交,干净的工作区

到这里,项目就有了第一个历史版本,可以通过 git log 命令查看版本库的历史版本。

基本的工作流程

暂时停一下,捋一捋基本的工作流程

  1. 在工作区 (也就是你肉眼所见的文件夹) 做工作,改动文件;
  2. 将觉得修改好的、可以成为一次历史版本的文件添加到暂存区;
  3. 将暂存区提交到版本库成为一次历史版本;

然后再来试试,修改 README.md 我打算在里面写上一首诗,先起个标题吧:

题葡萄图

然后提交到暂存区:

git add .

然后想了想,光有标题还不行,要有内容啊,那么完善一下内容:

题葡萄图
半生落魄已成翁 独立书斋啸晚风
笔底明珠无处卖 闲抛闲掷野藤中

这首徐谓的《题葡萄图》就写好了,再次提交吧:

git add .

在把整首诗提交到暂存区后,想了想还是改动下加个标题吧:

## 《题葡萄图》
半生落魄已成翁,独立书斋啸晚风。
笔底明珠无处卖,闲抛闲掷野藤中。

如果现在不提交添加了标点符号的改动到暂存区,而是直接将现有暂存区的改动提交到版本库呢?

git commit -m '添加了徐谓的《题葡萄图》'

此时使用 git status 命令可以得知存在尚未暂存以备提交的变更,也就是指最后添加的标点符号的改动还在,而版本库中这首诗是没有标点符号的。

说明

  • 可以不断提交改动到暂存区,直到暂存区的所有改动足以称为一次历史版本,就可以提交到版本库;
  • 工作区的中的改动不会因为暂存区提交到版本库的操作而也一起添加成为历史版本,所以不要把 Git 当做一个同步网盘的概念来使用;

到这里,使用 Git 进行改动提交的工作流程你应该已经熟悉了,用是会用了,但还是有些概念上的疑问是不是?

工作区?暂存区?版本库?

一般来说「工作区」就是你肉眼所见的项目文件夹,「工作目录」、「Working Directory/Tree」、「WorkSpace」都是在说它

而「暂存区 (Staging Area)」有时也称为「缓存 (Cache)」或「索引 (Index)」

最后就是版本库,用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,本地提交的改动或从其它计算机克隆仓库时,就是这里的数据,而这些数据就以名为 .git 的隐藏文件夹的存放在项目根目录,所以有时也叫「Git 目录」。

一开始只要记住工作区、暂存区和版本库的叫法以及工作流程就好。

「暂存区」?

「工作区」和「版本库」都比较好理解,前者就是肉眼可见的文件及目录,而后者是存放历史版本的地方,为什么要在这个提交流程中加一个「暂存区」?

简单来说将准备好的改动提交到「暂存区」,这个改动可能是文件内容的一部分也可能是新增的一个文件,等提交到「暂存区」中的改动足以成为一次历史版本时,再一起提交到「版本库」。

举几个例子:

  • 假设在一堆改动中只有两个文件适合提交成为一次历史版本,如果没有「暂存区」要怎么区分操作呢?
  • 假设我在 README.md 中写好了一段使用说明并提交到了「暂存区」,但提交后我发现还有另一种更好的说明方式
    于是我可以在「工作区」写第二版,如果第二版满意可以再次提交到「暂存区」覆盖第一版,如果不满意也可以将「暂存区」的第一版恢复到「工作区」撤销所写的第二版,且重要的是这些操作并不影响历史版本,即你没有提交一个让你后悔的历史版本到版本历史中,又可以很好的尝试多个方案

一个好的历史提交应该是怎样的?

以下是一些建议:

  1. 一个历史应该是一次完整的改动
    不要将一件未做完的事情当作一个历史版本。如果你遇到别的紧急加塞任务,需要临时保存一下当前的改动,可以使用 Git 的「贮藏 (Stash)」功能;

  2. 一次提交仅对应一个相关的改动
    一次提交应该只对应一个相关的改动,不要把毫无关联的改动共同提交为一个历史版本;
    例如,如果将修改了文章的格式风格与编写了文章的总结内容作为一次提交,那日后想要回滚到文章末尾的这个版本但又不想要所修改的格式时,就需要花费额外的功夫来修改,这也会造成管理混乱;

  3. 高质量提交信息
    说明为什么需要这次改动以及概述这次改动了什么。这样提交者或者团队的其他成员就可以非常简单的了解改动的目的以及修改的内容;

重命名文件

假设要将项目中的 README.md 文件重命名成 README,如果直接在文件管理器中对它右键重命名,那么在 Git 中会处理成将一个已知跟踪的文件删除了,同时出现了一个未跟踪的文件。

要正确重命名文件可以在重命名后使用 git rm 删除原文件,然后添加重命名后的文件到暂存区,但最优的做法是直接使用 git mv 命令:

$ mv README.md README
$ git rm README.md
$ git add README

# 更推荐的做法是使用 git mv 命令:
$ git mv <旧文件名> <新文件名>
$ git mv READMD.md README

查看提交历史

git log

# 其他选项: short、full 和 fuller
git log --pretty=oneline

# 仅显示 5 条历史
git log --pretty=oneline -5

# 显示改动差异
git log -p

# 在日志旁以 ASCII 图形显示分支与合并历史
git log --graph

最后附上一个个人蛮喜欢的选项参数搭配:

git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
* d9e694d - (HEAD -> master) 添加了标点符号 (19 秒钟前) <Conners Hua>
* 01f69d8 - 添加了徐谓的《题葡萄图》 (4 分钟前) <Conners Hua>
* 8d7d833 - 项目成立 (20 小时前) <Conners Hua>

小结

本文的小结以及一些受篇幅限制没有提到的小技巧都在这了:

  • 使用 git status 查看状态;
  • 使用 git log 查看提交历史;
  • 使用 git add <文件>添加文件到「暂存区」;
  • 使用 git commit 提交修改成为历史,使用 -m 选项带上提交信息;
  • 删除文件需要使用 git rm <文件> 命令,如果删除的同时保留文件在工作目录中使用 --cached 选项如 git rm --cached <文件>
  • 重命名或移动文件使用 git mv <旧文件名> <新文件名> 命令;
  • 添加多个文件到暂存区可以以空格隔开文件名以达到添加多个文件的目的。当然更好的办法是使用 git add .git add -A,如果不想添加未跟踪的文件则使用 git add -u
  • 有一些文件总是不想被 Git 接管的,但又不想在未追踪文件列表里看到它们,就可以设置 .gitignore 文件以隐藏它们;
  • 如果想要直接提交而省去暂存区这一步可以使用 -a 选项:git commit -am '<提交信息>'
  • git status 命令有一个 -s 选项可以以一种简洁的方式显示文件的状态;
  • 要想删除项目里的未追踪文件可以先使用 git clean -dn 看看会删除什么文件,如果确认了使用 git clean -df