# 基本概念
# 1. 什么是版本控制系统
版本控制系统(VCS)是将『什么时候、谁、对什么文件做了怎样的修改』这样的信息以版本的形式保存并进行管理的系统。
简单来说,版本控制系统会去记录它所管理的文件的『历史版本』。
版本控制系统 “不是网盘,而胜似网盘” :
它和网盘的相同点在于:它可以位于局域网,或者是外网上的一台服务器上,你和你的小伙伴们可以在不同的地方、不同的电脑上,以它为 “中介、桥梁” 共享文件。
它胜似网盘的地方在于:它能记录你和你的小伙伴们对文件修改的所以历史版本,并且你可以将你的文件恢复到你感兴趣的某个历史版本。
Git 并非唯一的版本控制系统,它的前辈是 Subversion 。2014 年 Git 的市场占有率首次超过 Subversion ,成为主流。
Git 相较于 Subversion 的先进之处在于:
Subversion 中的版本信息仅存在于中央仓库,而 Git 中所有的本地库中都有完整的历史版本信息。
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 的基本流程是:
Step 1:先变动 Repo 目录中的原有内容。所谓的 “变动” 包括:新增文件、修改原有文件内容、删除已有文件等。
对于你的 “变动” 行为,在 GitKraken 中你可以观察到它们:
Step 2:然后执行 git add 命令。git add 命令会把文件内容加入 Git 系统的『暂存区』(也叫索引区,Index)。
在 GitKraken 的图形化界面操作中,对应操作是:
Step 3:接着就可以执行 git commit 命令,将文件的内容存入『本地仓库』(.git)。
于是文档库中就多出来一份文件的新版本。在 GitKraken 的图形化界面操作中,对应操作是:
插曲
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 点需要强调的是:
所有的文件的初始状态都是 untracked 。
在正常情况下,文件不应该长期处于 untracked 状态,应尽快转变为 tracked 或 ignored 。
在 GitKraken 中如果你 “动” 过 Repo 目录中的文件的话,你所看到的图形化信息类似如下:
上述截图中的 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 提交操作的功能在这个区域:
注意
一个完整的提交信息包括 header、body、footer 三部分,你至少要保证提供 header ,否则,你无法提交。
要新增『一次提交』之所以要同时使用 git add 和 git commit 是因为在将文件当前的内容添加成至本地仓库之前,要 先 将其添加至 暂存区 。
在 GitKraken 中暂存区的是如下区域:
git commit
『只会』将暂存区的文件的内容提交至本地仓库进行保存。
# 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 条二选一的建议):
使用
git add <file>...
命令(及后续的git commit
命令),将你对工作区的改动提交到本地仓库。这样,工作区和本地仓库将会重新一致。这也就意味着,本地仓库的版本将向前演进。使用
git checkout -- <file>...
命令用本地仓库(的最新、最近版本)的内容覆盖你的工作区的内容。这样,工作区和本地仓库将会重新一致。这也就意味着,你的工作区的内容的变动将会被覆盖、舍弃。
你在使用 Git 对你的文件夹、进行版本控制的整个使用过程中,你的工作区和本地仓库的状态的一致性的变化状态将是如下情况:
一致 > 不一致 > 重新一致 > 不一致 > 重新一致 > ... > 一致
# 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 文件中有一个 !
表示的 “非” 的规则,对于它的使用相较而言比较复杂,暂时不要求掌握。
# 注意事项
.gitignore
配置文件的根目录就是当前 Git 工程目录。.gitignore
配置只对当前 Git 工程有效。在配置语句的前后面添加空格、Tab、注释等,会导致当前行的配置语句失效。所以,不要添加非必要的空白符。
配置语句对已经 add、commit 的文件无效。
# .gitignore 模板
见 《模板》
# 7. 取出历史版本
从本地版本库中取出文件只需要一个指令:
git checkout [ 版本标识 | 标签 ] <文件1>, <文件2>, ...
需要特别提醒的是,磁盘文件夹中的文件会被取出的文件『覆盖』(覆盖、覆盖),因此你对该文件作出的修改会丢失。
例如,本地版本库中的文件是 80 行的,你本地的文件被你改成 100 行(但未提交),执行 git checkout 之后,你的本地文件会变成 80 行。
# 8. 从 git 中删除文件
git rm 用于删除文件,删除行为分为『弱删除』和『强删除』。
git rm --cache 是『弱』删除。
它表示让 Git『不再监管』某文件/文件夹,而该文件/文件夹在磁盘上『仍存在』。
git rm 是强删除
它表示告知 Git『不再监管』某文件/文件夹的同时,还从硬盘上『删除』此文件/文件夹。