0%

Mosh的Git课程笔记(2)--Creating Snapshots

Mosh的课程网址

初始化一个仓库

1
2
3
mkdir Moon
cd Moon
git init

在目录Moon下有一个.git隐藏文件夹,可以用以下命令看到:

1
ls -a

删除.git:

1
rm -rf .git

Git Workflow

本地directory→staging area (index)→repository

Staging Files

01

1
git status

查看,标红是因为新建的两个txt文件还没在staging area中。

使用下面的命令将两个文件的变化(新建/删除文件,文件内容更改)存入staging area中:

1
git add file1.txt file2.txt
1
git add *.txt
1
git add .

上面三种方式,(1)就add这两个文件;(2)add所有txt文件;(3)add所有文件

然后再查看,就绿了:

02

Committing Changes

提交到仓库

1
git commit -m "Initial commit."

Committing Best Practices

  • commit的时候文件的变化量应该适中(不要太多,也不要太少)
  • Commit often: 想要记录下某个状态时,就commit
  • Make it mean something

例如:对于两个改变 (1) Bug Fix, (2) Typo 最好不要一起commit,不然不知道改了啥,最好分开commit

Skipping the Staging Area

使用 -a 选项跳过git add

可以将两个选项 -a 和 -m 合并写成 -am

1
git commit -am "Fix the bug"

Remove Files

1
2
3
4
5
6
7
8
9
10
11
12
13
$ rm file1.txt
$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: file1.txt

no changes added to commit (use "git add" and/or "git commit -a")

$ git ls-files
file1.txt
file2.txt

在本地目录下删除file1.txt,但是用git ls-files查看staging area中的文件会发现file1.txt还在其中。使用git add更新staging area然后commit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ git add file1.txt

$ git ls-files
file2.txt

$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: file1.txt

$ git commit -m "Remove unused code"
[master b184de1] Remove unused code
1 file changed, 7 deletions(-)
delete mode 100644 file1.txt

除了上面的方法,也可以使用以下方法(git rm)一次性删除本地目录和staging area中的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ git rm file2.txt
rm 'file2.txt'

$ git ls-files

$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
deleted: file2.txt

$ git commit -m "Remove unused code"
[master 3fd1e3e] Remove unused code
1 file changed, 1 deletion(-)
delete mode 100644 file2.txt

Renaming and Moving Files

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
$ mv file1.txt main.js

$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
deleted: file1.txt

Untracked files:
(use "git add <file>..." to include in what will be committed)
main.js

no changes added to commit (use "git add" and/or "git commit -a")

$ git add file1.txt

$ git add main.js
warning: LF will be replaced by CRLF in main.js.
The file will have its original line endings in your working directory

$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: file1.txt -> main.js

文件的更名涉及到两个变化,原文件file1.txt删除,以及新文件main.js的创建。

当然,也可以使用便捷的方式:

1
2
3
4
5
6
7
8
9
10
11
12
$ git mv file1.txt main.js

$ git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: file1.txt -> main.js

$ git commit -m "Refactor code"
[master 70800e8] Refactor code
1 file changed, 0 insertions(+), 0 deletions(-)
rename file1.txt => main.js (100%)

注意到0 insertions(+), 0 deletions(-),改名而已。

Ignoring Files

1
2
3
4
5
6
7
8
9
10
11
$ mkdir logs

$ echo hello > logs/dev.log

$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
logs/

nothing added to commit but untracked files present (use "git add" to track)

但是事实上我们并不想将log文件提交。方法:将 logs/ 写入.gitignore中

1
$ echo logs/ > .gitignore

用vscode打开文件.gitignore

1
$ code .gitignore

可以在 .gitignore 中的下一行写上*.log表示要忽略的文件类型。然后提交 .gitignore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore

nothing added to commit but untracked files present (use "git add" to track)

$ git add .gitignore
warning: LF will be replaced by CRLF in .gitignore.
The file will have its original line endings in your working directory

$ git commit -m "Add gitignore"
[master 9ed82f0] Add gitignore
1 file changed, 2 insertions(+)
create mode 100644 .gitignore

之后log文件的改变都会被staging area忽略了:

1
2
3
4
5
$ echo hello > logs/main.log

$ git status
On branch master
nothing to commit, working tree clean

可以看到nothing to commit。

​ 如果已经用 git add 不小心将 log 文件提交到 staging area 了(在写入.gitignore之前就交了),可以用命令删除 staging area 中的文件。先 help 一下看看用啥选项:

1
2
3
4
5
6
7
8
9
10
11
12
$ git rm -h
usage: git rm [<options>] [--] <file>...

-n, --dry-run dry run
-q, --quiet do not list removed files
--cached only remove from the index
-f, --force override the up-to-date check
-r allow recursive removal
--ignore-unmatch exit with a zero status even if nothing matched
--pathspec-from-file <file>
read pathspec from file
--pathspec-file-nul with --pathspec-from-file, pathspec elements are separated with NUL character
1
$ git rm --cached -r logs/

Short Status

git status 给出的信息易于理解,但是冗长:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ echo sky >> file1.js

$ echo sky > file2.js

$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: file1.js

Untracked files:
(use "git add <file>..." to include in what will be committed)
file2.js

no changes added to commit (use "git add" and/or "git commit -a")

加 -s 选项:

03

观察 M 的颜色变化,git add 之后 M 由红变绿,?? 变 A

1
2
M:表示modified
A:表示added

Viewing the Staged & Unstaged Changes

Motivation: 有时候在将changes上传到staging area或commit到repository之前,要先看看改了啥,改得对不对。

查看staged changes (在staging area中但是还没commit):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ git diff --staged
diff --git a/file1.js b/file1.js
index ce01362..7633964 100644
--- a/file1.js
+++ b/file1.js
@@ -1 +1,3 @@
hello
+sky
+sun
diff --git a/file2.js b/file2.js
new file mode 100644
index 0000000..f5e95e7
--- /dev/null
+++ b/file2.js
@@ -0,0 +1 @@
+sky

查看unstaged changes(在本地目录下,不在staging area中):

1
$ git diff

比如在file1.js中将hello 改为 hello world:

04

a是在staging area中的copy,b是本地目录下的copy

@@ -1,3 +1,3 @@ 表示旧版本(staging area中的)从第一行开始,有3行(-1,3);新版本(本地目录下的)从第一行开始,有3行(+1,3)

标红的是旧版本内容,标绿的是新版本内容,白色的是未更改内容。

Visual Diff Tools

1
2
3
4
👉 KDiff3
👉 P4Merge
👉 WinMerge (Windows Only)
👉 VSCode

我们选用VSCode来可视化changes。配置:

1
2
3
4
5
$ git config --global diff.tool vscode

$ git config --global difftool.vscode.cmd "code --wait --diff $LOCAL $REMOTE"

$ git config --global -e

查看unstaged changes:

1
$ git difftool

06

查看staged changes(旧版本是前一次提交到仓库的版本,新版本是如今在staging area中的版本):

1
2
3
4
5
6
7
$ git difftool --staged

Viewing (1/2): 'file1.js'
Launch 'vscode' [Y/n]? y

Viewing (2/2): 'file2.js'
Launch 'vscode' [Y/n]? y

05

07

Viewing the History

用以下命令查看提交历史:

1
$ git log

查看简短描述 (最近更改显示在上面):

1
$ git log --oneline

换显示顺序 (最近更改显示在下面):

1
$ git log --oneline --reverse

Viewing a Commit

可以通过ID查看历史commit,如果ID前几位就唯一标识了某历史commit,那么用前几位即可。

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
$ git log --oneline
fdb7fb6 (HEAD -> master) add data
62416e2 add files
fde6777 modified
f264740 modified
9ed82f0 Add gitignore
70800e8 Refactor code
d6f4cc6 Initial commit
3fd1e3e Remove unused code
b184de1 Remove unused code
dd03e4e Fix the bug
3b634ca add Lines
a787fca add line
568c0fc Initial commit.

$ git show 62416
commit 62416e293cdb5f7334ea6dc09a8fbb0314eedcc2
Author: Stone <masaike@qq.com>
Date: Thu Apr 22 11:32:51 2021 +0800

add files

diff --git a/file1.js b/file1.js
new file mode 100644
index 0000000..ce01362
--- /dev/null
+++ b/file1.js
@@ -0,0 +1 @@
+hello

也可以用HEAD:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ git show HEAD~1
commit 62416e293cdb5f7334ea6dc09a8fbb0314eedcc2
Author: Stone <masaike@qq.com>
Date: Thu Apr 22 11:32:51 2021 +0800

add files

diff --git a/file1.js b/file1.js
new file mode 100644
index 0000000..ce01362
--- /dev/null
+++ b/file1.js
@@ -0,0 +1 @@
+hello

HEAD是当前commit的reference, HEAD~1是指从HEAD往后的一个commit,此例中就是ID为62416e2的commit。

也可以查看某历史 commit 中存于 repository 的内容(而不是changes):

1
2
3
4
5
$ git show HEAD~1:.gitignore
logs/
bin/
*.log
*.bin

也可以查看某历史 commit 中存于 repository 的所有文件:

1
2
3
4
5
6
$ git ls-tree HEAD
100644 blob 1cd4d4e7fe27c57dc89d6e68d1f535f00382e563 .gitignore
040000 tree 38051ae48accaf0025a257eaa7a3a328e1f0fe56 data
100644 blob 29887f938f21333a5eee9dfd0decb4b0a207d855 file1.js
100644 blob f5e95e70e524ec32d0200e10ba179ab4c5f13884 file2.js
100644 blob ce013625030ba8dba906f756967f9e9ca394464a main.js

格式:ID, 文件类型, 唯一标识符 (根据文件内容确定的), 文件名

data是个文件夹,所以类型为tree,里面有个文件main.txt。

根据文件唯一标识符查看文件内容:

1
2
3
4
5
6
7
$ git show 3805
tree 3805

main.txt

$ git show f5e95
sky

f5e95 是 file2.js 的唯一标识符的前几位,该文件的内容只有一行,就是 sky

1
2
3
4
5
总结:Git Objects
👉 Commits
👉 Blobs (Files)
👉 Trees (Directories)
👉 Tags

Unstaging Files

file1.js有两次changes,我们用 git add 将 file1.js 的第一个变化上传到 staging area中了,第二次 change 还在本地目录中。

我们不想同时 commit file1.js 和 file2.js 两个文件的变化。所以要 undo 这个 git add 操作:

1
$ git restore --staged file1.js

08

现在 file1.js 的两次变化都只在本地目录下而不在 staging area 中了。

原理:staging area 从目前的 repository 读取 file1.js 。因为两次变化都还没 commit ,repository 中的 file1.js 就是原始的 (两次变化之前的)。

如果要撤销 file2.js 的 git add 操作。repository 中还没有 file2.js, 所以撤销之后 file2.js 是一个untracked file (用??表示)。

09

Discarding Local Changes

1
$ git restore .

可以撤销本地目录下的改变。file2.js 依然是untracked file,因为本地目录要从staging area 读取上一次状态,而staging area 中没有 file2.js, 就不知道该做啥。所以要用

1
$ git clean

从你的工作目录中删除所有untracked,没有被管理过的文件。

参数说明:

1
2
3
4
5
6
7
8
9
10
11
12
$ git clean -h
usage: git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>...

-q, --quiet do not print names of files removed
-n, --dry-run dry run
-f, --force force
-i, --interactive interactive cleaning
-d remove whole directories
-e, --exclude <pattern>
add <pattern> to ignore rules
-x remove ignored files, too
-X remove only ignored files

10

Restoring a File to an Earlier Version

下面的 demo 显示了删除了 file1.js 后用 git restore 从历史 commit 中恢复此文件。

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
$ git rm file1.js
rm 'file1.js'

$ git status -s
D file1.js

$ git commit -m "Delete file1.js"
[master 78daaab] Delete file1.js
1 file changed, 1 deletion(-)
delete mode 100644 file1.js

$ git log --oneline
78daaab (HEAD -> master) Delete file1.js
133904b commit file1.ls
0f62ff0 remove all js files
fdb7fb6 add data
62416e2 add files
fde6777 modified
f264740 modified
9ed82f0 Add gitignore
70800e8 Refactor code
d6f4cc6 Initial commit
3fd1e3e Remove unused code
b184de1 Remove unused code
dd03e4e Fix the bug
3b634ca add Lines
a787fca add line
568c0fc Initial commit.

$ git restore --source=HEAD~1 file1.js

$ git status -s
?? file1.js