首页 面向新手的Git教程:Git 的文件状态
文章
取消

面向新手的Git教程:Git 的文件状态

我在网上经常看到一种言论,说一些工具的用法没必要学,用的时候查一下就行了。笔者认为,这句话要分情况,当一个工具不经常用,那确实没什么学的必要;然而如果一个工具使用的频率非常高,每天都会用到,那这个工具就非常值得花时间去系统的学一下了。古人云,磨刀不误砍柴工。程序员日常使用的 Git 就是这样一个值得系统学一下的“神兵利器”,如果只是简单使用,就像手持“倚天屠龙”,也只能当砍柴刀砍砍柴一样。

这里再多废话一下本教程中为什么使用命令行操作,而不是某个GUI。因为命令行是最通用的,不管你当前用什么操作系统,这些命令都是通用的。而 GUI 就不行了,不同的操作系统、不同的 IDE 提供的 GUI 菜单都会不一样,这样无形中就增加了沟通的成本。而且只要学会了用命令行,换任何一个 GUI 都能很快适应,但如果只学会了某个 GUI,换另一个 GUI 可能就会不适应。另外只有命令行实现了全部的 Git 操作,各种 GUI 客户端都只是实现了部分常用的 Git 操作。

TL;DR

在版本控制系统中,文件状态是一个非常基础,也是非常重要的概念。每次执行 Git 操作,我们都应该在清楚每个文件状态的情况下进行。

分布式版本控制系统

在版本控制系统的发展历史中,分布式版本控制系统出现前,集中式版本控制系统的应用非常广泛。其中,有一个非常出名的集中式版本控制系统是 Apache 的 Subversion(也叫 SVN)。现在仍使用 SVN 的企业应该已经非常少了,就连苹果的 Xcode Command Line 中都不包含 SVN 了,但在十年前,还能见到一些公司在用 SVN,市场证明了分布式版本控制系统更具优势。

所谓集中式和分布式,区别是版本仓库的位置。比如甲、乙、丙、丁四个人协作开发一个系统,集中式版本控制系统的版本仓库位于四个人都能访问的一个中央服务器上;而对于分布式版本控制系统,版本仓库不光在服务器一个地方,同时也在甲、乙、丙、丁的电脑上各有一份。我们在前文说的“存档”,也就是版本控制系统中的“提交”,就是往版本仓库中提交。所以在集中式版本系统中,提交操作必须联网,然后把修改上传到中央版本仓库中;而在分布式版本系统中,提交只是像本地的版本仓库中提交,不仅不需要联网,也不会有上传文件这种耗时操作,所以在体验上分布式版本控制系统也是完全碾压集中式版本控制系统的。

版本仓库与工作区

当我们从服务器git clone下一个项目,或者在一个新项目中git init后,项目根目录中会包含一个名为.git的文件夹,这个文件夹就是 Git 中的版本仓库。项目根目录中除.git文件夹外的其他内容,就是工作区中的文件。当我们执行git checkout从版本仓库中检出一个指定版本时,Git 会把工作区中的文件替换成对应版本的文件。

工作区中文件的状态

总的来说,工作区域中的文件可能存在 4 种状态,分别是未跟踪(untracked)、已暂存(staged)、已提交(committed)、已修改(modified)。

未跟踪状态指未纳入 Git 管理的文件,在工作区中新建的文件就是未跟踪状态,Git 需要你明确说明才会跟踪这个文件,这样可以避免一些临时性文件被纳入版本控制。除未跟踪外的其他状态都是已跟踪状态。

假如我在项目根目录新建一个名为demo.txt的文件,怎么明确告诉 Git 需要把demo.txt纳入版本控制呢?

1
git add demo.txt

执行git add后,Git 开始跟踪demo.txtdemo.txt文件的状态就变成了已暂存。暂存是 Git 中的一个特殊操作,暂存时会将指定文件的当前版本放入暂存区

如果demo.txt是一个临时文件,只是在不小心的情况下把它纳入了版本控制,Git 允许我们食用git rm这颗后悔药来取消跟踪并从工作区中删除该文件。

暂存(stage)这个单词有阶段之意,可以把暂存区理解为存储本阶段修改的地方,后续执行提交操作时,Git 使用暂存区中的文件生成一个commit,而一个commit就是 Git 仓库中的一个版本。

git add命令不仅可以把未跟踪的文件的状态变成已暂存,实际上git add命令可以把任何状态下的文件状态变为已暂存:比如已修改状态的文件也是通过git add命令暂存;另外在合并 Git 分支时,如果某个文件不能自动合并,会处于有冲突状态,在把文件冲突解决完后,也是使用git add命令把合并完的文件放入暂存区,这同时也是告诉 Git 这个文件中的冲突已解决。这也从侧面说明了命令行的简单性,如果换成是 GUI,光是git add可能会有“纳入Git管理”、“暂存修改”、“标记冲突为已解决”等多个菜单。

接下来,可以使用git commit -m "你的提交备注"为当前阶段的工作创建一个提交。提交后,本地的 Git 仓库中就保存了当前这个版本,后续可以在任何时刻把这个版本检出(checkout)。

可能有的同学会问,暂存和提交为什么不合并成一个操作呢?一个步骤直接把所有修改了的文件都提交到本地的 Git 仓库,这确实可以:

1
git commit -a -m "你的提交备注"

只需要在原来的commit命令中添加-a选项,就可以在创建提交前自动把已修改的文件暂存。需要注意的是,这里的自动暂存只会暂存已修改或删除的文件,而不暂存新建的处于未跟踪状态的文件,所以,即便有这个“更便捷”的操作方式,也不能应对所有情况,未跟踪的文件你必须明确告诉 Git 需要跟踪它,否则 Git 不会“自作主张”替你做这个决定。关于为什么不替用户做决定,而是将决定的权利交由用户这种设计为什么更好,下面单独加了一个小节些许废话

分暂存、提交两个步骤还带来一个好处:比如有两个文件A和文件B,文件A中实现了功能A,文件B中实现了功能B。我们先修改了文件A,对功能A做了升级,然后又修改了文件B,对功能B也做了升级,但这时我们想为功能A和B的修改分别创建一次提交。如果暂存和提交是一个操作,那就只能创建一个提交,这个提交中揉杂了功能A和功能B的修改。但像 Git 这样分成两步操作的话,我们可以先暂存文件A,然后创建一个提交,然后暂存文件B,再创建另一个提交,这就更自由灵活了。

提交后,文件的状态就变成了已提交。当我们下次又修改demo.txt文件后,其状态变为已修改。

些许废话

「雅各布·尼尔森是哥本哈根的人机交互博士,他拥有的美国专利高达79项目,大部分都是涉及让互联网更容易使用的方法,纽约时报称他为“Web易用性大师”,还被 Internet Magazine 称为“易用之王”」

「尼尔森十大原则是尼尔森分析了两百多个可用性问题后,从而提炼出的十项通用性原则,于1995年1月1日发表,分别为:状态可见、环境贴切、用户可控、一致性、易取、防错、高效灵活、优美且简约、容错、人性化帮助。这些原则主要用来评价用户体验的好坏,无论产品经理还是设计师,都可以根据这十大原则进行自查」

「尼尔森十大原则同时又被成为「用户界面设计的10种可用性启发式」,之所以叫做“启发式”,因为这些原则泛指经验法则,并非特定的可用性准则。2019年,在官方的描述视频中将尼尔森原则定义为「十大UX设计试探法」,将其合理运用有助于我们试探设计的边界,探索更多的可能性』

尼尔森十大原则中的用户可控原则,说的就是做决定的权利应交由用户,而不是我们的产品自作主张替用户做一些决定。但是我们日常使用的产品中,替用户做决定的现象屡见不鲜。比如某购物软件,为了保护用户隐私,上线“隐私号码”功能并默认开启,当用户去快递驿站取快递却发现通过自己的手机号码无法查询到快递。个人认为更合理的做法应该是在用户下单时弹出对话框提示用户是否使用“隐私号码”并记住用户的选择应用到后续的购物操作中。

文件状态查看

在了解 Git 工作区域中文件可能存在的几种状态以及在各操作下文件状态如何变化后,我们还需要知道如何查看工作区中文件的状态。Git 提供了git status帮助我们,下面是当工作区域中所有已跟踪的文件都是已提交状态,且所有未跟踪的文件都已明确告诉 Git 不需要跟踪情况下的一个示例:

这里提到的“明确告诉 Git 不需要跟踪哪些文件”的方式我们另起一篇介绍,敬请关注。

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

这个命令打印了三句话,描述了:

  1. 当前工作区在 master 分支上
  2. 你本地的分支与origin/master分支保存同步(既不领先,也不落后),其中的origin/master代表名为origin远程仓库中的master分支,新克隆下来的仓库,其源远程仓库默认名为origin
  3. 没有内容需要提交,工作目录干净

描述的够清楚了吧?所以别一看到英文就发怵!试着去读读,大不了让 AI 帮你翻译一下。

这里需要注意一下“工作目录干净”这句话,这里的干净指的是目录下的所有已跟踪文件都是干净的,且所有未跟踪的文件都是用户明确不需要跟踪的。

如果有文件处于非“干净”状态,git status会额外列出,例如对于新建、已修改等未暂存的文件,git status会额外打印列出来:

1
2
3
4
5
6
7
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

  	new file: fileA
  	modified: fileB

对于已暂存的文件,则打印:

1
2
3
4
5
Changes to be commited:
	(use "git reset HEAD <file>..." to unstage)

	modified:   fileC
	deleted: 	fileD

可以看到,git status不光列出了非干净文件的状态,还很贴心的给出了当前状态下用户可以执行的操作及命令。在git status命令上添加-s--short参数,可以使输出更加简洁:

1
2
3
4
5
6
7
8
# git status --short 或
git status -s

 M fileA
MM fileB
A  fileC
M  fileD
?? fileE

够简洁吧,前面是状态,后面是文件名。其中:

  • ??代表新增的文件,且用户未明确表示是否要跟踪该文件,所以 Git 打了两个问号
  • A 代表新增的文件,且用户明确表示要跟踪该文件,A 即 Add
  • MMM M也好区分,左边的M代表已修改且已暂存,右边的M代表已修改未暂存,两个MM则表示已修改已暂存后,接着又修改了该文件且第二次修改后未暂存。有点绕,M 即 Modify

因本人能力有限,错误之处在所难免,如有问题,请在评论区不吝赐教。另因公众平台无法对历史文章重新编辑,可点击查看原文查看最新版本。

本文由作者按照 CC BY 4.0 进行授权

面向新手的Git教程:打不开GitHub?

面向新手的Git教程:Git 忽略文件