# 基本概念

# 1. 什么是版本控制系统

版本控制系统(VCS)是将『什么时候、谁、对什么文件做了怎样的修改』这样的信息以版本的形式保存并进行管理的系统。

简单来说,版本控制系统会去记录它所管理的文件的『历史版本』。

版本控制系统 “不是网盘,而胜似网盘” :

  • 它和网盘的相同点在于:它可以位于局域网,或者是外网上的一台服务器上,你和你的小伙伴们可以在不同的地方、不同的电脑上,以它为 “中介、桥梁” 共享文件。

  • 它胜似网盘的地方在于:它能记录你和你的小伙伴们对文件修改的所以历史版本,并且你可以将你的文件恢复到你感兴趣的某个历史版本。

Git 并非唯一的版本控制系统,它的前辈是 Subversion 。2014 年 Git 的市场占有率首次超过 Subversion ,成为主流。

Git 相较于 Subversion 的先进之处在于:

  1. Subversion 中的版本信息仅存在于中央仓库,而 Git 中所有的本地库中都有完整的历史版本信息。

  2. Subversion 中的 “分支” 很重,创建分支、删除分支、合并分支等分支相关操作很慢,而 Git 中的分支很轻,分支操作是 Git 的拳头特性。

# 2. 创建本地仓库

Git 可以管理任何一个文件夹(及其中内容),只要在该文件夹中执行 git init,就可以让 Git 完成管理前的准备工作。

$ git init

执行成功后,你会看到类似如下信息:

Initialized empty Git repository in ...

Git 会在它管理的文件夹下创建名为 .git 的子文件夹,这个文件夹也就是逻辑上的『本地仓库』。它里面会存放被 Git 所管理的文件的相关信息(例如,历史版本)

警告

你不要自己去操作 .git 目录,更不要随意将 .git 目录删除。这个目录下的内容是 Git 来使用和操作的。

当你执行完 git init 命令之后,当前目录就位于 Git 的监管范畴内,这个目录中的文件的变动(新创建的、被删除的、内容有更新的)都会被 Git 察觉。

在 Gitkraken 中打开这个刚刚初始化的 Git 仓库效果如下:

git-GitKraken-01

git-GitKraken-02


操作 Git 的基本流程是:

  • Step 1:先变动 Repo 目录中的原有内容。所谓的 “变动” 包括:新增文件、修改原有文件内容、删除已有文件等。

    对于你的 “变动” 行为,在 GitKraken 中你可以观察到它们:

    git-GitKraken-04

  • Step 2:然后执行 git add 命令。git add 命令会把文件内容加入 Git 系统的『暂存区(也叫索引区,Index)

    在 GitKraken 的图形化界面操作中,对应操作是:

    git-GitKraken-05

  • Step 3:接着就可以执行 git commit 命令,将文件的内容存入『本地仓库(.git)
    于是文档库中就多出来一份文件的新版本。

    在 GitKraken 的图形化界面操作中,对应操作是:

    git-GitKraken-06

图片

插曲

git 的 add 命令,有一个同义词命令叫 stage ,它俩的功效是一样的。

# 3. Git 中文件的受管状态

Git 会将文件(文件夹)的状态分成以下 3 类:

  • tracked:被追踪

    tracked 状态意味着 Git 正在关注着这个文件。

    你对这个文件的任何改动,都会被 Git 发现。Git 会进一步要求你提交你的改动,或撤销你的改动。

  • ignored:被忽略的

    ignored 状态意味着 Git 完全不管这个文件,在 Git 看来它就跟不存在一样。

  • untracked:不被追踪的

    untracked 状态是所有文件的初始状态。

    逻辑上,它是 tracked 和 ignored 状态『之前』的一种状态,逻辑上,它是一种临时状态:你既没有要求 Git 监管它,又没有要求 Git 忽略它。

    对于这个文件,Git 也是一脸懵逼不知道接下来该不该监测文件的变动,正因为如此,untracked 是一种临时状态,不应该长期存在。

这里有 2 点需要强调的是:

  1. 所有的文件的初始状态都是 untracked

  2. 在正常情况下,文件不应该长期处于 untracked 状态,应尽快转变为 trackedignored

在 GitKraken 中如果你 “动” 过 Repo 目录中的文件的话,你所看到的图形化信息类似如下:

git-GitKraken-03

上述截图中的 3 个小图标的含义显而易见:修改、删除、新增。

对于新创建的文件,它们都是处于 untracked 状态的,接下来它们应该尽快通过下述 2 种方法之一切换成 ignored 状态,或 tracked 状态。

untracked 
└──> ignored 

必须先在 Repo 文件夹中创建一个名为 .gitignore 的文件(必须是这个名字),
然后把要忽略的文件逐一列在这个文件中,一个文件一行,支持通配符。
untracked 
└──> tracked 

可以使用 git add + git commit 命令,将它提交给 Git 监管即可。

# 4. 新增一个文件的历史版本

git add + git commit 的作用简单来说,就是将一个文件的当前内容提交给 Git :

  • 对于 untracked 状态的文件(即新增的文件),提交后会变成 tracked 状态;

  • 对于 tracked 状态的文件(即原有的文件),其历史版本记录则会演进一步。

$ git add "readme.txt"
$ git commit -m "message"

对于一次『提交』而言,提交消息(message)是必须的 。否则,Git 会拒绝你的这次提交。

在 GitKraken 的图形化界面中,git commit 提交操作的功能在这个区域:

git-GitKraken-07

注意

一个完整的提交信息包括 header、body、footer 三部分,你至少要保证提供 header ,否则,你无法提交。

要新增『一次提交』之所以要同时使用 git addgit commit 是因为在将文件当前的内容添加成至本地仓库之前,要 将其添加至 暂存区

在 GitKraken 中暂存区的是如下区域:

git-GitKraken-08

git commit只会』将暂存区的文件的内容提交至本地仓库进行保存。

git-GitKraken-09

git-GitKraken-10

# 5. 使用 Git 的核心思想:一致性

当你改动了工作区(硬盘上)的文件的内容之后,你可以使用 git status 命令查看文件的状态。你会看到类似如下信息:

On branch master
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)

        modified:   新建文本文档.txt

这里除了能看到 Git 监控到的文件的变动 modified: 新建文本文档.txt 之外,在这条信息的上面,你还能看到 Git 为你给出的 2 条建议:接下来请使用 git add <file>... 命令,或使用 git checkout -- <file>... 命令。

这两条建议的背后,体现出 Git 的一个核心『关注点』:Git 希望你能保持工作区和本地仓库的一致性

在初始状况下,工作区和本地仓库的内容是一致的,当你改动工作区的文件后,工作区和本地仓库的内容就不再一致了。

对于此情况,Git 希望你将它们重新『调整』成一致。

至于如何『调整』,有 2 种方案(这也就是 Git 对你给出的 2 条二选一的建议)

  1. 使用 git add <file>... 命令(及后续的 git commit 命令),将你对工作区的改动提交到本地仓库。这样,工作区和本地仓库将会重新一致。这也就意味着,本地仓库的版本将向前演进。

  2. 使用 git checkout -- <file>... 命令用本地仓库(的最新、最近版本)的内容覆盖你的工作区的内容。这样,工作区和本地仓库将会重新一致。这也就意味着,你的工作区的内容的变动将会被覆盖、舍弃。

你在使用 Git 对你的文件夹、进行版本控制的整个使用过程中,你的工作区和本地仓库的状态的一致性的变化状态将是如下情况:

一致 > 不一致 > 重新一致 > 不一致 > 重新一致 > ... > 一致

git_3

# 6. .gitignore 文件

之前已经提到过,.gitignore 是用于屏蔽某些文件被纳入到 Git 管理范围下的配置文件。

简单来说,.gitignore 文件就是一个『黑名单』,在其中列举的文件都不会被 Git 管理,Git 不会关注这些文件的创建、删除、改动,也不会将它们存入到本地版本库,更不会将它们上传到远程仓库。

# 创建 .gitignore 文件

注意

.gitignore 文件必须叫 .gitignore ,一定不能错

在 window 系统中,windows 不允许直接新建文件名以 . 开头的文件(Linux 不存在这个问题)。因为,Windows 会误以为 .gitignore 是文件的后缀,而你的文件是没有名字的!

所以这种情况下,你需要 “动脑子” 创建。你可以在 git bash 中通过 touch 命令创建,也可以从别的项目中拷贝一个过来。

# .gitignore 语法

.gitignore 文件的基本语法:

##:表示注释
/:目录层级
*:通配符

例如:

  • 忽略所有后缀名为 log 的文件,无所谓文件名。无论它位于什么层次结构。

    *.log
    
  • 忽略『根目录』下的 target 文件夹

    /target/
    

    这里最后的 / 非必须。不过个人建议还是加上,因为可以暗示出它是一个文件夹。

  • 忽略所有名为 target 的文件夹。无论它位于什么层次结构中。

    target/
    

补充

.gitignore 文件中有一个 ! 表示的 “非” 的规则,对于它的使用相较而言比较复杂,暂时不要求掌握。

# 注意事项

  1. .gitignore 配置文件的根目录就是当前 Git 工程目录。.gitignore 配置只对当前 Git 工程有效。

  2. 在配置语句的前后面添加空格、Tab、注释等,会导致当前行的配置语句失效。所以,不要添加非必要的空白符。

  3. 配置语句对已经 add、commit 的文件无效。

# .gitignore 模板

见 《模板》

# 7. 取出历史版本

从本地版本库中取出文件只需要一个指令:

git checkout [ 版本标识 | 标签 ] <文件1>, <文件2>, ...

需要特别提醒的是,磁盘文件夹中的文件会被取出的文件『覆盖(覆盖、覆盖),因此你对该文件作出的修改会丢失。

例如,本地版本库中的文件是 80 行的,你本地的文件被你改成 100 行(但未提交),执行 git checkout 之后,你的本地文件会变成 80 行。

git-GitKraken-11

# 8. 从 git 中删除文件

git rm 用于删除文件,删除行为分为『弱删除』和『强删除』。

  • git rm --cache 是『弱』删除。

    它表示让 Git『不再监管』某文件/文件夹,而该文件/文件夹在磁盘上『仍存在』。

  • git rm 是强删除

    它表示告知 Git『不再监管』某文件/文件夹的同时,还从硬盘上『删除』此文件/文件夹。