git基础
git使世界上最受欢迎的版本控制系统,版本控制系统记录随着时间的推移对我们的代码所做的更改。他能知道谁在何时何地进行了那些修改。如果我们有一个版本搞砸了,我们可以轻松地将项目还原到之前的版本。在早期,没有这种版本控制系统,我们不得不不断地以各种方式存储整个项目的副本——这会严重地降低效率尤其是多个人在同一个项目上工作时。
版本控制系统分为2类:分布式和集中式。在集中式系统中,所有人必须连接到中央服务器获取代码的最新副本。比如说 Subversion和Team Foundation Server 。但集中式版本控制系统的问题就是服务器必须在线,否则我们将无法协作
在分布式系统中,我们就能很好的规避这些问题。因为每个团队成员都有一个项目副本及其历史记录。如果我们的中央服务器离线了,我们可以直接与他人同步我们的工作。比如Git和Mercurial

git是这些版本控制器中最流行的,因为git 免费开源,速度快且可扩展。并对分支合并操作十分友好。
那么怎么使用Git呢?
- 我们可以在命令行上使用 Git,也就是在终端或者CMD上使用。
- 我们可以在代码编辑器或者IDE上的终端使用
- Git有自身的GUI可以使用
- 第三方软件GitKraken、Sourcetree等 GUI (但是我们主要还是使用命令行因为有些远程服务器上不方便安装GUI,使用命令行会更快、更方便)
在使用git之前,我们要设置一些信息
- 名字
- Default Editor
- Line Ending
我们可以为三个不同层级设置这些信息
- System 最高级 面向所有的用户
- Global 适用于当前用户的所有repositories
- Local 当前repository
1 | git config --global user.name "名字" |
这两行是设置global邮箱和名字的
1 | git config --global core.editor "code --wait" |
这一行是用来告诉终端窗口在我们关闭 vs code界面之前保持等待。在gitbash 中输入code,就会自动跳转至 vsc
1 | git config --global -e |
这行代码是用来看全局配置的.输入以后vscode会跳出一个 .gitconfig文件。

我们还需要设置
1 | git config --global core.autocrlf true |
这是因为不同操作系统下,处理行尾结束符的方法是不同的
首先了解回车和换行
LF:Line Feed 换行
CRLF:Carriage Return Line Feed 回车换行键
- windows下:CRLF(表示句尾使用回车换行两个字符,即windows下的”\r\n”换行)
- unix下:LF(表示句尾,只使用换行)
- mac下:CR(表示只使用回车)
在git下处理换行有三种情况:
- 设置为true
- 当设置成true时,这意味着你在任何时候添加(add)文件到git仓库时,git都会视为它是一个文本文件(text file)。它将把CRLF变成LF。当有人check代码的时候,又会将LF变成CRLF。所以在 windows下使用这个模式。
- 设置为false
- 当设置成false时,line endings将不做转换操作。文本文件保持原来的样子。
- 设置为input
- 添加文件git仓库时,git把CRLF变成LF。当有人check代码的时候还是 LF 方式。因此在window操作系统下,不要使用这个设置。而mac使用这个模式。

Getting Help
这是 git -config 的文档。
https://git-scm.com/docs/git-config
或者使用 git config —help 来查看这个文档
git config -h 则会显示一个比较简短的总结。
1 | usage: git config [<options>] |
Creating Snapshots
Initializing a repository
我们现在要做的是为我们的project创建一个目录。
我们新建一个文件夹:
1 | mkdir moon |
然后再
1 | git init |
就是在这个文件夹中新建了一个仓库。在win下就显示为
1 | Jason@JasonsT90 MINGW64 /d/projects/moon (master) |
Git Workflow
现在来介绍一下 Git的工作流程
左边是我们的文件夹,最右边的是Git的仓库。在Git中有一个很多其他版本控制器没有的Staging Area。

当我们对一些文件作出修改之后,我们会将文件暂存在Staging Area中,等确认无误之后,我们才会对Staging Area中的文件进行一个snapshot ,将他们永久的保存在repository当中。有了这个机制之后,我们就能反复确认是否有不应该出现的改变。然后就可以在舍弃这些内容
下面是一个真实的例子,我们有两个文件file1和file2,我们首先将他们放在staging Area中,确认无误后我们递交一个commit,也就是初次提交:git commit -m "Initial commit"
以后,我们将如果修复了bugs、重构了代码,就在第一次的基础之上commit

我们会有一个错误的观念:提交了commit之后staging area就会清空。实际上并不是这样,staging area中的文件仍然是保留的。所以我们可以这样理解:再递交了commit之后, repository 中的文件和staging area中的文件是一模一样的(没递交之前可能有差别,因为staging area中的文件会有更新)
然后我们修改了一个文件 file1 通过代码git add files1将修改后的文件更新至staging area然后再git commit来提交第二个版本,备注了 ”Fixed the bug that…”

现在我们删除本地的 file2文件,再通过git add file2更新至staging area,我们会感到很奇怪,明明是要做删除,但是为什么用 git add file2 呢? 这是因为git会自动检测 file2 是否在本地。因为本地没有file2,所以在staging area中的file也会删除。这里的add更偏向于更新的意思,而不是单纯的添加。

现在我们的repository中有3个commit了,每一个commit包含了一个git生成的独一无二的ID以及其他信息

我们也许会感到很奇怪git能保存下所有的snapshot,那不会非常消耗内存嘛?实际上 git的内存管理是非常有效的。这是因为git会压缩内容并且不存储重复的内容
Staging Files
我们先创建两个文件 file1和file2,echo hello>>file1.txt 然后利用 git status查看现在的状态。我们发现现在没有commits,现在两个文件也是untracked的。也就是说,现在staging area中还没有文件

我们可以使用 git add file1.txt file2.txt 来添加这两个文件
也可以 git add *.txt 来添加所有扩展名为 txt 的文件。
当然,我们也可以直接 add . 来添加所有文件。但是对这行命令需要注意,因为有些大文件可能是我们不想要的。而且如果递交了大文件有可能会出错。
我们这里只有两个文件,所以我们用 git add . 即可
然后我们看看 git status ,我们发现刚才没有 的文件变成了绿色,说明已经被添加至staging area当中了
、
现在我们使用 echo 来对file1.txt进行修改, echo world>>file1.txt 再用 git status 查看状态。

我们发现有两个file已经在staging area中了,但是还有一个已经修改过的文件没有提交给 staging area。现在的状态就如下图所示:本地有更新,但是staging area中的文件还未被更新

那么我们敲入 git add .并再次查看状态,发现刚刚修改的文件已经躺在staging area中了

Committing Changes
现在 staging area保存了我们的文件,然后我们利用 commit 命令来将这些文件交给repository
我们可以用 git commit -m "description" 来提交。有时候短短一行description是不够的(修复 bugs),我们可以 直接 git commit 然后 git bash 会唤醒 vscode,我们可以在vscode中做更为详细的描述

保存关闭之后,gitbash就会成功commit
Committing Best Practices
我们每次commit的代码不要太大或者太小:也就是说不要修改一次代码就commit一次或者写了好几天代码才commit一次,这都不太好。我们要保持一个合理的更新频率(大工作量下每天5-10次),因为如果我们发现了一些严重问题我们还能够”读档“,”回滚“
而且我们要做到不把两个可以解决的问题放到一个commit当中,比如我现在修复了一个bug,又发现一个排版错误,我们不能把这两个问题合并起来commit 一次。而要分两次commit。
此外我们还要养成对每次commit创建有意义的commit messages(description) 。因为这些信息会显示commit的信息和历史。对未来版本的回退等会更加友好。
Skipping the Staging Area
我们初学者常常会问,我们一定要每次把我们修改过后的文件放在staging area后才能commit嘛?
事实上我们不必要,我们可以跳过 staging agea直接commit。但做这个行为之前我们必须百分之百保证我们的代码没有问题。

我们可以用命令行
git commit -am "Fix the bug that prevented the users from signing up"
-am 也可以写成 -a -m : -a 代表着all,-m 代表 message
Removing Files
有时候我们想删除我们不想要的代码,于是我们需要删除一些文件。
先利用 rm file2.txt 删除 file2.txt,然后我们利用 git ls-files 看看现在 staging area中的文件 :
因为没有更新,所以现在staging area中还是有两个文件。
现在我们 git add file2.txt 再次 git ls-files ,这时候staging area中就只存在一个文件 file1.txt 了
当然,除了 rm file2.txt 之外,我们可以用 git rm file2.txt 直接将本地文件夹和staging area中的file2.txt删除。 git rm 和git add后面都可以跟多个文件或者模板
Renaming or Moving Files
和 删除文件类似,重命名文件或者是移动文件(切换文件类型) 也有两种方式
第一种是用Unix代码 mv file1.txt main.js 将 file1.txt 转换成main.js 。这时候我们查看git status。我们发现file1.txt已经被删除了,而新增了一个文件main.js. 而这个文件还没有被放进staging area,是untracked的。

我们需要 git add main.js 将文件放入到 staging area当中
第二种方法就是git自己的命令 git mv main.js file1.js 再次查看 git status 我们发现git会直接在本地和staging area同时更新

Ignoring Files
在很多的项目中,我们需要告诉git有些文件需要被忽略。比如日志文件或者机密二进制文件等。我们能通过一些命令来实现。
首先我们新建一个文件夹并在里面写一个log文件
1 | mkdir logs |
然后我们需要告诉git忽略logs中的文件:
有一个专门的文件叫做 .gitignore 在这个文档里面记录的文件名都不会被git放到staging area中
我们可以通过命令echo logs/ > .gitignore 让logs这个文件夹的内容全部处于被忽略状态
利用 code .gitignore 可以在vscode 中打开 .gitignore
我们也可以手动向.gitignore 中添加我们希望git要忽略的文件。比如 main.log 或者 *.log 等。最后我们需要将.gitignore 加入到staging area 中,再commit之后发现 logs文件夹确实没有被加入进来。

但是需要注意得是,.gitignore 只能对于并不在repository中的文件有效。如果有一个文件已经在repository中了,然后再将他添加到 .gitignore 中去,这是无效的。
下面我们来操作一下:
首先将bin文件夹和里面的一个hello.bin 文件commit到 repository当中。
1 | mkdir bin |
我们再 .gitignore 中将 bin/ 加入并 commit
1 | git add . |
现在我们修改hello.bin ,再新建一个app.bin文件。git status 之后,会发现git竟仍然追踪者 bin中的hello.bin文件但是并不会追踪在bin中的新建的 app.bin 文件。

为了解决这个问题,我们需要删除躺在 staging area当中的bin文件夹。
1 | git rm --cached -r bin/ |
—cached代表着删除的范围是 staging area, -r是删除文件夹的命令。 git status 之后,发现在staging area中的hello.bin 就被删除了(本地文件没有被删除!)

如果我们再次修改bin中的 hello.bin 文件,git也不会再追踪bin中的文件了。
Short Status
我们修改和新建一些文件,然后 git status 发现文字很多,看起来比较繁杂。我们可以利用 命令git status -s 来精简化它们
我们看到精简过后的status只有两行 。这两行也很容易理解,M files1.js 代表files1.js 被Modified了,而 ?? file2.js 说明file2.js 是未被追踪的。

Viewing Staged and Unstaged Changes
在我们将staging area 中的文件提交之前,我们需要再三回顾我们的代码,因为我们不想把有瑕疵的代码提交。那么我们怎么能看到在staging area当中的一行行代码2呢? 我们需要使用diff命令
git diff --staged

我们发现这样的文档是比较难阅读的。一般我们会用 GUI 来运行diff命令。我们现在看看怎么来阅读这些文档
我们看到第一行是 diff —git a/file1.js b/file2.js ,说明我们能在比较同一份文件不同拷贝的内容差别。a是比较早的copy,b是比较新的copy(现在存放在staging area当中的copy )
@@ -1,3 +1,5 @@ 告诉我们那里发生了改变 负号代表了旧的copy,正号代表了新的copy
1,3 代表从第一行开始,一共三行;1,5 代表从第一行开始一共五行
对于 file2.js 文件,因为这是新建的,所以显示为 —- /dev/null 即不存在老的copy
那么如何看本地文件的修改内容呢? 还是用 diff 命令,不过 这次命令变为 diff --git
Visual Diff Tools

我们使用vscode来查看diff命令。首先我们来做一些设置
1 | git config --global diff.tool vscode |
第一行命令是让我们将difftool设置为vscode 并对所有的repository适用
第二行命令是告诉git怎么唤醒vscode帮助我们diff文件。—wait 告诉终端保持等待状态直到我们关闭vscode为止,$LOCAL 和 $REMOTE 则代表了老的copy和新的copy
然后我们用 git config --global -e 打开vscode

我们发现diff 后面并没有$LOCAL 和 $REMOTE我们再把他补全

然后我们用 git difftool --staged 来让vscode充当我们的diff工具



但是现如今使用Diff工具的机会不多,大多数编辑器或者IDE都支持查看 staged 和 unstaged changes
Viewing History
我们已经commit不少次了,那么我们怎么才能看这些commit的信息呢?
使用命令 git log
如果觉得信息太繁杂,我们可以使用命令 git log --oneline
我们看到这个顺序是越上面的越晚提交。

如果我们想把最早递交的版本放到第一行,可以使用
git log --oneline --reverse

Viewing a Commit
刚才我们看了整体递交的信息,但是如果我们想看看某一个特定commit所做的改变该怎么办呢?
我们看到在每个commit之前有一串序列。这就是这个commit的ID,我们用命令 git show ID 来查看这个commit的信息
如果我们只想看最后一次commit ,我们可以 git show HEAD
或者我们想看倒数第n次的我们只要命令 git show HEAD~n-1 就行了
如果我们不想看commit更新了什么,我们只想看特定文件中的所有代码,我们可以再原来的基础上加上冒号和文件名即可。比如 git show HEAD~1:.gitignore 就是查看倒数第二次提交的 .gitignore 文件。
我们也可以使用 git ls-tree 来查看一个commit中的所有文件

我们看到在这个commit当中有三个Objects,两个文件和一个bin文件夹
git 有四种类型的Objects
- Commits
- Blobs(Files)
- Trees(Directories)
- Tags
Unstaging Files
在 git add . 之后,我又修改了 file1.js ,那么现在的 status 就是一个绿色的M一个红色的M。现在我想撤回第一个 git add . 的命令,也就是将 file1.js 从staging area取出再放到本地。

我们可以使用 命令 git restore --staged file1.js 就可以了

如果我再 git restore --staged file2.js ,那么我们会发现file2也不会被git追踪了

Discarding Local Changes
如果我们现在对本地文件进行了改变,但是发现这个改变有错误,想要取消。也可以用 restore命令来实现。其原理就是从 staging area中拷贝一份,再复制到 working directory
git restore file1.js
现在我们对本地的所有文件 执行命令 git restore . 却发现file2.js 这个问题。这是因为本来 file2.js 就不在staging area当中了。git不知道从哪里去复制。
为了解决这个问题,我们可以使用 git clean -fd 来完成操作。这个操作其实就是将 file2.js 从我们的文件夹中删除。
Restoring a File to an Earlier Version
现在我们要演示一下如果我们误操作删除了一个本地文件,我们怎么利用git恢复到之前的版本。我们删除文件最好使用 git rm 因为git rm会将文件同时从staging area 和 working directory 中删除。

但是现在我发现这个文件是不该删除的!那么怎么回退呢??
我们首先查看日志我们看到 ID为 6e72a9b 这个版本是我们要恢复的。

回复命令如下:usage: git restore [<options>] [--source=<branch>] <file>...
我们用命令 git restore --source=HEAD~1 file1.js 来恢复从HEAD开始数上一个版本中的file1.js 文件。
Creating Snapshots with VScode
