(该系列文章大部分内容来源于MIT课程笔记,加入了个人的理解、原笔记中没有的细节和其他的需要理解的内容,公开课地址:https://www.bilibili.com/video/BV14E411J7n2?p=1)
上一讲:命令行环境
下一讲:调试及性能分析
版本控制系统 (VCSs) 是一类用于追踪源代码(或其他文件、文件夹)改动的工具。顾名思义,这些工具可以帮助我们管理代码的修改历史;不仅如此,它还可以让协作编码变得更方便。VCS通过一系列的快照将某个文件夹及其内容保存了起来,每个快照都包含了文件或文件夹的完整状态。同时它还维护了快照创建者的信息以及每个快照的管相关信息等等。和别人协作开发时,它更是一个无价之宝,您可以看到别人对代码进行的修改,同时解决由于并行开发引起的冲突。
现代的版本控制系统可以帮助您轻松地(甚至自动地)回答以下问题:
尽管版本控制系统有很多, 其事实上的标准则是 Git 。而这篇 XKCD 漫画 则反映出了人们对 Git 的评价:
因为 Git 接口的抽象泄漏(leaky abstraction)问题,通过自顶向下的方式(从命令行接口开始)学习 Git 可能会让人感到非常困惑。很多时候您只能死记硬背一些命令行,然后像使用魔法一样使用它们,一旦出现问题,就只能像上面那幅漫画里说的那样去处理了。
尽管 Git 的接口有些丑陋,但是它的底层设计和思想却是非常优雅的。丑陋的接口只能靠死记硬背,而优雅的底层设计则非常容易被人理解。因此,我们将通过一种自底向上的方式像您介绍 Git。我们会从数据模型开始,最后再学习它的接口。一旦您搞懂了 Git 的数据模型,再学习其接口并理解这些接口是如何操作数据模型的就非常容易了。
Git 拥有一个经过精心设计的模型,这使其能够支持版本控制所需的所有特性,例如维护历史记录、支持分支和促进协作。
Git 将顶级目录中的文件和文件夹作为集合,并通过一系列快照来管理其历史记录。在Git的术语里,文件被称作Blob对象(数据对象),也就是一组数据。目录则被称之为“树”,它将名字与Blob对象或树对象进行映射(使得目录中可以包含其他目录)。快照则是被追踪的最顶层的树。例如,一个树看起来可能是这样的:
<root> (tree)
|
+- foo (tree)
| |
| + bar.txt (blob, contents = "hello world")
|
+- baz.txt (blob, contents = "git is wonderful")
线性历史记录是一种最简单的模型,它包含了一组按照时间顺序线性排列的快照。不过处于种种原因,Git 并没有采用这样的模型。在 Git 中,历史记录是一个由快照组成的 有向无环图。注意,快照具有多个“父辈”而非一个,因为某个快照可能由多个父辈而来。例如,经过合并后的两条分支。
在 Git 中,这些快照被称为“提交”。通过可视化的方式来表示这些历史提交记录时,看起来差不多是这样的:
o <-- o <-- o <-- o
^
\
--- o <-- o
上面是一个 ASCII 码构成的简图,其中的 o 表示一次提交(快照)。
箭头指向了当前提交的父辈(这是一种“在。。。之前”,而不是“在。。。之后”的关系)。在第三次提交之后,历史记录分岔成了两条独立的分支。这可能因为此时需要同时开发两个不同的特性,它们之间是相互独立的。开发完成后,这些分支可能会被合并并创建一个新的提交,这个新的提交会同时包含这些特性。新的提交会创建一个新的历史记录,看上去像这样(最新的合并提交用粗体标记):
Git 中的提交是不可改变的。但这并不代表错误不能被修改,只不过这种“修改”实际上是创建了一个全新的提交记录。而引用(参见下文)则被更新为指向这些新的提交。
以伪代码的形式来学习 Git 的数据模型,可能更加清晰:
// 文件就是一组数据
type blob = array<byte>
// 树包含文件夹名和文件或子树
type tree = map<string, tree | file>
// 每个提交都包含一个父辈,元数据和顶层树
type commit = struct {
parent: array<commit>
author: string
message: string
snapshot: tree
}
Git 中的对象可以是 blob、树或提交:
type object = blob | tree | commit
Git 在储存数据时,所有的对象都会基于它们的SHA-1 hash进行寻址。
objects = map<string, object>
def store(object):
id = sha_1(object)
objects[id] = object
def load(id):
return objects[id]
Blobs、树和提交都一样,它们都是对象。当它们引用其他对象时,它们并没有真正的在硬盘上保存这些对象,而是仅仅保存了它们的哈希值作为引用。
例如,上例中的树(可以通过git cat-file -p 698281bc680d1995c5f4caaf3359721a5a58d48d
来进行可视化),看上去是这样的:
100644 blob 4448adbf7ecd394f42ae135bbeed9676e894af85 baz.txt
040000 tree c68d233a33c5c06e0340e4c224f0afca87c8ce87 foo
树本身会包含一些指向其他内容的指针,例如baz.txt (blob)
和foo
(树)。如果我们用git cat-file -p 4448adbf7ecd394f42ae135bbeed9676e894af85
,即通过哈希值查看 baz.txte
的内容,会得到以下信息:
git is wonderful
现在,所有的快照都可以通过它们的 SHA-1
哈希值来标记了。但这也太不方便了,谁也记不住一串 40 位的十六进制字符。
针对这一问题,Git 的解决方法是给这些哈希值赋予人类可读的名字,也就是引用(references)。引用是指向提交的指针。与对象不同的是,它是可变的(引用可以被更新,指向新的提交)。例如,master
引用通常会指向主分支的最新一次提交。
references = map<string, string>
def update_reference(name, id):
references[name] = id
def read_reference(name):
return references[name]
def load_reference(name_or_id):
if name_or_id in references:
return load(references[name_or_id])
else:
return load(name_or_id)
有一个细节需要我们注意, 通常情况下,我们会想要知道“我们当前所在位置”,并将其标记下来。这样当我们创建新的快照的时候,我们就可以知道它的相对位置(如何设置它的“父辈”)。在 Git 中,我们当前的位置有一个特殊的索引,它就是”HEAD”。
最后,我们可以粗略地给出 Git 仓库的定义了:对象 和 引用。
在硬盘上,Git 仅存储对象和引用:因为其数据模型仅包含这些东西。所有的 git 命令都对应着对提交树的操作,例如增加对象,增加或删除引用。
当您输入某个指令时,请思考一些这条命令是如何对底层的图数据结构进行操作的。另一方面,如果您希望修改提交树,例如“丢弃未提交的修改和将 master
引用指向提交5d83f9e
时,有什么命令可以完成该操作(针对这个具体问题,您可以使用git checkout master; git reset --hard 5d83f9e
)
Git 中还包括一个和数据模型完全不相关的概念,但它确是创建提交的接口的一部分。
就上面介绍的快照系统来说,您也许会期望它的实现里包括一个 “创建快照” 的命令,该命令能够基于当前工作目录的当前状态创建一个全新的快照。有些版本控制系统确实是这样工作的,但 Git 不是。我们希望简洁的快照,而且每次从当前状态创建快照可能效果并不理想。例如,考虑如下场景,您开发了两个独立的特性,然后您希望创建两个独立的提交,其中第一个提交仅包含第一个特性,而第二个提交仅包含第二个特性。或者,假设您在调试代码时添加了很多打印语句,然后您仅仅希望提交和修复 bug 相关的代码而丢弃所有的打印语句。
Git 处理这些场景的方法是使用一种叫做 “暂存区(staging area)”的机制,它允许您指定下次快照中要包括那些改动。
Pro git 中文版
git help init # 指定参考某个语句
lilhoe@LilHoedeMacBook-Pro Downloads % cd demo
lilhoe@LilHoedeMacBook-Pro demo % ls
lilhoe@LilHoedeMacBook-Pro demo % git init
Initialized empty Git repository in /Users/lilhoe/Downloads/demo/.git/
lilhoe@LilHoedeMacBook-Pro demo % ls -a
. .. .git
lilhoe@LilHoedeMacBook-Pro demo % ls .git
HEAD description info refs
config hooks objects
lilhoe@LilHoedeMacBook-Pro demo % git status # 我当前为空状态
On branch master
No commits yet
nothing to commit (create/copy files and use "git add" to track)
git add -a
表示从根节点开始递归加入所有子节点的内容)
git commit: 创建一个新的提交 (如何编写 良好的提交信息)
lilhoe@LilHoedeMacBook-Pro demo % echo "hello world">hello.txt
lilhoe@LilHoedeMacBook-Pro demo % git status
On branch master
No commits yet
Untracked files:
(use "git add ..." to include in what will be committed)
hello.txt
nothing added to commit but untracked files present (use "git add" to track)
lilhoe@LilHoedeMacBook-Pro demo % git add hello.txt
lilhoe@LilHoedeMacBook-Pro demo % git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached ..." to unstage)
new file: hello.txt
lilhoe@LilHoedeMacBook-Pro demo % git commit
lilhoe@LilHoedeMacBook-Pro demo % git log
commit 0e628c60e70dcdac9c4df7ff21e99db047478916 (HEAD -> master)
Author: SijiaHoe <[email protected]>
Date: Mon Aug 31 15:13:59 2020 +0800
Add hello.txt
lilhoe@LilHoedeMacBook-Pro demo % git cat-file -p 0e628c60e70dcd
tree 68aba62e560c0ebc3396e8ae9335232cd93a3f60
author SijiaHoe <[email protected]> 1598858039 +0800
committer SijiaHoe <[email protected]> 1598858039 +0800
Add hello.txt
lilhoe@LilHoedeMacBook-Pro demo % git cat-file -p 68aba62e560c0ebc3396e8ae9335232cd93a3f60
100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad hello.txt
lilhoe@LilHoedeMacBook-Pro demo % git cat-file -p 3b18e512dba79e4c8300dd08aeb37f8e728b8dad
hello world
git log
本身将历史记录扁平化,而不是输出图)(如果加上参数–oneline,会更加简洁的在一行之内显示log)lilhoe@LilHoedeMacBook-Pro demo % git log --all --graph --decorate
# 由于目前只有一个commit,图==扁平化
* commit 0e628c60e70dcdac9c4df7ff21e99db047478916 (HEAD -> master)
Author: SijiaHoe <[email protected]>
Date: Mon Aug 31 15:13:59 2020 +0800
Add hello.txt
lilhoe@LilHoedeMacBook-Pro demo % echo "another line" >> hello.txt
lilhoe@LilHoedeMacBook-Pro demo % cat hello.txt
hello world
another line
lilhoe@LilHoedeMacBook-Pro demo % git commit # 忘记add直接commit了
On branch master
Changes not staged for commit:
modified: hello.txt
no changes added to commit
lilhoe@LilHoedeMacBook-Pro demo % git add hello.txt
lilhoe@LilHoedeMacBook-Pro demo % git status
On branch master
Changes to be committed:
(use "git reset HEAD ..." to unstage)
modified: hello.txt
lilhoe@LilHoedeMacBook-Pro demo % git commit
hint: Waiting for your editor to close the fil[master 4221e64] add line
1 file changed, 1 insertion(+)
lilhoe@LilHoedeMacBook-Pro demo % git log --all --graph --decorate
* commit 4221e64f48b37c94fc66104deb76584490f28137 (HEAD -> master)
| Author: SijiaHoe <[email protected]>
| Date: Mon Aug 31 15:34:54 2020 +0800
|
| add line
|
* commit 0e628c60e70dcdac9c4df7ff21e99db047478916
Author: SijiaHoe <[email protected]>
Date: Mon Aug 31 15:13:59 2020 +0800
Add hello.txt
# 体现出了有向无环图
lilhoe@LilHoedeMacBook-Pro demo % git diff hello.txt
# git diff 默认是和HEAD比较,因此现在我们和HEAD没有差异,没有输出
lilhoe@LilHoedeMacBook-Pro demo % git diff 0e628c60e70dcdac9c4df7ff21e99db hello.txt # 比较和最初版本的差异
diff --git a/hello.txt b/hello.txt
index 3b18e51..054cc85 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1 +1,2 @@
hello world
+another lin
lilhoe@LilHoedeMacBook-Pro demo % git checkout 0e628c60e70dcdac9c4df7ff21e
Previous HEAD position was 4221e64 add line
HEAD is now at 0e628c6 Add hello.txt
lilhoe@LilHoedeMacBook-Pro demo % cat hello.txt
hello world # 返回到了增加一行前的状态
# 在查看一次log文件,注意和上面一次的区别:
lilhoe@LilHoedeMacBook-Pro demo % git log --all --graph --decorate
* commit 4221e64f48b37c94fc66104deb76584490f28137 (master)
| Author: SijiaHoe <[email protected]>
| Date: Mon Aug 31 15:34:54 2020 +0800
|
| add line
|
* commit 0e628c60e70dcdac9c4df7ff21e99db047478916 (HEAD)
Author: SijiaHoe <[email protected]>
Date: Mon Aug 31 15:13:59 2020 +0800
Add hello.txt
分析(注意查看上述代码中引用的变化):第一次的Head(当前位置)处于最后一次提交上,并指向master(最新一次提交);而切换目录后,Head处于第一次修改上。
# 目录返回最近一次更新(可以不需要输入哈希值,直接输入别名)
lilhoe@LilHoedeMacBook-Pro demo % git checkout master
Previous HEAD position was 0e628c6 Add hello.txt
Switched to branch 'master'
首先再创建一个文件并commit:
lilhoe@LilHoedeMacBook-Pro demo % touch animal.py
lilhoe@LilHoedeMacBook-Pro demo % vim animal.py
# animal.py
import sys
def default():
print("hello")
def main():
default()
if __name__=='__main__':
main()
lilhoe@LilHoedeMacBook-Pro demo % python animal.py
hello
lilhoe@LilHoedeMacBook-Pro demo % git add animal.py
lilhoe@LilHoedeMacBook-Pro demo % git commit
hint: Waiting for your editor to close the file[master c7a5381] Add animal.py
1 file changed, 10 insertions(+)
create mode 100644 animal.py
lilhoe@LilHoedeMacBook-Pro demo % git log --all --graph --decorate
* commit c7a53818539c8b1a1c1a4535d4f965f7c811dbd7 (HEAD -> master)
| Author: SijiaHoe <[email protected]>
| Date: Mon Aug 31 16:13:28 2020 +0800
|
| Add animal.py
|
* commit 4221e64f48b37c94fc66104deb76584490f28137
| Author: SijiaHoe <[email protected]>
| Date: Mon Aug 31 15:34:54 2020 +0800
|
| add line
|
* commit 0e628c60e70dcdac9c4df7ff21e99db047478916
Author: SijiaHoe <[email protected]>
Date: Mon Aug 31 15:13:59 2020 +0800
Add hello.txt
lilhoe@LilHoedeMacBook-Pro demo % git branch
* master
lilhoe@LilHoedeMacBook-Pro demo % git branch -vv
* master c7a5381 Add commit
lilhoe@LilHoedeMacBook-Pro demo % git branch cat # 创建一个引用cat指向head
我们先切换到cat引用目录下:
lilhoe@LilHoedeMacBook-Pro demo % git checkout cat
Switched to branch 'cat'
# 此时head,cat,master都同时指向最近一次修改
lilhoe@LilHoedeMacBook-Pro demo % git log --all --graph --decorate --oneline
* c7a5381 (HEAD->cat,master) Add animal.py
* 4221e64 add line
* 0e628c6 Add hello.txt
下面我们修改一下animal.py文件,加入cat功能并提交:
# animal.py
import sys
def cat():
print("Meow!")
def default():
print("hello")
def main():
if sys.argv[1] == 'cat':
cat()
else:
default()
if __name__=='__main__':
main()
# 查看提交后的日志文件
lilhoe@LilHoedeMacBook-Pro demo % git log --all --graph --decorate --oneline
* aeb1a22 (HEAD -> cat) Add cat functionality
* c7a5381 (master) Add animal.py
* 4221e64 add line
* 0e628c6 Add hello.txt
# 将当前目录切换到master
lilhoe@LilHoedeMacBook-Pro demo % git checkout master
# master引用目录下,还没有加入cat
lilhoe@LilHoedeMacBook-Pro demo % cat animal.py
import sys
def default():
print("hello")
def main():
default()
if __name__=='__main__':
main()
此时我们在master目录先修改animal.py增加dog功能,就形成了两个分支,一个支持cat,一个支持dog:
# animal.py
import sys
def default():
print("hello")
def dog():
print("Woof!")
def main():
if sys.argv[1] == 'dog':
dog()
else:
default()
if __name__=='__main__':
main()
提交,查看log:
lilhoe@LilHoedeMacBook-Pro demo % git log --all --graph --decorate --oneline
* b352003 (HEAD -> dog) Add dog functionality
| * aeb1a22 (cat) Add cat functionality
|/
* c7a5381 (master) Add animal.py
* 4221e64 add line
* 0e628c6 Add hello.txt
# 切换到分支根部
lilhoe@LilHoedeMacBook-Pro demo % git checkout master
Switched to branch 'master'
# 先将根节点和cat合并,此时animal.py中只有cat功能
lilhoe@LilHoedeMacBook-Pro demo % git merge cat
Updating c7a5381..aeb1a22
Fast-forward
animal.py | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
# 再和dog合并,报错!
lilhoe@LilHoedeMacBook-Pro demo % git merge dog
Auto-merging animal.py
CONFLICT (content): Merge conflict in animal.py
Automatic merge failed; fix conflicts and then commit the result.
# 查看animal.py文件,发现是if语句合并出问题
lilhoe@LilHoedeMacBook-Pro demo % cat animal.py
import sys
def cat():
print("Meow!")
def default():
print("hello")
def dog():
print("Woof!")
def main():
<<<<<<< HEAD
if sys.argv[1] == 'cat':
cat()
=======
if sys.argv[1] == 'dog':
dog()
>>>>>>> dogs
else:
default()
if __name__=='__main__':
main()
为了解决dog合并出错的问题:
# 修改错误
lilhoe@LilHoedeMacBook-Pro demo % vim animal.py
# 使合并继续
lilhoe@LilHoedeMacBook-Pro demo % git merge --continue
U animal.py
error: Committing is not possible because you have unmerged files.
hint: Fix them up in the work tree, and then use 'git add/rm '
hint: as appropriate to mark resolution and make a commit.
fatal: Exiting because of an unresolved conflict.
# 提交
lilhoe@LilHoedeMacBook-Pro demo % git add animal.py
lilhoe@LilHoedeMacBook-Pro demo % git commit
hint: Waiting for your editor to close the file[cat de54fd5] Merge branch 'dog' into cat
# 查看log
lilhoe@LilHoedeMacBook-Pro demo % git log --all --graph --decorate --oneline
* de54fd5 (HEAD -> master) Merge branch 'dog' into cat
|\
| * b352003 (dog) Add dog functionality
* | aeb1a22 (cat) Add cat functionality
|/
* c7a5381 Add commit
* 4221e64 add line
* 0e628c6 Add hello.txt
git mergetool: 使用工具来处理合并冲突(参考这里或这里)
git rebase: 将一系列补丁变基(rebase)为新的基线
lilhoe@LilHoedeMacBook-Pro Downloads % mkdir remote
lilhoe@LilHoedeMacBook-Pro Downloads % cd remote
lilhoe@LilHoedeMacBook-Pro remote % git init --bare
Initialized empty Git repository in /Users/lilhoe/Downloads/remote/
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/remote $ cd ..
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads $ cd demo
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ git remote add origin ../remote
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ git remote
origin
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ git push origin master:master
Enumerating objects: 12, done.
Counting objects: 100% (12/12), done.
Delta compression using up to 8 threads
Compressing objects: 100% (8/8), done.
Writing objects: 100% (12/12), 1.11 KiB | 1.11 MiB/s, done.
Total 12 (delta 0), reused 0 (delta 0)
To ../remote
* [new branch] master -> master
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ git graph # 该指令请移步作业4
* de54fd5 (HEAD -> master, origin/master) Merge branch 'dog' into cat
|\
| * b352003 (dog) Add dog functionality
* | aeb1a22 (cat) Add cat functionality
|/
* c7a5381 Add commit
* 4221e64 add line
* 0e628c6 Add hello.txt
这里的origin/master
值得是在本地副本知道在远程服务器上也有一份。
如果此时我在本地修改了animal.py
文件,并且commit到git上,但是此时远程服务器接收不到这个修改。解决方案:(跟着网课做的似乎有点问题,后续会进一步学习更正!)
# 克隆操作
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ cd ..
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads $ git clone ./remote demo2
Cloning into 'demo2'...
done.
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads $ cd demo2
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo2 $
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ git push origin master:master
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ git branch --set-upstream-to=origin/master
Branch 'cat' set up to track remote branch 'master' from 'origin'.
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo2 $ git fetch
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo2 $ git merge
举例:我修改了一下hello.py的内容,使用该语句可以重新回到上一个提交状态。
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ vim hello.txt
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ git diff
diff --git a/hello.txt b/hello.txt
index 054cc85..4fb8c94 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1,2 +1,3 @@
+Nihao
hello world
another line
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ git stash
Saved working directory and index state WIP on cat: e37c248 Change
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ cat hello.txt
hello world
another line # 可见并没有显示修改,但他并不是删除了,而是隐秘的存在了其他地方
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ git stash pop # 撤销刚刚的返回
On branch master
Your branch is ahead of 'origin/master' by 4 commits.
(use "git push" to publish your local commits)
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: hello.txt
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{
0} (847e9bb5f5492b3dfa0e3f40d23ab6ca1b59b289)
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ git diff
diff --git a/hello.txt b/hello.txt
index 054cc85..4fb8c94 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1,2 +1,3 @@
+Nihao # 可见修改依然存在
hello world
another lin
举例:操作系统中存在很多没用的二进制文件,在提交时,我们只希望源代码进入历史记录,而忽略某些二进制文件:
# 创建两个需要被忽略的文件
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ touch .DS_Store
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ touch foo.o
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ git status
此时未跟踪的文件中有.DS_Store
和foo.o
两个。
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ vim .gitignore
修该.gitignore
文件,加入需要忽略的文件名(或与正则表达式匹配的文件名):
再一次查看git status
,会发现未跟踪文件中只有一个.gitignore文件。并且这两个被忽略的文件不再显式存在于文件夹中。
"git commit -a")
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ lss
animal.py foo.o hello.txt
[lilhoe@LilHoedeMacBook-Pro] ~/Downloads/demo $ ls -a
. .DS_Store .gitignore foo.o
.. .git animal.py hello.txt
git log
命令并添加合适的参数)_config.yml
文件中 collections:
行时的提交信息是什么?(提示:使用git blame
和 git show
)git stash
会发生什么?当您执行 git log --all --oneline
时会显示什么?通过 git stash pop
命令来撤销 git stash
操作,什么时候会用到这一技巧?~/.gitconfig
配置文件 (或 dotfile)。请在 ~/.gitconfig
中创建一个别名,使您在运行git graph
时,您可以得到 git log --all --graph --decorate --oneline
的输出结果;git config --global alias.graph "log --all --graph --decorate --oneline"
git config --global core.excludesfile ~/.gitignore_global
在 ~/.gitignore_global
中创建全局忽略规则。配置您的全局 gitignore
文件来字典忽略系统或编辑器的临时文件,例如 .DS_Store
;参考:
https://missing-semester-cn.github.io/2020/version-control/#snapshots
https://git-scm.com/book/zh/v2/Git-基础-Git-别名
https://blog.csdn.net/qq_27905183/article/details/78575247