今日份的分享将会介绍Git本地的基本操作,涉及:创建或克隆操作、修改文件、提交到暂存区、提交修改至版本库,以及查看仓库的变更历史。接下来的操作使用的系统环境为Ubuntu 20.04,Windows上的操作也是一样的,只需要修改下涉及的文件或目录路径即可。
1 获取版本库
在本地建立Git项目主要有两种方法:一个是在本地初始化Git项目,另一个是从服务器上克隆Git项目。
1.1 本地初始Git项目
在本地初始Git项目也很方便,首先,定位到需要创建项目的文件夹,然后在此目录下执行如下命令:
git init
这句命令会在当前目录中创建.git子目录,这个子目录中包含了构成Git版本控制仓库骨架的必需的文件。但是,这时候并没有追踪此目录中的任何文件。
如果你选择的这个子目录是非空目录,并且想使用Git把这个文件夹已有的文件管理起来,可使用如下命令:
git add *
git commit -m "初始化Git仓库"
这里简单描述这两条命令的含义,后面的章节内容会详细介绍。
git add *将当前工作区中所有的文件添加到暂存区git commit -m "初始化Git仓库"将暂存区的数据提交的本地Git仓库,并附上提交信息
1.2 克隆服务器上的Git项目
在笔者参与的项目中,大多数是先在GitLab或者GitHub中创建项目,然后克隆到本地。克隆这个过程需要使用的命令是git clone [url],例如:
git clone https://gitee.com/jiaoxn/git_learn_note_202102.git
这条命令将会创建一个名为git_learn_note_202102的子目录,并在其中初始化.git目录,然后将拉去远程仓库(url指定的)的最新版本的所有数据到本地。
如果想克隆到其他名字的目录中,可在后面指定目录的名称,例如:
git clone https://gitee.com/jiaoxn/git_learn_note_202102.git my_learn_note
这将会把服务器上的Git项目克隆至my_learn_note,这里需要注意的是,如果这个目录不为空,Git将会提示警告。
细心的朋友可能注意到上述命令中均使用https协议,Git还会支持其他的协议,比如:SSH,这将会在后续的笔记中详细介绍。接下来,让我们熟悉下项目过程中常用的Git本地操作命令吧。
2 修改本地的文件
还记得昨天的笔记中提到的,Git中的文件有3种状态,分别是已修改、已暂存和已提交。这里,把文件的状态再丰富一下。
Git工作目录中的文件首先可以分为已追踪(tracked)和未追踪(untracked)两种状态。已追踪表示该文件存在与上一次提交的快照中,又可细分为已修改、已暂存和已提交。未追踪表示当前工作目录中出去已追踪的其余的所有的问题,这些文件没有包含在上一次提交的快照中。那么如何查看当前工作目录中文件的状态呢?
2.1 查看文件状态
查看文件状态可使用git status命令,在本地创建的Git项目中执行会得到:
$ git status
On branch master
No commits yet
nothing to commit (create/copy files and use "git add" to track)
上述输出首先说明了:
- 当前所处的分支为
master - 当前是个空项目,没有任何提交
- 当前项目中不存在未追踪的文件,也没有存在处于修改或暂存状态的已追踪的文件处
在刚刚克隆的Git项目中,执行该命令会得到:
$ git status
位于分支 master
您的分支与上游分支 'origin/master' 一致。
无文件要提交,干净的工作区
2.2 追踪新文件
在本地初始化的目录中添加一个名为helloworld.txt的文件,然后在执行会出现什么情况呢?
$ sudo echo 'Hello, world!' > helloworld.txt
$ git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
helloworld.txt
nothing added to commit but untracked files present (use "git add" to track)
可以看到,前两行输出没有变化,但这次输出提示我们有一个文件处于未追踪状态,并将其添加“未追踪的文件”(Untracked files)列表下面,同时,也很友好地提示我们如何将追踪当前文件。
根据提示,使用命令git add <文件>追踪helloworld.txt这个文件,然后再执行git status命令,这时的输出又是什么呢?
$ git add helloworld.txt
$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: helloworld.txt
这是我们可以看到,当前文件已处于追踪状态了,并且以添加到暂存区,等带下一次的提交。这里的列表Changes to be commited(等待提交的更改)列出了当前暂存区的文件。
2.3 暂存已修改的文件
如果这时再对处于暂存状态的helloworld.txt文件做修改,然后执行git status命令,又会得到什么样的输出呢?
$ sudo echo "我爱你,中国" > helloworld.txt
$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: helloworld.txt
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: helloworld.txt
这时的输出中出现了Changed not staged for commit(已更改但会添加到暂存区)列表,并且helloworld.txt文件也出现在这个列表中,这表明,helloworld.txt被修改但是没有添加到暂存区。
我们在上一步中不是把这个文件添加到暂存区了,怎么又会出现的这个提示呢?要解释这个问题,需要改变一下对于git add这个命令的看法。git add命令是“添加修改的内容到下一次提交中,而不是把文件添加到这个项目中”;将文件添加加到暂存区时,保存的是执行git add这个命令时该文件的状态。如果此时执行git commit命令提交,提交的内容包含的是上一次执行git add时的文件内容(Hello, world!),不会包含现有工作区中的该文件修改的内容(“我爱你,中国”)。这样是不是更好理解的呢?
我们先使用命令git add helloworld.txt将该文件再次提交到暂存区。
2.4 简化显示的状态信息
在项目中,经常会遇到多个文件处于未暂存或未提交状态,这时的git status输出无疑时很冗长的。Git提供了简化的状态输出的命令:git status -s或git status --short
$ git status -s
A bb.txt
AM helloworld.txt
?? aa.txt
怎么理解这个输出呢?
- 输出内容中包含了未追踪的文件和未暂存或未提交的已追踪的文件
- 文件名称左边有两列,最左边的列表示是否已暂存,右边的列表示是否已修改但未暂存
??表示未追踪,A表示已暂存的文件,M表示已修改的文件
留个简单的思考题,为了得到上述的输出,我做了哪些操作呢?
2.5 忽略文件
很多时候,我们并不想把一些文件或目录添(例如:node_modules)加到Git的追踪目录中,这种情况可以通过在工作区中创建.gitignore文件来解决,.gitignore文件示例:
dist/
.DS_Store
node_modules/
/coverage
*.log
.gitignore文件中每行都会匹配特定的规则来告诉Git忽略哪些文件,常用的匹配规则模式有:
- 空行或者
#开头的行将被.gitignore文件忽略 - 支持标准的
glob模式 - 以斜杠('/')开头的模式可用于禁止递归匹配
- 以斜杠('/')结尾的模式表示目录
- 以感叹号('!')开头的模式表示取反
上面提到的glob模式类似与shell所使用的简化版的正则表达式,具体来讲:
*匹配零个或多个字符,可以使用**匹配嵌套的目录,比如/a/**/z可以匹配/a/z,也可以匹配/a/b/c/z等[abc]匹配括号内任意单个字符?匹配任意单个字符- 方括号中用短划线连接的两个字符(例如:
[0-9])表示匹配这个两个字符之间的任意单个字符(示例将会匹配0到9中任意的数字)
我们来尝试这解释下.gitignore文件的示例:
dist/表示忽略dist文件下的所有文件/coverage表示忽略当前目录下的coverage,而不会忽略子目录下coverage*.log表示忽略后缀名为.log的文件
2.6 查看已暂存和未暂存的变更内容
前几小节介绍的git status可以帮助我们了解每个文件的状态,但是,并不能帮助我们了解不同状态文件之间的哪些内容发生了变化,可使用git diff命令来达到此目的。这里我们先了解如何使用git diff,详细的内容将会在后续介绍
- 对比工作区与暂存区的变更内容:
$ git diff
diff --git a/helloworld.txt b/helloworld.txt
index af5626b..3341d9a 100644
--- a/helloworld.txt
+++ b/helloworld.txt
@@ -1 +1 @@
-Hello, world!
+<E6><88><91><E7><88><B1><E4><BD><A0><EF><BC><8C><E4><B8><AD><E5>BD>
- 对比暂存区与版本库的变更内容:
$ git diff --staged
diff --git a/bb.txt b/bb.txt
index daebebf..e61ef7b 100644
--- a/bb.txt
+++ b/bb.txt
@@ -1 +1 @@
-ShiXX
+aa
需要注意的是,git diff不会对比当前工作区与版本库上一次提交的快照之间的差异,而是与暂存区的内容做的对比,如果把工作区所有的修改内容添加到暂存区,执行git diff将不会得到任何差异
2.7 提交变更
可通过git commit命令将暂存区的内容提交到版本库,这里需要注意的是,已修改但是没有添加到暂存区的内容不会提交到版本库。
如果执行git commit命令,将会打开昨天分享的笔记中设置的编辑器,然后输入提交的内容,保存,退出。编辑器中会有一些默认的注释的文本信息,在编写提交信息时,可以删除这些注释,也可以不删除,因为,退出编辑器时Git会自动删除这些注释并对比差异,把剩余的信息记录到对应的提交中
也可以执行git commit -m '<提交描述信息>'来简化上述步骤
$ git commit -m '测试'
[master 0fa7c98] 测试1
2 files changed, 2 insertions(+), 1 deletion(-)
create mode 100644 aa.txt
在输出信息中,说明了一些与提交相关的信息,如:提交到哪个分支,提交的SHA1校验和是多少(这里输出的是校验和的缩写)、提交的描述信息、有几个文件修改以及增加和删除多少行
2.8 跳过暂存区
既然可以通过-m选项简化提交操作,有没有方法跳过“添加到暂存区”这个步骤,直接将修改的文件提交呢?Git中提供了-a选项来实现。
$ git commit -a -m '<描述信息>'
2.9 移除文件
在``Git`项目中移除文件分为2中情况:
- 在工作区中移除文件,并提交
这种情况,可以首先在工作区删除文件,然后执行命令git rm将文件的移除状态添加到暂存区,然后使用命令git commit提交修改
- 保留工作区中的文件,从暂存区中删除文件
这种情况,可以通过命令git rm --cached <文件名>来实现,例如:
$ git rm --cached README
其中,文件名可以通过glob模式来匹配
2.10 移动文件
在Git中可通过git mv命令来移动文件或者重命名文件,例如:
$ git mv aa.txt cc.txt
上述命令把aa.txt重命名未cc.txt,这时使用git status命令来查看,就会发现Git实现了重命名这个动作。
$ git mv aa.txt cc.txt
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
renamed: aa.txt -> cc.txt
这条命令相当于执行了以下3条命令:
$ mv aa.txt cc.txt
$ git rm aa.txt
$ git add cc.txt