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 文件。