廖雪峰Git教程学习笔记
前言
关于教程作者
廖雪峰,十年软件开发经验,业余产品经理,精通Java/Python/Ruby/Visual Basic/ObjectiveC等,对开源框架有深入研究,著有《Spring 2.0核心技术与最佳实践》一书,多个业余开源项目托管在GitHub,个人官网:https://www.liaoxuefeng.com
一、Git设置
安装完毕git后,设置个人名字和邮件地址
git config --global user.name "Your Name"
git config --global user.email "email@example.com"
二、创建版本库
版本库又叫仓库,里面的所有文件都可以被Git管理起来,每个文件的修改、删除、跟踪都可以被跟踪,以便实现追踪历史记录还原。
首先,创建一个空文件夹:
$ mkdir gitRepo
第二步,将这个目录变成Git可以管理的仓库
$ cd gitRepo
$ git init
Initialized empty Git repository in /work/gitRepo/.git/
生成的.git
目录是用来跟踪管理仓库的,不要轻易修改里面的文件,容易破坏掉git
仓库。.get
目录默认是隐藏的,通过ls -ah
可以查看到
第三步,创建一个文件,然后把它放到Git仓库中。
创建文件一定要在gitRepo或者其子目录中创建,否则Git找不到。创建readme.txt,并添加两端内容:
$ touch readme.txt
$ gedit readme.txt
内容:
Git is a version control system.
Git is free software.
把文件添加到仓库:
$ git add readme.txt
若没有任何显示则表示添加成功,继续使用git命令,将文件提交到仓库:
$ git commit -m "this is a test"
[master (root-commit) 92a925f] this is a test
1 file changed, 2 insertions(+)
create mode 100644 readme.txt
在这里,-m
后面输入的内容可以理解为对本次提交的解释说明。git commit
成功后1 file changed
意味着1个文件被改动即我们新添加的readme.txt文件,2 insertions
代表插入了两行内容,即readme.txt内添加的两行内容。
Git添加文件需要两步,具体每一步作用后续有讲到,其中add
可以分批add文件,也可以一次add多个文件,而commit
则是一次性将所有add的文件提交到仓库。
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."
三、查看工作区状态
对创建的readme.txt做如下内容变动:
Git is a distributed version control system.
Git is free software.
使用git status
查看状态,即查看文件是否被修改过
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
git status
可以让我们随时掌握仓库目前的状态,从上面的信息可以看到,readme.txt已经被修改了,显示为还没有准备提交的修改。
如果想看到修改的具体内容,要用到git diff
命令,diff就是difference的缩写。
$ git diff readme.txt
diff --git a/readme.txt b/readme.txt
index e35ea24..86ccde8 100644
--- a/readme.txt
+++ b/readme.txt
@@ -1,2 +1,2 @@
-Git is a version control system.
+Git is a distributed version control system.
Git is free software.
从上面可以看得出来,我们增加了一个distributed单词。接着我们可以放心的将文件提交了。
$ git add readme.txt
使用git status
查看一下
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme.txt
可以看到,我们即将提交的是readme.txt。使用git commit
进行提交并使用git status
查看状态。
$ git commit -m "add distributed"
[master 1ae9018] add distributed
1 file changed, 1 insertion(+), 1 deletion(-)
$ git status
On branch master
nothing to commit, working directory clean
查看提交后的状态,显示没有需要提交的修改,而且工作目录是干净的。
四、版本回退
当我们对一个文件不断的修改后,难免因操作失误需要将文件恢复到某个时刻的状态,所以,就像打游戏存档一样,我们可以保存一个快照,在Git中,这个快照被称为commit
,当我们误操作后,可以从最近的一个commit
恢复,然后继续工作,防止工作成果丢失。
先使用git log
命令查看历史记录
$ git log
commit 1ae901871c9fedda1dd5ad9bd8c952aa244319eb
Author: uidq5232 <Guodong.Chen@desaysv.com>
Date: Tue Jun 7 19:06:12 2022 +0800
add distributed
commit 92a925f1281d986aa9f48d7eaa4a74233b8638dc
Author: uidq5232 <Guodong.Chen@desaysv.com>
Date: Tue Jun 7 17:25:10 2022 +0800
this is a test
上述代码告诉我们,第一次commit
为this is a test
,第二次为add distributed
。
使用git log
输出的信息很多,容易眼花缭乱,加上--pretty=oneline
参数则简约很多。
$ git log --pretty=oneline
1ae901871c9fedda1dd5ad9bd8c952aa244319eb add distributed
92a925f1281d986aa9f48d7eaa4a74233b8638dc this is a test
还需要注意的是,代码中1ae9…19eb代表的是commit id
(版本号),git的commit id
是由SHA1计算出来的40位数字,极大的防止在分布式系统中多人协作时版本号冲突。
既然知道了文件的版本号等信息,如何回退到以前的版本呢?在git中,用HEAD
表示当前版本,即最新提交的1ae9…19eb,上一个版本就是HEAD^
,上上个版本是HEAD^^
,当然版本号回退100个也可以用HEAD~100
。我们把版本回退到上一个版本,这里用到的是git reset
命令。
$ git reset --hard HEAD^
HEAD is now at 92a925f this is a test
当我们再次使用git log
查看状态时
$ git log
commit 92a925f1281d986aa9f48d7eaa4a74233b8638dc
Author: uidq5232 <Guodong.Chen@desaysv.com>
Date: Tue Jun 7 17:25:10 2022 +0800
this is a test
最新的add distributed已经不见了,但是我们若还想回到那个版本的话,只要窗口没有关掉,add distributed的版本号1ae9…19eb还能找到,就可以指定回到未来的某个版本。
$ git reset --hard 1ae90
HEAD is now at 1ae9018 add distributed
版本号写前几位就可以了,git会自己去找。
当我们不知道未来版本号但是还要回到未来版本时要怎么做那?比如我们要从this is a test回到add distributed要怎么做那,git提供了git reflog
用来记录每一次命令。
$ git reflog
92a925f HEAD@{0}: reset: moving to HEAD^
1ae9018 HEAD@{1}: reset: moving to 1ae90
92a925f HEAD@{2}: reset: moving to HEAD^
1ae9018 HEAD@{3}: commit: add distributed
92a925f HEAD@{4}: commit (initial): this is a test
可以看到,我们从哪个版本回退到另一个版本的历史操作都能找到add distributed的commit id
是1ae9018,所以我们可以根据版本号再次回到add distributed版本。
五、工作区和暂存区
工作区:我们创建的gitRepo就是一个工作区。
版本库:工作区内的隐藏目录.git
,这个不算工作区,是Git的版本库。
暂存区:GIt版本库中有一个名为stage
(或者index
)的地方叫做暂存区,当我们执行git add
时,文件就被放到了暂存区等待提交。当我们继续执行git commit
时,实际上就是把暂存区的所有文件提交到当前分支。
分支:每一次提交Git都会把他串成一条时间线,方便版本回退,这条时间线就是分支,创建版本库时,Git自动为我们创建了第一个分支master
,这个分支叫主分支,以及指向master
的一个指针HEAD
,具体后面详述。
在工作区新添加一个license文件,内容随意,并且对readme.txt做一次修改,如加上一行内容。
使用git status
查看状态:
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
license
no changes added to commit (use "git add" and/or "git commit -a")
上述代码可以看到,readme.txt被修改了但还没有添加到暂存区,license是一个Untracked files
,表示还没有被添加过,现在将两个文件添加到暂存区。
$ git add readme.txt license
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: license
modified: readme.txt
再使用commit
命令将暂存区的所有修改提交到分支。
$ git commit -m "understand how stage works"
[master f086944] understand how stage works
2 files changed, 2 insertions(+)
create mode 100644 license
再次查看状态,提交后如果没有对工作区做任何修改,那么工作区是“干净”的。
$ git status
On branch master
nothing to commit, working directory clean
六、撤销修改
当你不小心在readme.txt中错误的添加了一行内容:
$ cat readme.txt
Git is a version control system.
Git is free software.
Git has a mutable index called stage.
oh,god,i'm a dog.
当然你可以删掉最后一行,手动将文件恢复到上一个版本的状态,但是我们用git status查看时:
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
Git告诉我们use "git checkout -- <file>..." to discard changes in working directory
,所以我们可以使用git checkout -- <file>
,丢弃工作区的修改:
$ cat readme.txt
Git is a version control system.
Git is free software.
Git has a mutable index called stage.
果然,文件内容复原了。
git checkout -- <file>
就是把文件在工作区的修改全部撤销,这里有两种情况:
一种是只是在工作区修改还没有提交到暂存区,使用该命令可以撤回到和版本库一摸一样的装填。
一种是已经添加到暂存区,又做了修改,使用该命令就回到添加到暂存区的状态。
总之,就是让这个文件回到最近一次git commit
或者git add
的状态。
git checkout -- file
命令中的--
很重要,没有--
,就变成了“切换到另一个分支”的命令,我们在后面的分支管理中会再次遇到git checkout
命令。
假如说我们不光添加了错误的内容还将文件添加到了暂存区:
git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme.txt
幸运的是我们只是添加到了暂存区还没有提交,Git提示我们可以用git reset HEAD <file>
把暂存区的修改撤销(unstage
),重新弄放回工作区:
$ git reset HEAD readme.txt
Unstaged changes after reset:
M readme.txt
git reset
既可以回退版本,也可以把暂存区的修改回退到工作区,当我们用HEAD
时,表示最新的版本。使用git status
查看:
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
暂存区是干净的,工作区有修改,继续使用之前的git checkout
命令丢弃工作区的修改:
$ git checkout -- readme.txt
$ git status
On branch master
nothing to commit, working directory clean
终于,所有修改都被撤销掉了。
如果说你已经commit了,那么就只能使用reset回退到上一个版本,但是如果已经推送到远程仓库,就没有办法了。
小结:
场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令git checkout – file。场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令git reset HEAD file,就回到了场景1,第二步按场景1操作。
场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。
七、删除文件
删除文件的操作实际上就是修改文件,我们新创建一个test.txt到Git并提交:
$ git add test.txt
$ git commit -m "add test.txt"
[master 789e43f] add test.txt
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 test.txt
常规下,我们使用rm
命令就可以把文件管理器中的文件删除掉:
$ rm test.txt
这个时候,Git知道你删除了文件,因此,工作区和版本库就不一致了,git status
命令会立刻告诉你哪些文件被删除了:
$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm
删掉,并且git commit
:
$ git rm test.txt
rm 'test.txt'
$ git commit -m "remove test.txt"
[master 0e437ff] remove test.txt
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 test.txt
现在,文件就从版本库中被删除了。
另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:
$ git checkout -- test.txt
git checkout
其实就是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。