【Git】从零开始的 Git 基本操作

文章目录

  • [1. 创建 Git 本地仓库](#1. 创建 Git 本地仓库)
  • [2. 配置 Git](#2. 配置 Git)
  • [3. 认识工作区、暂存区、版本库](#3. 认识工作区、暂存区、版本库)
    • [3.1 添加文件 | 场景一](#3.1 添加文件 | 场景一)
    • [3.2 查看 .git 文件](#3.2 查看 .git 文件)
    • [3.3 添加文件 | 场景二](#3.3 添加文件 | 场景二)
  • [4. 修改文件](#4. 修改文件)
  • [5. 版本回退](#5. 版本回退)
  • [6. 撤销修改](#6. 撤销修改)
    • [6.1 情况一:对于工作区的代码,还没有 add](#6.1 情况一:对于工作区的代码,还没有 add)
    • [6.2 情况二:已经 add,但没有 commit](#6.2 情况二:已经 add,但没有 commit)
    • [6.3 情况三:已经 add 了,并且也 commit 了](#6.3 情况三:已经 add 了,并且也 commit 了)
  • [7. 删除文件](#7. 删除文件)
  • [8. 本文命令总结](#8. 本文命令总结)

1. 创建 Git 本地仓库

要提前说的是,仓库是进行版本控制的一个文件目录。我们想对文件进行版本控制,就必须先创建一个仓库出来。

创建一个 Git 本地仓库对应的命令为 git init,注意命令要在文件目录下执行,例如:

bash 复制代码
ubuntu@VM-20-5-ubuntu:~/gitcode$ pwd
/home/ubuntu/gitcode
ubuntu@VM-20-5-ubuntu:~/gitcode$ git init
Initialized empty Git repository in /home/ubuntu/gitcode/.git/
ubuntu@VM-20-5-ubuntu:~/gitcode$ ll -a
total 12
drwxrwxr-x  3 ubuntu ubuntu 4096 Apr 16 19:26 ./
drwxr-xr-x 10 ubuntu ubuntu 4096 Apr 16 19:26 ../
drwxrwxr-x  7 ubuntu ubuntu 4096 Apr 16 19:31 .git/

我们发现,当前目录下多了一个 .git 的隐藏文件,.git 目录是 Git 用来跟踪管理仓库的,不要手动修改这个目录里面的文件,不然改乱了,就把 Git 仓库给破坏了

其中包含 Git 仓库的诸多细节,使用 tree .git/ 命令看看:

bash 复制代码
ubuntu@VM-20-5-ubuntu:~/gitcode$ tree .git/
.git/
├── branches
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-merge-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   └── update.sample
├── info
│   └── exclude
├── objects
│   ├── info
│   └── pack
└── refs
    ├── heads
    └── tags

9 directories, 16 files

2. 配置 Git

当安装 Git 后首先要做的事情是设置你的 用户名称E-mail 地址,这是非常重要的。配置命令为:

bash 复制代码
git config [--global] user.name "your name"
git config [--global] user.email "email@example.com"

# 把 your name 改为你的昵称
# 把 email@example.com 改为你的邮箱
# [--global] 代表 --global 是一个可选项,输入命令的时候不要加方框!!!

其中 --global 是一个可选项。如果使用了该选项,表示这台机器上所有的 Git 仓库都会使用这个配置。如果你希望在不同仓库中使用不同的 namee-mail,可以不要 --global 这个选项,但要注意的是,执行命令时必须要在仓库里

查看配置的命令为:

bash 复制代码
git config -l
bash 复制代码
# 实机演示
ubuntu@VM-20-5-ubuntu:~/gitcode$ git config user.name "tjq"
ubuntu@VM-20-5-ubuntu:~/gitcode$ git config user.email "1234567890@qq.com"
ubuntu@VM-20-5-ubuntu:~/gitcode$ git config -l
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
user.name=tjq					# 新增
user.email=1234567890@qq.com	# 新增

删除对应配置的命令为:

bash 复制代码
git config [--global] --unset user.name "your name"
git config [--global] --unset user.email "email@example.com"

# 如果配置的时候加了 --global 选项,那么删除的时候也要加

删除配置后,可以使用查看配置的命令(git config -l)查看是否真的已经删除。

bash 复制代码
# 实机演示
ubuntu@VM-20-5-ubuntu:~/gitcode$ git config --unset user.name "tjq"
ubuntu@VM-20-5-ubuntu:~/gitcode$ git config --unset user.email "1234567890@qq.com"
ubuntu@VM-20-5-ubuntu:~/gitcode$ git config -l
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true

3. 认识工作区、暂存区、版本库

  • 工作区:是在电脑上你要写代码或文件的目录。
  • 暂存区 :英文叫 stage 或 index。一般存放在 .git 目录下的 index 文件(.git/index)中,我们有时把暂存区也叫做索引(index)。
  • 版本库 :又名仓库,英文名 repository 。工作区有一个隐藏目录 .git ,它不算工作区,而是 Git 的版本库。这个版本库里面的所有文件都可以被 Git 管理起来,每个文件的修改、删除,Git 都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以"还原"。

下图展示了工作区、暂存区和版本库之间的关系:

  • 图中左侧为工作区,右侧为版本库。Git 的版本库里存了很多东西,其中最重要的就是暂存区。
  • 在创建 Git 版本库时,Git 会为我们自动创建一个唯一的 master 分支,以及指向 master 的一个指针 HEAD。
  • 当对工作区修改(或新增)的文件执行 git add 命令时,暂存区目录树的文件索引会被更新。
  • 当执行提交操作 git commit 时,master 分支会做相应的更新,可以简单理解为暂存区的目录树才会被真正写到版本库中。

由上述描述中,我们得知:通过新建或粘贴进目录的文件,并不能称之为向仓库中新增文件,而只是在工作区新增了文件。必须要通过使用 git addgit commit 命令才能将文件添加到仓库中进行管理!

3.1 添加文件 | 场景一

在包含 .git 的目录下新建一个 ReadMe 文件,我们可以使用 git add 命令将文件添加到暂存区:

bash 复制代码
# 添加一个或多个文件到暂存区:
git add [file1] [file2] ...

# 添加指定目录到暂存区,包括子目录:
git add [dir]

# 添加当前目录下的所有文件改动到暂存区:
git add .

再使用 git commit 命令将暂存区内容添加到本地仓库中:

bash 复制代码
# 提交暂存区全部内容到本地仓库中:
git commit -m "message"

# 提交暂存区的指定文件到仓库区:
git commit [file1] [file2] ... -m "message"

注意 git commit 后面的 -m 选项,要跟上描述本次提交的 message,由用户自己完成,这部分内容绝对不能省略,并要好好描述,是用来记录你的提交细节,是给我们人看的。

bash 复制代码
# 实机演示
ubuntu@VM-20-5-ubuntu:~/gitcode$ touch ReadMe
ubuntu@VM-20-5-ubuntu:~/gitcode$ la
.git  ReadMe
ubuntu@VM-20-5-ubuntu:~/gitcode$ vim ReadMe
ubuntu@VM-20-5-ubuntu:~/gitcode$ cat ReadMe 
hello git
ubuntu@VM-20-5-ubuntu:~/gitcode$ git add ReadMe 
ubuntu@VM-20-5-ubuntu:~/gitcode$ git commit -m "add first file"
[master (root-commit) daca7d4] add first file
 1 file changed, 1 insertion(+)
 create mode 100644 ReadMe

git commit 命令执行成功后会告诉我们,1 个文件被改动(就是我们新添加的 ReadMe 文件),插入了一行内容。

我们还可以多次 add 不同的文件,而只 commit 一次便可以提交所有文件,是因为需要提交的文件是通通被 add 到暂存区中,然后一次性 commit 暂存区的所有修改。

bash 复制代码
# 实机演示
ubuntu@VM-20-5-ubuntu:~/gitcode$ touch file1 file2 file3
ubuntu@VM-20-5-ubuntu:~/gitcode$ la
file1  file2  file3  .git  ReadMe
ubuntu@VM-20-5-ubuntu:~/gitcode$ git add file1 file2 file3
ubuntu@VM-20-5-ubuntu:~/gitcode$ git commit -m "add 3 files"
[master 63ffac4] add 3 files
 3 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 file1
 create mode 100644 file2
 create mode 100644 file3

截至目前为止,我们已经能够将代码直接提交至本地仓库了。那么可以查看我们的历史提交记录吗?如何查看?

bash 复制代码
# 查看历史提交记录
git log

# 查看美观且简略的历史提交记录
git log --pretty=oneline

该命令显示从最近到最远的提交日志,并且可以看到我们 commit 时的日志消息。

如果嫌输出信息太多,看得眼花缭乱的,可以试试加上 --pretty=oneline 参数。

bash 复制代码
# 实机演示
ubuntu@VM-20-5-ubuntu:~/gitcode$ git log
commit 63ffac4330249843d4b68f6a5f59384ba09a0358 (HEAD -> master)
Author: tjq <1234567890@qq.com>
Date:   Thu Apr 18 10:46:40 2024 +0800

    add 3 files

commit daca7d41906c112f4b11c148a43cd2a0c23a4871
Author: tjq <1234567890@qq.com>
Date:   Thu Apr 18 10:20:39 2024 +0800

    add first file
# 上面信息太多?试试加上 --pretty=oneline
ubuntu@VM-20-5-ubuntu:~/gitcode$ git log --pretty=oneline 
63ffac4330249843d4b68f6a5f59384ba09a0358 (HEAD -> master) add 3 files
daca7d41906c112f4b11c148a43cd2a0c23a4871 add first file

需要说明的是,我们看到的一大串类似 63ffa...a0358 的数字是每次提交的 commit id(版本号),Git 的 commit id 不是 1, 2, 3, ... 递增的数字,而是一个 SHA1 计算出来的一个非常大的数字,用十六进制表示。

3.2 查看 .git 文件

先来看看我们的 .git 目录结构:

bash 复制代码
ubuntu@VM-20-5-ubuntu:~/gitcode$ tree .git/
.git/
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── fsmonitor-watchman.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── pre-merge-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   ├── pre-receive.sample
│   └── update.sample
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── 0e
│   │   └── 6b1780b73cd9220ec5073dc64b42f7ad4bd945
│   ├── 15
│   │   └── a37e9ef171cca4a5d985fccd1fcf9414b2c7cf
│   ├── 63
│   │   └── ffac4330249843d4b68f6a5f59384ba09a0358
│   ├── 8d
│   │   └── 0e41234f24b6da002d962a26c2495ea16a425f
│   ├── da
│   │   └── ca7d41906c112f4b11c148a43cd2a0c23a4871
│   ├── e6
│   │   └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master
    └── tags

18 directories, 27 files
  1. index 就是我们的暂存区,add 后的内容都是添加到这里的。

  2. HEAD 就是我们默认指向 master 分支的指针;

    bash 复制代码
    ubuntu@VM-20-5-ubuntu:~/gitcode$ cat .git/HEAD 
    ref: refs/heads/master

    而默认的 master 分支又是什么呢?

    bash 复制代码
    ubuntu@VM-20-5-ubuntu:~/gitcode$ cat .git/refs/heads/master 
    63ffac4330249843d4b68f6a5f59384ba09a0358

    打印的 63ffac4330249843d4b68f6a5f59384ba09a0358 是什么东西呢?是不是看起来有点眼熟,其实保存的就是当前最新的 commit id

  3. objects 为 Git 的对象库,里面包含了创建的各种版本库对象及内容。当执行 git add 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,就位于 .git/objects 目录下,让我们来看看这些对象有何用处:

    bash 复制代码
    ubuntu@VM-20-5-ubuntu:~/gitcode$ ls .git/objects/
    0e  15  63  8d  da  e6  info  pack

    查找 objects 时要将 commit id 分成 2 部分,其中前 2 位是文件夹名称,后 38 位是文件名称。

    找到这个文件之后,一般不能直接看到里面是什么,该类文件是经过 sha(安全哈希算法)加密过的文件,但是我们可以使用 git cat-file 命令来查看版本库对象的内容:

    bash 复制代码
    git cat-file [选项] [要查看的内容]
    bash 复制代码
    # 实机演示
    ubuntu@VM-20-5-ubuntu:~/gitcode$ git cat-file -p 63ffac4330249843d4b68f6a5f59384ba09a0358
    tree 15a37e9ef171cca4a5d985fccd1fcf9414b2c7cf
    parent daca7d41906c112f4b11c148a43cd2a0c23a4871
    author tjq <2780888910@qq.com> 1713408400 +0800
    committer tjq <2780888910@qq.com> 1713408400 +0800
    
    add 3 files
    # 这是我们最近一次的提交

    其中,还有一行 tree 15a37e9ef171cca4a5d985fccd1fcf9414b2c7cf ,我们使用同样的方法,看看结果:

    bash 复制代码
    ubuntu@VM-20-5-ubuntu:~/gitcode$ git cat-file -p 15a37e9ef171cca4a5d985fccd1fcf9414b2c7cf
    100644 blob 8d0e41234f24b6da002d962a26c2495ea16a425f	ReadMe
    100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391	file1
    100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391	file2
    100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391	file3

    再看 ReadMe 对应的 8d0e41234f24b6da002d962a26c2495ea16a425f

    bash 复制代码
    ubuntu@VM-20-5-ubuntu:~/gitcode$ git cat-file -p 8d0e41234f24b6da002d962a26c2495ea16a425f
    hello git
    # 这是我们对 ReadMe 做的修改,被 git 记录了下来!

总结一下,在本地的 git 仓库中,有几个文件或者目录很特殊:

  • index :暂存区,git add 后会更新该内容。
  • HEAD:默认指向 master 分支的一个指针。
  • refs / heads / master :文件里保存当前 master 分支的最新 commit id
  • objects:包含了创建的各种版本库对象及内容,可以简单理解为放了 git 维护的所有修改。

在学习 git 过程中,我们最好能将常见的 git 操作与 .git 目录当中的结构内容变化对应起来,这样有利于我们理解 git 细节流程!

3.3 添加文件 | 场景二

学习到这里,我们已经清楚了如何向仓库中添加文件,并且对于工作区、暂存区、版本库也有了一定的认识。那么我们再展示一种添加文件的场景,能加深对工作区、暂存区、版本库的理解,示例如下:

bash 复制代码
ubuntu@VM-20-5-ubuntu:~/gitcode$ touch file4				# 1. 新增 file4 文件
ubuntu@VM-20-5-ubuntu:~/gitcode$ git add file4				# 2. 将 file4 添加到暂存区
ubuntu@VM-20-5-ubuntu:~/gitcode$ touch file5				# 3. 新增 file5 文件
ubuntu@VM-20-5-ubuntu:~/gitcode$ git commit -m "add file"	# 4. 提交修改
[master 6856fbd] add file
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 file4

提交后发现打印了 1 file changed, 0 insertions(+), 0 deletions(-) ,意思是只有一个文件改变了,这时我们提出了疑问,不是新增了两个文件吗?

再来回忆下,git add 是将文件添加到暂存区,git commit 是将暂存区的内容添加到本地仓库中。由于我们并没有使用 git add file5 ,file5 就不在暂存区中维护,所以我们 commit 的时候其实只是把已经在暂存区的 file4 提交了,而遗漏了工作区的 file5。

4. 修改文件

Git 比其他版本控制系统设计的更优秀,因为 Git 跟踪并管理的是修改,而非文件

什么是修改?比如你新增了一行,这就是一个修改;更改了某些字符,是一个修改;创建一个新文件,也是一个修改。

下面我们对 ReadMe 文件进行一次修改:

bash 复制代码
# 实机演示
ubuntu@VM-20-5-ubuntu:~/gitcode$ la
file1  file2  file3  file4  file5  .git  ReadMe
ubuntu@VM-20-5-ubuntu:~/gitcode$ cat ReadMe 
hello git
ubuntu@VM-20-5-ubuntu:~/gitcode$ vim ReadMe 
ubuntu@VM-20-5-ubuntu:~/gitcode$ cat ReadMe 
hello git
hello world

此时,仓库中的 ReadMe 和我们工作区的 ReadMe 是不同的,如何查看当前仓库状态呢?

bash 复制代码
# 查看仓库状态
git status
bash 复制代码
# 可以用这个命令查看在上次提交之后,是否对代码进行了再次修改
# 实机演示
ubuntu@VM-20-5-ubuntu:~/gitcode$ 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:   ReadMe

no changes added to commit (use "git add" and/or "git commit -a")

上面的结果告诉我们,ReadMe 被修改过了,但还没有完成添加与提交。

目前,我们只知道文件被修改了,如果能知道具体哪些地方被修改了,就更好了,这样代码多了以后也可以很方便的进行对比管理。怎么做呢?

bash 复制代码
# 查看暂存区和工作区文件的差异
git diff [file]

# 查看版本库和工作区文件的差异
git diff HEAD -- [file]
bash 复制代码
# git diff [file] 实机演示
ubuntu@VM-20-5-ubuntu:~/gitcode$ git diff ReadMe
diff --git a/ReadMe b/ReadMe
index 8d0e412..05fe86c 100644
--- a/ReadMe
+++ b/ReadMe
@@ -1 +1,2 @@
 hello git
+hello world

# git diff HEAD -- [file] 实机演示
ubuntu@VM-20-5-ubuntu:~/gitcode$ git diff HEAD -- ReadMe
diff --git a/ReadMe b/ReadMe
index 8d0e412..05fe86c 100644
--- a/ReadMe
+++ b/ReadMe
@@ -1 +1,2 @@
 hello git
+hello world

git diff [file] 命令用来显示暂存区和工作区文件的差异,显示的格式正是 Unix 通用的 diff 格式。也可以使用 git diff HEAD -- [file] 命令来查看版本库和工作区文件的区别。

知道了对 ReadMe 做了什么修改后,再把它提交到本地仓库就放心多了。

bash 复制代码
# git add 后再次查看仓库状态
ubuntu@VM-20-5-ubuntu:~/gitcode$ git add ReadMe 
ubuntu@VM-20-5-ubuntu:~/gitcode$ git status 
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   ReadMe

git add 之后,就没有看到上面 no changes added to commit (use "git add" and/or "git commit -a") 的消息了。接下来我们继续 git commit 即可:

bash 复制代码
# git commit 后再次查看仓库状态
ubuntu@VM-20-5-ubuntu:~/gitcode$ git commit -m "modify ReadMe file"
[master 1c25401] modify ReadMe file
 1 file changed, 1 insertion(+)
ubuntu@VM-20-5-ubuntu:~/gitcode$ git status 
On branch master
nothing to commit, working tree clean	# 仓库状态恢复正常

5. 版本回退

之前我们提到过,Git 能够管理文件的历史版本,这也是版本控制器重要的能力。如果有一天你发现之前做的工作出现了很大的问题,需要在某个特定的历史版本重新开始,这个时候,就需要版本回退的功能了。

bash 复制代码
# 版本回退
git reset [--soft | --mixed | --hard] [HEAD]

执行 git reset 命令用于版本回退,可以指定退回某一次提交的版本。要解释一下"回退"本质是要将版本库中的内容进行回退,工作区或暂存区是否回退由命令参数决定

选项 工作区 暂存区 版本库
--soft 不回退 不回退 回退
--mixed(默认) 不回退 回退 回退
--hard(慎用) 回退 回退 回退
  • --soft 参数对于工作区和暂存区的内容都不变,只是将版本库回退到某个指定版本。
  • --mixed 为默认选项,使用时可以不用带该参数。该参数将暂存区的内容退回为指定提交版本内容,工作区文件保持不变。
  • --hard 参数将暂存区与工作区都退回到指定版本。切记工作区有未提交的代码时不要用这个命令,因为工作区会回滚,你没有提交的代码就再也找不回了,所以使用该参数一定要慎重
  • HEAD 说明:
    • 可直接写成 commit id,表示指定退回的版本
    • 可以使用 ^ 表示:
      • HEAD 表示当前版本
      • HEAD^ 表示上一个版本
      • HEAD^^ 表示上上个版本
      • ...
    • 也可以使用 ~数字 表示:
      • HEAD~0 表示当前版本
      • HEAD~1 表示上一个版本
      • HEAD~2 表示上上个版本
      • ...

为了便于表述,方便测试回退功能,我们先做一些准备工作:更新 3 个版本的 ReadMe,并分别进行 3 次提交,如下所示:

bash 复制代码
# 第一次修改提交
ubuntu@VM-20-5-ubuntu:~/gitcode$ cat ReadMe 
hello git
hello world
hello version1		# 第1次新增
ubuntu@VM-20-5-ubuntu:~/gitcode$ git add ReadMe 
ubuntu@VM-20-5-ubuntu:~/gitcode$ git commit -m "add version1"
[master 1fbbbb8] add version1
 1 file changed, 1 insertion(+)

# 第二次修改提交
ubuntu@VM-20-5-ubuntu:~/gitcode$ cat ReadMe 
hello git
hello world
hello version1		# 第1次新增
hello version2		# 第2次新增
ubuntu@VM-20-5-ubuntu:~/gitcode$ git add ReadMe 
ubuntu@VM-20-5-ubuntu:~/gitcode$ git commit -m "add version2"
[master 792517a] add version2
 1 file changed, 1 insertion(+)

# 第三次修改提交
ubuntu@VM-20-5-ubuntu:~/gitcode$ cat ReadMe 
hello git
hello world
hello version1		# 第1次新增
hello version2		# 第2次新增
hello version3		# 第3次新增
ubuntu@VM-20-5-ubuntu:~/gitcode$ git add ReadMe 
ubuntu@VM-20-5-ubuntu:~/gitcode$ git commit -m "add version3"
[master ca0330f] add version3
 1 file changed, 1 insertion(+)

# 查看历史提交记录
ubuntu@VM-20-5-ubuntu:~/gitcode$ git log --pretty=oneline 
ca0330f09ba66638fc757525042034d82bb7ee90 (HEAD -> master) add version3
792517a8bc024eab200301baf8691de821548cbe add version2
1fbbbb826ccc07c0037f7208faeefeb577195f8c add version1
...

现在,如果我们在提交完 version 3 后,发现 version 3 编写错误,想回退到 version 2,重新基于 version 2 开始编写。

由于我们在这里希望的是将工作区的内容也回退到 version 2 版本,所以需要用到 --hard 参数,示例如下:

bash 复制代码
# 查看历史提交记录
ubuntu@VM-20-5-ubuntu:~/gitcode$ git log --pretty=oneline 
ca0330f09ba66638fc757525042034d82bb7ee90 (HEAD -> master) add version3
792517a8bc024eab200301baf8691de821548cbe add version2
1fbbbb826ccc07c0037f7208faeefeb577195f8c add version1
...
# 输入 commit id 指定回退到 version2
ubuntu@VM-20-5-ubuntu:~/gitcode$ git reset --hard 792517a8bc024eab200301baf8691de821548cbe
HEAD is now at 792517a add version2
ubuntu@VM-20-5-ubuntu:~/gitcode$ cat ReadMe 
hello git
hello world
hello version1
hello version2

我们发现:此时 ReadMe 文件的内容,已经回退到 version 2 了!当前我们再次用 git log 查看一下提交日志,发现 HEAD 指向了 version2,如下所示:

bash 复制代码
ubuntu@VM-20-5-ubuntu:~/gitcode$ git log --pretty=oneline 
792517a8bc024eab200301baf8691de821548cbe (HEAD -> master) add version2
1fbbbb826ccc07c0037f7208faeefeb577195f8c add version1
...

到这里一般的回退功能就演示完了,但是现在我后悔了,想再回到 version 3 怎么办?我们可以继续使用 git reset 命令,回退到 version 3 版本,但我们必须要拿到 version 3commit id 才能指定回退的版本。

但我们看到了 git log 并不能打印出 version 3commit id ,我们该如何找回呢?

bash 复制代码
# 显示本地每次提交的信息
git reflog
bash 复制代码
# 实机演示
ubuntu@VM-20-5-ubuntu:~/gitcode$ git reflog 
792517a (HEAD -> master) HEAD@{0}: reset: moving to 792517a8bc024eab200301baf8691de821548cbe
ca0330f HEAD@{1}: commit: add version3		# version 3 的记录
792517a (HEAD -> master) HEAD@{2}: commit: add version2
1fbbbb8 HEAD@{3}: commit: add version1
1c25401 HEAD@{4}: commit: modify ReadMe file
e094860 HEAD@{5}: commit: add file5
6856fbd HEAD@{6}: commit: add file
63ffac4 HEAD@{7}: commit: add 3 files
daca7d4 HEAD@{8}: commit (initial): add first file

这样,你就可以很方便的找到你的所有操作记录了,但 ca0330f 这个是啥东西?其实这就是 version 3commit id 的一部分。

没错,Git 版本回退的时候,也可以使用部分 commit id 来代表目标版本。示例如下:

bash 复制代码
# 回退到 version 3
ubuntu@VM-20-5-ubuntu:~/gitcode$ git reset --hard ca0330f
HEAD is now at ca0330f add version3

# 查看工作区
ubuntu@VM-20-5-ubuntu:~/gitcode$ cat ReadMe 
hello git
hello world
hello version1
hello version2
hello version3

# 查看 log
ubuntu@VM-20-5-ubuntu:~/gitcode$ git log --pretty=oneline 
ca0330f09ba66638fc757525042034d82bb7ee90 (HEAD -> master) add version3
792517a8bc024eab200301baf8691de821548cbe add version2
1fbbbb826ccc07c0037f7208faeefeb577195f8c add version1
1c25401534e7b402fc77529a1014647d2268447e modify ReadMe file
e094860cb7f95c34041e3520d17ee393965740c1 add file5
6856fbd20a828ed8c32b4f03982c8b1947c8b465 add file
63ffac4330249843d4b68f6a5f59384ba09a0358 add 3 files
daca7d41906c112f4b11c148a43cd2a0c23a4871 add first file

在长时间开发中,提交次数过多 / 本地信息丢失,可能使用 git reflog 也无法找到想要的 commit id 了,所以我们在版本回退时一定要慎重,后悔药也不是万能的!

值得一提的是:Git 的版本回退速度非常快,因为 Git 在内部有个指向当前分支(此处是 master)的 HEAD 指针,refs/heads/master 文件里保存当前 master 分支的最新 commit id 。当我们回退版本的时候,Git 仅仅是给 refs/heads/master 中存储一个特定的 version,可以简单理解成如下示意图:

6. 撤销修改

如果我们在工作区写了很长时间代码,越写越难受,感觉自己写的代码太垃圾了,想恢复到上一个版本,如何操作?

6.1 情况一:对于工作区的代码,还没有 add

你当然可以直接手动删除代码,这个谁都会,但是在代码量很多的情况下,这种方法还适用吗?

Git 其实为我们提供了更好的方式:

bash 复制代码
# 对工作区代码撤销
git checkout -- [file]

我们可以使用这个命令让工作区的文件回到最近一次 addcommit 时的状态。要注意 git checkout -- [file] 命令中的 -- 很重要,切记不要省略,一旦省略,该命令就变为其他意思了

bash 复制代码
# 实机演示

# 向 ReadMe 中新增一行代码
ubuntu@VM-20-5-ubuntu:~/gitcode$ vim ReadMe 
ubuntu@VM-20-5-ubuntu:~/gitcode$ cat ReadMe 
hello git
hello world
hello version1
hello version2
hello version3
This piece of code is like shit		# 新增

# 恢复到上一次 add 或 commit
ubuntu@VM-20-5-ubuntu:~/gitcode$ git checkout -- ReadMe
ubuntu@VM-20-5-ubuntu:~/gitcode$ cat ReadMe 
hello git
hello world
hello version1
hello version2
hello version3

6.2 情况二:已经 add,但没有 commit

add 后,代码保存到了暂存区,如何撤销?

bash 复制代码
# 向 ReadMe 中新增一行代码
ubuntu@VM-20-5-ubuntu:~/gitcode$ vim ReadMe 
ubuntu@VM-20-5-ubuntu:~/gitcode$ cat ReadMe 
hello git
hello world
hello version1
hello version2
hello version3
This piece of code is like shit		# 新增

# add 存入暂存区
ubuntu@VM-20-5-ubuntu:~/gitcode$ git add ReadMe 
ubuntu@VM-20-5-ubuntu:~/gitcode$ git status 
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	modified:   ReadMe

回忆一下上面学过的 git reset 回退命令,该命令如果使用 --mixed 参数,可以将暂存区的内容退回为指定的版本内容,但工作区文件保持不变。我们可以用这个命令回退暂存区的内容:

bash 复制代码
# --mixed 是默认参数,使用时可以省略
ubuntu@VM-20-5-ubuntu:~/gitcode$ git reset HEAD ReadMe
Unstaged changes after reset:
M	ReadMe

# 使用 git status 查看状态,发现暂存区是干净的,工作区有修改
ubuntu@VM-20-5-ubuntu:~/gitcode$ 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:   ReadMe

no changes added to commit (use "git add" and/or "git commit -a")

现在可以使用刚才 情况一 中的 git checkout -- [file] 撤销命令,把工作区的内容撤销:

bash 复制代码
ubuntu@VM-20-5-ubuntu:~/gitcode$ git checkout -- ReadMe
ubuntu@VM-20-5-ubuntu:~/gitcode$ git status 
On branch master
nothing to commit, working tree clean	# 工作区是干净的
ubuntu@VM-20-5-ubuntu:~/gitcode$ cat ReadMe 
hello git
hello world
hello version1
hello version2
hello version3	# 新增的代码没有了

6.3 情况三:已经 add 了,并且也 commit 了

不要担心,我们可以使用 git reset --hard HEAD^ 命令回退到上一个版本!

不过,这是有条件的,即:我们还没有把本地仓库的代码推送到远程仓库(gitee / github),因为撤销功能本质就是为了防止不好的代码被推送到远端。

bash 复制代码
# 实机演示

# 向 ReadMe 中新增一行代码
ubuntu@VM-20-5-ubuntu:~/gitcode$ vim ReadMe 
ubuntu@VM-20-5-ubuntu:~/gitcode$ cat ReadMe 
hello git
hello world
hello version1
hello version2
hello version3
This piece of code is like shit		# 新增

# 提交
ubuntu@VM-20-5-ubuntu:~/gitcode$ git add ReadMe 
ubuntu@VM-20-5-ubuntu:~/gitcode$ git commit -m "test quash"
[master 15e3520] test quash
 1 file changed, 1 insertion(+)

# 回退
ubuntu@VM-20-5-ubuntu:~/gitcode$ git reset --hard HEAD^
HEAD is now at ca0330f add version3
ubuntu@VM-20-5-ubuntu:~/gitcode$ cat ReadMe 
hello git
hello world
hello version1
hello version2
hello version3

总结:

工作区 暂存区 版本库 解决方法
最新版本 上一版本 | 其他版本 git check -- [file] 撤销工作区到上一版本
最新版本 最新版本 上一版本 | 其他版本 `git reset [--mixed
最新版本 最新版本 最新版本 | 上一版本 | 其他版本 git reset [--hard] [HEAD] 回退版本库、暂存区和工作区

7. 删除文件

在 Git 中,删除一个文件不仅仅是把工作区的文件删除,还要把版本库中对应的文件也删除,否则会造成工作区与版本库不匹配的问题!

我们删除之前提交的 file5 试试:

bash 复制代码
# 实机演示

# rm 删除工作区的 file5
ubuntu@VM-20-5-ubuntu:~/gitcode$ ls
file1  file2  file3  file4  file5  ReadMe
ubuntu@VM-20-5-ubuntu:~/gitcode$ rm file5

# 查看状态
ubuntu@VM-20-5-ubuntu:~/gitcode$ git status 
On branch master
Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
	deleted:    file5

no changes added to commit (use "git add" and/or "git commit -a")
# git status 提示我们工作区被修改了(删除也算修改)
# 我们需要把本次修改提交到版本库

# 提交
ubuntu@VM-20-5-ubuntu:~/gitcode$ git add file5
ubuntu@VM-20-5-ubuntu:~/gitcode$ git commit -m "delete file5"
[master 6fdee7a] delete file5
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 file5

# 再次查看
ubuntu@VM-20-5-ubuntu:~/gitcode$ git status
On branch master
nothing to commit, working tree clean
ubuntu@VM-20-5-ubuntu:~/gitcode$ ls
file1  file2  file3  file4  ReadMe
# 到这里才算是删干净了

除了上面的基本方法以外,Git 还提供了一个专用的删除命令:

bash 复制代码
# 删除文件
git rm [file]

使用此命令可以让我们少 add 一次,相当于直接把工作区的删除操作同步到了暂存区,我们删除 file4 试试:

bash 复制代码
ubuntu@VM-20-5-ubuntu:~/gitcode$ ls
file1  file2  file3  file4  ReadMe

# 使用 git rm 命令删除 file4
ubuntu@VM-20-5-ubuntu:~/gitcode$ git rm file4
rm 'file4'
ubuntu@VM-20-5-ubuntu:~/gitcode$ git status 
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	deleted:    file4

ubuntu@VM-20-5-ubuntu:~/gitcode$ git commit -m "delete file4"
[master 0c91580] delete file4
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 file4

# 删除完成后查看
ubuntu@VM-20-5-ubuntu:~/gitcode$ git status 
On branch master
nothing to commit, working tree clean
ubuntu@VM-20-5-ubuntu:~/gitcode$ ls
file1  file2  file3  ReadMe

现在,我们就掌握了两种在 Git 中删除文件的方式。

8. 本文命令总结

为了方便查阅,我按照本文的先后顺序,在这里列出我们学习到的 Git 新命令:

bash 复制代码
1. 创建本地仓库
git init

2. 配置 git
git config [--global] user.name "your name"
git config [--global] user.email "email@example.com"

3. 查看配置
git config -l

4. 删除配置
git config [--global] --unset user.name "your name"
git config [--global] --unset user.email "email@example.com"

5. 添加文件
# 添加一个或多个文件到暂存区:
git add [file1] [file2] ...
# 添加指定目录到暂存区,包括子目录:
git add [dir]
# 添加当前目录下的所有文件改动到暂存区:
git add .

6. 提交文件
# 提交暂存区全部内容到本地仓库中:
git commit -m "message"
# 提交暂存区的指定文件到仓库区:
git commit [file1] [file2] ... -m "message"

7. 查看提交记录
# 查看历史提交记录
git log
# 查看美观且简略的历史提交记录
git log --pretty=oneline

8. 查看版本库对象的内容
git cat-file [选项] [要查看的内容]

9. 查看仓库状态
git status 

10. 查看文件间差异
# 查看暂存区和工作区文件的差异
git diff [file]
# 查看版本库和工作区文件的差异
git diff HEAD -- [file]

11. 版本回退
git reset [--soft | --mixed | --hard] [HEAD]

12. 显示本地每次提交的信息
git reflog

13. 撤销工作区操作回上个版本
git checkout -- [file]

14. 删除文件
git rm [file]

END
上一篇: 【Git】安装 Git

相关推荐
lllsure3 小时前
Linux 实用指令
linux·物联网
努力的小T3 小时前
使用 Docker 部署 Apache Spark 集群教程
linux·运维·服务器·docker·容器·spark·云计算
Nerd Nirvana3 小时前
OpenSSL crt & key (生成一套用于TLS双向认证的证书密钥)
linux·ssl·shell·认证·加密·tls·oepnssl
letisgo54 小时前
记录一次部署PC端网址全过程
linux·阿里云·服务器运维
大溪地C4 小时前
Git 合并冲突解决与状态分析笔记
笔记·git
CsbLanca4 小时前
Git配置个人和公司的提交信息,通过‘目录配置‘
git
娶个名字趴4 小时前
Git企业开发
git
猫猫的小茶馆4 小时前
【网络编程】UDP协议
linux·服务器·网络·网络协议·ubuntu·udp
尚墨11114 小时前
linux 安装启动zookeeper全过程及遇到的坑
linux·zookeeper
鱼嘻5 小时前
Linux自学day23-进程和线程
linux·服务器·c语言·进程和线程