前言
这一章将讲解使用Git完成各种工作中要使用到的基础命令,基本上你大部分的工作都是使用这些基础命令来完成的。
获取Git仓库
有两种方式来取得Git项目仓库,第一种是初始化一个现有目录作为Git仓库,第二种方法是从一个服务器克隆一个现有的Git仓库。
- 在现有目录中初始化仓库
git init
该命令将创建一个名为.git的子目录,这个子目录还有你初始化的Git仓库所有的必须文件,这些文件是Git仓库的骨干。
- 克隆远程的仓库
如果你想要获得一份已经存在的Git仓库的拷贝,就可以使用克隆命令。
git clone [url]
git支持多种数据传输协议,例如url可以是https://github.com/xxx 这就是使用了http协议,也可以使用git://协议,或者使用ssh传输协议即user@server:path/to/repo.git这样的格式
记录每次更新到仓库
当我们获取了Git仓库后,剩下的就是对仓库中文件的管理。在工作目录下的每一个文件都不外乎两种状态:已跟踪和未跟踪。
已跟踪的文件指那些被纳入了版本控制的文件,在上一次快照中有他们的记录,已跟踪的文件在工作一段时间后它的状态可能处于未修改、已修改和已暂存。
未跟踪还没有被纳入版本控制中,通常是新建的一个文件,还没有被保存到上次快照的记录中。
通过git init初始化的仓库中所有的文件都是未跟踪的,而通过git clone初始化的仓库中所有的文件都是已跟踪并且处于未修改的状态。
我们使用git的过程通常都是如下步骤:
- 新建文件、修改原有文件
- 放入暂存区
- 提交暂存区中的文件将其纳入版本库即文件已跟踪
- 重复这个过程
文件的状态变化周期
- Untracked:新建的文件就是未跟踪的,你也可以将一个已跟踪的文件从版本库中删除(实际上被删除的文件还是跟踪的,你可以恢复到其删除前的一次快照,还是那句话在git会将所有的变更状态都尽可能多的保存而不是删除他)
- Staged:对于一个未跟踪的文件我们可以将他们放入暂存区
- Unmodified:位于暂存区中的文件可以被提交到版本库,这是这个文件就被记录在一个快照中被正式纳入版本库中了。
- Modified:对位于版本库中的文件进行修改,文件就会处于被修改状态,因为其与版本库中的文件不同了,要想实现这个已修改文件纳入版本库,也需要暂存、提交这两个步骤。
检查当前文件状态
要查看哪些文件处于什么状态,可以使用git status命令,实际上这个命令是显示工作区的状态,如果你git init了一个空文件夹的话,其状态如下:
(base) [hncjygd@VM_0_11_centos test]$ git status
On branch master
No commits yet
nothing to commit (create/copy files and use "git add" to track)
第一句表示当前所在的分支,并且告诉我们这个分支同远程服务器上对应的分支没有偏离。第二句告诉我们暂存区是空的,不需要提交。
这时让我们创建一个README.md文件,然后在查询状态:
(base) [hncjygd@VM_0_11_centos test]$ git status
On branch master
No commits yet
Untracked files:
(use "git add ..." to include in what will be committed)
README.md
nothing added to commit but untracked files present (use "git add" to track)
在状态报告中可知新建的README.md文件出现在Untacked files下面,即未跟踪。这说明该文件在Git之前的快照中没有这些文件,Git不会自动将他们纳入跟踪范围,除非你显式说明。其上面也明确指出如果想要跟踪他们就使用git add命令。
跟踪新文件
使用git add命令来开始跟踪一个文件:
(base) [hncjygd@VM_0_11_centos test]$ git add README.md
(base) [hncjygd@VM_0_11_centos test]$ git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached ..." to unstage)
new file: README.md
位于Changes to be committed下面的说明这些文件都处于已暂存状态。如果此时提交,那么该文件此时此刻的版本就被留存到了历史记录中。git add命令使用文件或者目录的路径作为参数,可以使用通配符例如*来表示全部,如果参数是目录,该命令将递归跟踪该目录下的所有文件。
提交更新
文件被添加的暂存区也就意味着准备妥当可以提交了,当然在提交之前最好重新查询下状态看看是否还有需要提交的文件没有被add到暂存区中。而提交命令为git commit.
git commit
直接运行git commit命令会启动一个文本编辑器(linux下通常为vim),并且其还会显示如下文本信息:
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
#
# Initial commit
#
# Changes to be committed:
# new file: README.md
#
~
你只需要在第一行输入要提交的信息就可以了,:wq退出编辑器时,Git会丢掉这些注释行,用你输入提交附带信息生成一次提交。
(base) [hncjygd@VM_0_11_centos test]$ git commit
[master (root-commit) 19c38aa] 添加README文件
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 README.md
如果你不想启动一个vim来编辑提交信息(有时候是有意义的,因为启动的vim文件中会显示要提交的内容,并且如果你使用git commit -v来启动vim的话,其中还将包括你所做的改变的diff输出,这可以让你更加确定你的修改)。我们可以直接使用git commit -m 后跟提交信息来一次性执行。
当你提交完成后,他会告诉你当前在哪个分支(master)提交的,本次提交的完整SHA校验和是多少,以及本次提交中有多少文件修订过,多少行添加或删除过。
提交时记录的是放到暂存区的快照,任何还未暂存的仍然保持已修改的状态。每一次运行提交操作,都是对你项目做一次快照,以后可以回到这个状态,或者进行比较。
跳过使用暂存区域直接更新
尽管使用暂存区域的方式来提交可以让你在提交的时候能够三思,但是有时候这样做会显得略微繁琐,git commit 提供了一个-a选项让你直接提交而跳过git add这个步骤,不过对于还没有纳入版本库的文件例如新建文件则不能执行这个操作。
暂存已修改文件
现在我们的版本库中已经有了一个README.md文件了,此时的git status中显示目前的工作目录是干净的。让我们往README.md文件中添加内容:
## 介绍
这是一个示例文件,用于学习git
此时我们来查看工作目录的状态:
(base) [hncjygd@VM_0_11_centos test]$ git status
On branch master
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git restore ..." to discard changes in working directory)
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
可以看到README.md文件已经处于modified状态,如果你想要让版本库管理它就使用git add来添加到暂存区中。
忽略文件
一般总会有一些文件无需纳入Git的管理,也不希望他们总是出现在未跟踪文件列表中,通常是一些自动生成的文件,在这种情况下,我们可以创建一个名为.gitignore的文件,列出要忽略的文件模式。文件.gitignore的格式规范如下:
- 所有空行或者#开头的被忽略
- 可以使用标准的glob模式匹配
- 匹配模式可以以/开头防止递归
- 匹配模式可以以/结尾表示目录
- 要忽略指定模式以外的文件或目录,可以在模式前添加感叹号取反
所谓的glob模式是指shell所使用的简化的正则表达式:
- 星号匹配0到多个任意字符
- [abc]匹配任何一个列在方括号内的字符
- 问号只匹配一个任意字符
.gitignore的示例如下:
# 忽略所有后缀名为.o .a的文件
*.[oa]
#只忽略当前文件夹下的TODO文件,不递归
/TODO
# 忽略所有build文件夹下的文件
bulid/
#忽略所有doc文件夹下的pdf文件
doc/**/*.pdf
实际上你通常不需要自己编写这个文件,网络上有总结好的正对于各种项目及语言的.gitignore文件列表,你只需要在他们的基础上编写自己特有的就好了。
查看修改的内容
如果我们修改了文件,使用git status中只是显示这个文件已经modified了,如果你想要知道具体修改了什么地方,这就需要用到git diff命令了。
(base) [hncjygd@VM_0_11_centos test]$ git diff
diff --git a/README.md b/README.md
index e69de29..344444b 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,2 @@
+## 介绍
+这是一个示例文件,用于学习git
需要注意的是不带参数的git diff比较的是工作区与暂存区中的区别,如果想要比较暂存区与版本库中的区别需要添加--cached参数。例如这里我们的README.md还没有提交到暂存区这个时候你执行git diff --cached没有任何反应,因为此时暂存区是空的与版本库没有任何区别,此时我们将这个已经修改的文件add到暂存区在比较就有区别了,而反过来git diff变成的空的,因为已经提交给暂存区了,此时工作目录与暂存区又完全一致了。
(base) [hncjygd@VM_0_11_centos test]$ git diff --cached
(base) [hncjygd@VM_0_11_centos test]$ git add README.md
(base) [hncjygd@VM_0_11_centos test]$ git diff --cached
diff --git a/README.md b/README.md
index e69de29..344444b 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,2 @@
+## 介绍
+这是一个示例文件,用于学习git
(base) [hncjygd@VM_0_11_centos test]$ git diff
移除文件
要从Git中移除某个文件,就必须要从已跟踪文件清单中移除,然后提交。可以用git rm命令来完成此项操作,它还会从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了。
正常移除工作区的一个文件
例如README.md文件已经生成了一个快照,但是我们现在想在版本库中删除他。
(base) [hncjygd@VM_0_11_centos test]$ rm README.md
(base) [hncjygd@VM_0_11_centos test]$ ls
(base) [hncjygd@VM_0_11_centos test]$ git status
On branch master
Changes not staged for commit:
(use "git add/rm ..." to update what will be committed)
(use "git restore ..." to discard changes in working directory)
deleted: README.md
no changes added to commit (use "git add" and/or "git commit -a")
你会看到提示Changes not staged for commit,并且文件被表名deleted。这说明你在工作目录中删除了一个文件,但是这个删除文件的操作还没有被记录到暂存区中,此时你可以执行 git rm将这次操作添加到暂存区中。
(base) [hncjygd@VM_0_11_centos test]$ git rm README.md
rm 'README.md'
(base) [hncjygd@VM_0_11_centos test]$ git status
On branch master
Changes to be committed:
(use "git restore --staged ..." to unstage)
deleted: README.md
这样在下一次提交时,在创建的新的快照中就没有这个文件了,也就是说在新的快照中该文件就不在纳入版本管理了。
(base) [hncjygd@VM_0_11_centos test]$ git commit -m "rm README"
[master c8ec9f5] rm README
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 README.md
(base) [hncjygd@VM_0_11_centos test]$ git status
On branch master
nothing to commit, working tree clean
(base) [hncjygd@VM_0_11_centos test]$ ls
你也可以直接使用git rm README.md这样的命令,他会默认执行rm操作将工作目录的文件删除。
删除文件的同时保留工作目录中的文件
如果我们想要把文件从git仓库中删除,但仍然希望保留在当前工作目录中,换句话说,你想让文件保留在磁盘中,但是并不想让git继续跟踪。通常用在忘记添加.gitignore文件,不小心将一些不需要的文件上蹿到暂存区时,这一做法尤其有用。为了达到这个目的,可以使用git commit --cached来实现。
(base) [hncjygd@VM_0_11_centos test]$ touch test.md
(base) [hncjygd@VM_0_11_centos test]$ git add test.md
(base) [hncjygd@VM_0_11_centos test]$ git commit -m 'add test'
[master b89242e] add test
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 test.md
(base) [hncjygd@VM_0_11_centos test]$ git status
On branch master
nothing to commit, working tree clean
(base) [hncjygd@VM_0_11_centos test]$ git rm --cached test.md
rm 'test.md'
(base) [hncjygd@VM_0_11_centos test]$ git status
On branch master
Changes to be committed:
(use "git restore --staged ..." to unstage)
deleted: test.md
Untracked files:
(use "git add ..." to include in what will be committed)
test.md
(base) [hncjygd@VM_0_11_centos test]$ git commit -m 'del test'
[master aece6a5] del test
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 test.md
(base) [hncjygd@VM_0_11_centos test]$ git status
On branch master
Untracked files:
(use "git add ..." to include in what will be committed)
test.md
nothing added to commit but untracked files present (use "git add" to track)
(base) [hncjygd@VM_0_11_centos test]$ ls
test.md
可以看到,原有git rm会默认先将工作目录下的文件删除并将这个删除动作添加到暂存区,然后我们在commit时将这个文件脱离跟踪。而--cached选项唯一的不同就是不在删除工作目录下的文件,所以在执行完git commit命令后,工作区的状态是有一个未跟踪的文件test.md而使用git rm时工作区的状态是干净的。
移动文件
使用git mv命令来移动,而改名操作也是通过这个命令来实现的。而在实际原理中git命令等同于执行:
mv READMD.md README
git rm README.md
git add README
你完全可用这样分开操作,而且Git也能智能的意识到这是一次改名操作,不管何种方式结果都是一样的,两者唯一的区别就是mv是一条命令,而另一种是三条命令。不过这三条命令也是有其存在意义的,如果你使用其他工具来批量改名后,需要先提交删除老的文件名,在添加新的文件名。