git基础

git基础

git使世界上最受欢迎的版本控制系统,版本控制系统记录随着时间的推移对我们的代码所做的更改。他能知道谁在何时何地进行了那些修改。如果我们有一个版本搞砸了,我们可以轻松地将项目还原到之前的版本。在早期,没有这种版本控制系统,我们不得不不断地以各种方式存储整个项目的副本——这会严重地降低效率尤其是多个人在同一个项目上工作时。

版本控制系统分为2类:分布式和集中式。在集中式系统中,所有人必须连接到中央服务器获取代码的最新副本。比如说 Subversion和Team Foundation Server 。但集中式版本控制系统的问题就是服务器必须在线,否则我们将无法协作

在分布式系统中,我们就能很好的规避这些问题。因为每个团队成员都有一个项目副本及其历史记录。如果我们的中央服务器离线了,我们可以直接与他人同步我们的工作。比如Git和Mercurial

git是这些版本控制器中最流行的,因为git 免费开源,速度快且可扩展。并对分支合并操作十分友好。

那么怎么使用Git呢?

  • 我们可以在命令行上使用 Git,也就是在终端或者CMD上使用。
  • 我们可以在代码编辑器或者IDE上的终端使用
  • Git有自身的GUI可以使用
  • 第三方软件GitKraken、Sourcetree等 GUI (但是我们主要还是使用命令行因为有些远程服务器上不方便安装GUI,使用命令行会更快、更方便)

在使用git之前,我们要设置一些信息

  • 名字
  • Email
  • Default Editor
  • Line Ending

我们可以为三个不同层级设置这些信息

  • System 最高级 面向所有的用户
  • Global 适用于当前用户的所有repositories
  • Local 当前repository
1
2
git config --global user.name "名字"
git config --global user.email 邮箱

这两行是设置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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
usage: git config [<options>]

Config file location
--global use global config file
--system use system config file
--local use repository config file
-f, --file <file> use given config file
--blob <blob-id> read config from given blob object

Action
--get get value: name [value-regex]
--get-all get all values: key [value-regex]
--get-regexp get values for regexp: name-regex [value-regex]
--get-urlmatch get value specific for the URL: section[.var] URL
--replace-all replace all matching variables: name value [value_regex]
--add add a new variable: name value
--unset remove a variable: name [value-regex]
--unset-all remove all matches: name [value-regex]
--rename-section rename section: old-name new-name
--remove-section remove a section: name
-l, --list list all
-e, --edit open an editor
--get-color find the color configured: slot [default]
--get-colorbool find the color setting: slot [stdout-is-tty]

Type
-t, --type <> value is given this type
--bool value is "true" or "false"
--int value is decimal number
--bool-or-int value is --bool or --int
--path value is a path (file or directory name)
--expiry-date value is an expiry date

Other
-z, --null terminate values with NUL byte
--name-only show variable names only
--includes respect include directives on lookup
--show-origin show origin of config (file, standard input, blob, command line)
--default <value> with --get, use default value when missing entry

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 rmgit 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
2
mkdir logs
echo hello>logs/dev.log

然后我们需要告诉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
2
3
4
mkdir bin
echo hello > bin /hello.bin
git add.
git commit -m "Add bin."

我们再 .gitignore 中将 bin/ 加入并 commit

1
2
git add .
git commit -m "Include bin/ in gitignore"

现在我们修改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
2
git config --global diff.tool vscode
git config --global difftool.vscode.cmd "code --wait --diff $LOCAL $REMOTE"

第一行命令是让我们将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

-------------本文结束,感谢您的阅读-------------