1、Git的基本使用
1.1 Git是什么?
Git是目前世界上最先进的分布式版本控制系统(没有之一)
那什么是版本控制系统?如果你用Microsoft Word写过长篇大论,那你一定有这样的经历:
想删除一个段落,又怕将来想恢复找不回来怎么办?有办法,先把当前文件“另存为……”一个新的Word文件,再接着改,改到一定程度,再“另存为……”一个新文件,这样一直改下去,最后你的Word
过了一周,你想找回被删除的文字,但是已经记不清删除前保存在哪个文件里了,只好一个一个文件去找,真麻烦。于是你想,如果有一个软件,不但能自动帮我记录每次文件的改动,这个软件用起来就应该像这个样子,能记录每次文件的改动:
版本 | 文件名 | 用户 | 说明 | 日期 |
---|---|---|---|---|
1 | service.doc | 张三 | 删除了软件服务条款5 | 7/12 10:38 |
2 | service.doc | 张三 | 增加了License人数限制 | 7/12 18:09 |
3 | service.doc | 李四 | 财务部门调整了合同金额 | 7/13 9:51 |
4 | service.doc | 张三 | 延长了免费升级周期 | 7/14 15:17 |
这样,你就结束了手动管理多个“版本”的史前时代,进入到版本控制的20世纪
1.2 Git的安装
在Windows上使用Git,可以从Git官网直接下载安装程序,(网速慢的同学请移步国内镜像),然后按默认选项安装即可。
安装完成后,在开始菜单里找到“Git”->“Git Bash”,蹦出一个类似命令行窗口的东西,就说明Git安装成功!
安装完成后,还需要最后一步设置,在命令行输入:
//因为Git是分布式版本控制系统,所以每个机器都必须自报家门:你的名字和Email地址
$ git config --global user.name "Your Name"
$ git config --global user.email "email@example.com
1.3 创建版本库
什么是版本库呢?版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。
1.3.1 创建一个空目录
$ mkdir learngit
$ cd learngit
1.3.2 初始化仓库
$ git init
Initialized empty Git repository in D:/Git/.git/
瞬间Git就把仓库建好了,而且告诉你是一个空的仓库(empty Git repository),细心的读者可以发现当前目录下多了一个.git
的目录,这个目录是Git来跟踪管理版本库的,没事千万不要手动修改这个目录里面的文件,不然改乱了,就把Git仓库给破坏了。
1.4 Git常用操作
使用Windows的童鞋要特别注意:千万不要使用Windows自带的记事本编辑任何文本文件。原因是Microsoft开发记事本的团队使用了一个非常弱智的行为来保存UTF-8编码的文件!
现在我们在learngit目录下新建一个 a.js
文件,内容如下:
console.log("a");
console.log("b");
1.4.1 git status
git status 命令可以随时掌握仓库的状态
1.4.2 git add
$ git add a.js //把文件添加到暂存区
1.4.3 git commit
$ git commit -m "wrote a.js"
[master (root-commit) b10aa1b] wrote a.js
1 file changed, 2 insertions(+)
create mode 100644 a.js
简单解释一下git commit命令,-m后面输入的是本次提交的说明,可以输入任意内容,当然最好是有意义的,这样你就能从历史记录里方便地找到改动记录。
1.4.4 git diff
我们已经成功地添加并提交了一个a.js文件,于是我们继续修改a.js文件,改成如下内容:
console.log("a");
console.log("b");
console.log("c");
现在,运行git status命令查看结果:
$ 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: a.js
no changes added to commit (use "git add" and/or "git commit -a"
使用git diff 查看版本与库的区别
$ git diff
diff --git a/a.js b/a.js
index 2314941..5cf5101 100644
--- a/a.js
+++ b/a.js
@@ -1,2 +1,3 @@
console.log("a");
-console.log("b");
\ No newline at end of file
+console.log("b");
+console.log("c");
\ No newline at end of file
使用下面的命令将文件提交到仓库中
$ git add readme.txt
$ git commit -m "添加打印c"
$ git status
//Git告诉我们当前没有需要提交的修改,而且,工作目录是干净(working tree clean)的。
2. 时空穿梭
2.1 查看历史版本
修改a.js
console.log("a");
console.log("b");
console.log("c");
console.log("d");
然后尝试提交
$ git add a.js
$ git commit -m "添加打印d"
[master 1e7d4ff] 添加打印d
1 file changed, 2 insertions(+), 1 deletion(-)
在Git中,我们用 git log --pretty=oneline
命令查看:
$ git log --pretty=oneline
1e7d4ff9d288e0e4f230f543bd4641df5995b172 (HEAD -> master) 添加
打印d
55559e95b25b3113dba1317398123c80d4e5fa49 添加打印c
b10aa1bcf48e6d21ff8cf0128fd0971e2350864a wrote a.js
2.2 版本回退
用 HEAD
表示当前版本
上一个版本就是 HEAD^
往上100个版本写成 HEAD~100
//回退上一个版本``
$ git reset --hard HEAD`
//使用git log查看版本信息
$ git log --pretty=oneline
55559e95b25b3113dba1317398123c80d4e5fa49 (HEAD -> master) 添加
打印c
b10aa1bcf48e6d21ff8cf0128fd0971e2350864a wrote a.js
2.3 版本还原
当我们使用git log命令查看版本库的时候,我们发现最新的那个版本看不到了!如果此时我们想要回到最新版本,该怎么办呢?
Git提供了一个命令git reflog
用来记录你的每一次命令:
$ git reflog
e83249e (HEAD -> master) HEAD@{0}: reset: moving to HEAD^
6975a37 HEAD@{1}: commit: 添加打印d
e83249e (HEAD -> master) HEAD@{2}: commit: '添加打印c'
a97486a HEAD@{3}: commit (initial): wrote a.js
使用下面命令就可以指定回到未来的某个版本:
$ git reset --hard 6975a
HEAD is now at 6975a37 添加打印d
// 版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的 HEAD
指针,当你回退版本的时候,Git仅仅是把HEAD的指向改变了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M7x0trTY-1665451388017)(E:/%E5%AD%A6%E4%B9%A0/01.Git/%E8%AF%BE%E4%BB%B6/assets/0-1544519271750.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y1Qfz3Uo-1665451388018)(E:/%E5%AD%A6%E4%B9%A0/01.Git/%E8%AF%BE%E4%BB%B6/assets/0-1544519274176.jpg)]
3.工作区和缓存区
Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。
先来看名词解释。
3.1 工作区(Working Directory)
就是你在电脑里能看到的目录,比如我的 learngit
文件夹就是一个工作区:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zAJa2y9g-1665451388018)(E:/%E5%AD%A6%E4%B9%A0/01.Git/%E8%AF%BE%E4%BB%B6/assets/1581139350602.png)]
3.2 暂存区和本地仓库(Repository)
工作区有一个隐藏目录 .git
,这个不算工作区,而是Git的版本库。
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master
,以及指向master
的一个指针叫HEAD
。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9WZ3O6DS-1665451388019)(E:/%E5%AD%A6%E4%B9%A0/01.Git/%E8%AF%BE%E4%BB%B6/assets/0-1544520027136.jpg)]
分支和 HEAD
的概念我们以后再讲。
前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:
>第一步 `git add` 把文件和文件修改添加到暂存区;
>第二步 `git commit` 把暂存区的所有内容提交到当前分支。
因为我们创建Git版本库时,Git自动为我们创建了唯一一个master
分支,所以现在git commit
就是往master
分支上提交更改。
3.3工作区、暂存区和本地仓库详解
修改a.js,新建b.js
console.log("a");
console.log("b");
console.log("c");
console.log("d");
使用git status查看
$ 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: a.js
Untracked files:
(use "git add <file>..." to include in what will be committed)
b.js
no changes added to commit (use "git add" and/or "git commit -a")
Git非常清楚地告诉我们,a.js被修改了,b.js是未跟踪状态。
使用git add *之后再查看状态
$ git add *
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: a.js
new file: b.js
现在,暂存区的状态就变成这样了:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xi1NT6Xw-1665451388019)(E:/%E5%AD%A6%E4%B9%A0/01.Git/%E8%AF%BE%E4%BB%B6/assets/0-1544520023847.jpg)]
所以, git add
命令实际上就是把要提交的所有修改放到暂存区(Stage)。然后执行git commit
就可以一次性把暂存区的所有修改提交到分支。
$ git commit -m '新增b.js,a.js新增打印e'
[master 8261415] 新增b.js,a.js新增打印e
2 files changed, 2 insertions(+), 1 deletion(-)
create mode 100644 b.js
一旦提交后,如果你又没有对工作区做任何修改,那么工作区就是“干净”的:
$ git status
On branch master
nothing to commit, working tree clean
现在版本库变成了这样,暂存区就没有任何内容了:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AkET5qU1-1665451388020)(E:/%E5%AD%A6%E4%B9%A0/01.Git/%E8%AF%BE%E4%BB%B6/assets/0-1544520020528.jpg)]
3.4 撤销修改
3.4.1 撤销工作区的修改
继续修改a.js,当我们准备提交代码的时候,发现其中某行代码发生了错误,想要撤回到之前没有修改过的状态。
console.log("a");
console.log("b");
console.log("c");
console.log("d");
console.log("e");
console.log("哈哈");
console.log("嘻嘻");
我们使用 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: a.js
no changes added to commit (use "git add" and/or "git commit -a")
你可以发现,Git会告诉你,git checkout -- file
可以丢弃工作区的修改:
$ git checkout -- a.js
命令 git checkout -- a.js
意思就是,把a.js
文件在工作区的修改全部撤销,这里有两种情况:
一种是 a.js
自修改后还没有被放到暂存区,现在撤销修改就回到和版本库一模一样的状态;
一种是 a.js
已经添加到暂存区后同时作了修改,现在撤销修改就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次 git commit
或git add
时的状态。
现在,看看 readme.txt
的文件内容:
console.log("a");
console.log("b");
console.log("c");
console.log("d");
console.log("e");
3.4.2 撤销暂存区的修改
假如我们在a.js中做了修改,并且使用 git add
到暂存区了:
console.log("a");
console.log("b");
console.log("c");
console.log("d");
console.log("e");
console.log("哈哈")
console.log("嘻嘻")
$ git add a.js
接下来我们使用 git status
查看一下,修改添加到了暂存区,还没有commit:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: a.js
接下来Git告诉我们,用命令 git reset HEAD <file>
可以把暂存区的修改撤销掉(unstage),重新放回工作区:
$ git reset HEAD a.js
Unstaged changes after reset:
M a.js
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: a.js
3.5 git reset的三个参数
三者的使用情况:
1.代码编写及修改是在工作区
2.git add 将本地修改添加到暂存区
3.git commit 将暂存区中的内容提交到本地仓库
git reset --hard HEAD^
三者的改变全都丢失,即代码的修改内容丢失
git reset --soft HEAD^
回退到git commit之前,此时处在暂存区。(即执行git add 命令后)
git reset --mixed HEAD^ (默认)
就等于 git reset HEAD 回退到工作区,即git add 之前
3.6 删除
要从版本库中删除该文件,那就用命令 git rm
删掉,并且git commit
:
$ git rm a.js
rm 'a.js'
$ git commit -m "remove a.js"
[master d46f35e] remove a.js
1 file changed, 1 deletion(-)
delete mode 100644 a.js
现在,文件就从版本库中被删除了。
另一种情况是删错了,没有commit,此时版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:
$ git reset
$ git checkout -- a.js
git checkout
其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
git reset HEAD 回退到工作区,即git add 之前
3.6 删除
要从版本库中删除该文件,那就用命令 git rm
删掉,并且git commit
:
$ git rm a.js
rm 'a.js'
$ git commit -m "remove a.js"
[master d46f35e] remove a.js
1 file changed, 1 deletion(-)
delete mode 100644 a.js
现在,文件就从版本库中被删除了。
另一种情况是删错了,没有commit,此时版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:
$ git reset
$ git checkout -- a.js
git checkout
其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。