【Git原理与使用】-- 基本操作

目录

添加文件

查看objects中的文件

小结

修改文件

版本回退

回退的回退

小结

撤销修改

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

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

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

小结 

删除文件


#:首先引入一个未进行任何操作的 ./git 内部情况

[qcr@ecs-205826 project]$ tree .git
.git
├── branches
├── config
├── description
├── HEAD
├── hooks
│?? ├── applypatch-msg.sample
│?? ├── commit-msg.sample
│?? ├── post-update.sample
│?? ├── pre-applypatch.sample
│?? ├── pre-commit.sample
│?? ├── prepare-commit-msg.sample
│?? ├── pre-push.sample
│?? ├── pre-rebase.sample
│?? └── update.sample
├── info
│?? └── exclude
├── objects
│?? ├── info
│?? └── pack
└── refs
    ├── heads
    └── tags

9 directories, 13 files

【Git原理与使用】-- 基本操作_第1张图片

        我们可以找到对应的:objectsHEAD,但是没有对应的indexmaster

添加文件

        在包含 .git 的目录下新建⼀个 Test.txt  文件,我们可以使用  git add 命令可以将文件添加到暂存区:
  • 添加 或多个文件 到暂存区: git add [file1] [file2] ...
  • 添加 指定目录 到暂存区,包括子目录: git add [dir ]
  • 添加当前目录下的 所有文件 改动到暂存区: git add .
再使用  git commit 命令将暂存区内容添加到本地仓库中:
  • 提交暂存区全部内容到本地仓库中:git commit -m "message"
  • 提交暂存区的指定文件到仓库区:git commit [file1] [file2] ... -m "message"

Note: git commit 后面的 -m 选项,描述本次提交的 message(由用户自己完成),这部分内容绝对不能省略,并且要好好描述,是用来记录我们的提交细节。

【Git原理与使用】-- 基本操作_第2张图片

        可以看见 Git 的提示:一个文件改变,是新增了一行的内容。

补充:

 $ git log 

        打印出来所有时间从近到远的提交记录。

[qcr@ecs-205826 project]$ git log
commit e797df72c0669a2b81339ff4854328126d2e6c8e
Author: qcr <[email protected]>
Date:   Wed Jun 14 19:29:48 2023 +0800

    一行你好,用于学习git

        "e797df72c0669a2b81339ff4854328126d2e6c8e":commit ID,每一次提交都会有一个 commit ID,其是经过哈希计算出来的非常大的一个数字(用十六进制表示)。

        如果觉得这个内容眼花缭乱的,可以给这个命令加一个选项参数。

[qcr@ecs-205826 project]$ git log --pretty=oneline
e797df72c0669a2b81339ff4854328126d2e6c8e 一行你好,用于学习git

        现在再让我们看一看 ./git 

[qcr@ecs-205826 project]$ tree .git
.git
├── branches
├── COMMIT_EDITMSG
├── config
├── description
├── HEAD
├── hooks
│   ├── applypatch-msg.sample
│   ├── commit-msg.sample
│   ├── post-update.sample
│   ├── pre-applypatch.sample
│   ├── pre-commit.sample
│   ├── prepare-commit-msg.sample
│   ├── pre-push.sample
│   ├── pre-rebase.sample
│   └── update.sample
├── index
├── info
│   └── exclude
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── 38
│   │   └── 1feea9040943c8fa00ac3312d93ed7d97c42ff
│   ├── e6
│   │   └── 076a05b53658ebd812398523da7f38fc552aa1
│   ├── e7
│   │   └── 97df72c0669a2b81339ff4854328126d2e6c8e
│   ├── info
│   └── pack
└── refs
    ├── heads
    │   └── master
    └── tags

15 directories, 21 files

        可以发现其与之前没进行任何操作的 ./git 多了很多很多的东西。而之前未出现的 index 出现了,就是由于 git add 而出现的暂存区,而 add 的内容就是添加到这个里面的。这个时候再来看 HEAD ,其就是一个指针,指向了 master

[qcr@ecs-205826 project]$ cat .git/HEAD
ref: refs/heads/master

        通过查看我们可以发现,其确实就是指向 master 的,并且该段路径就是在 ./git 可以找到的。

【Git原理与使用】-- 基本操作_第3张图片

        然后我们打印 HEAD 内指向的 master 内容可以发现。

[qcr@ecs-205826 project]$ cat .git/refs/heads/master 
e797df72c0669a2b81339ff4854328126d2e6c8e

        这个 master 里面存放的其实就是最新的一次提交的 commit ID 。而之前又有所提到,master 里面放的都是一些索引,这些索引执指向的又其实是对象库中的对象。接下来再将我们的视角放在 objects多的东西上。

【Git原理与使用】-- 基本操作_第4张图片

        可以看到里面更新了一堆的对象,其实它们就是一些文件。

查看objects中的文件

        首先我们要将 commit ID 分为两个部分。

        下述其实还有一项 parent  ,后面跟的是commit ID 意为上一次的提交。

[qcr@ecs-205826 project]$ git cat-file -p e797df72c0669a2b81339ff4854328126d2e6c8e
tree 381feea9040943c8fa00ac3312d93ed7d97c42ff
author qcr <[email protected]> 1686742188 +0800
committer qcr <[email protected]> 1686742188 +0800

一行你好,用于学习git

        -p:pertty更漂亮的打印出来。

        其中的 tree 我们可以打印其后面的 commit ID

[qcr@ecs-205826 project]$ git cat-file -p 381feea9040943c8fa00ac3312d93ed7d97c42ff
100644 blob e6076a05b53658ebd812398523da7f38fc552aa1	test.txt

        可以发现此处出现了我们前面的 test.txt 文件,并且其对应着一个 commit ID ,也就是对应一个索引,我们可以使用 cat-file 进行查看。

[qcr@ecs-205826 project]$ git cat-file -p e6076a05b53658ebd812398523da7f38fc552aa1
你好

        可以发现正是我们在 test.txt 文件中新增的一行内容,这就是我们对于test.txt文件的一次修改,被 Git 记录下来了,所以我们每一次提交的修改都会被 Git 记录下来,就是通过对象记录下来的修改。对于objects一句话:修改的工作区内容会被写入到对象库的一个新的git对象中

小结

        git仓库 中有一个 index(暂存区),用于放add后的一些新增内容的。HEAD是一个指针,指向的是 .git/refs/heads/master ,master 里面存放的是我们最新的一次提交的 commit ID,其又是一个索引,对应着的是一个 git 对象,该对象被维护在对象库中。对应的对象也有其 commit ID 进行查看,可以发现里面就有存放我们所修改的所有内容。

Note:

        明确一个观念:Git 追踪管理的其实是修改,而不是文件

        并且这是可以在对象库中体现出来,对象库中的一个对象,里面存储的是修改的工作区中的内容。

修改文件

[qcr@ecs-205826 project]$ cat test.txt 
你好
hello

        此时,在工作区中已经对 test.txt 进行了更改,而 Git 中为我们提供了一个命令用于查看当前仓库的一个状态。

[qcr@ecs-205826 project]$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add ..." to update what will be committed)
#   (use "git checkout -- ..." to discard changes in working directory)
#
#	modified:   test.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

        用于查看我们上一次提交之后到现在是否对文件进行过修改,可以根据上述发现:Changes not staged for commit(没有将要被提交的暂存区内容),也就是说暂存区中目前是干净的没有内容。

        修改的是在工作区中修改的,修改的文件就是 test.txt git status只能查哪个文件修改了,具体修改的是哪一些内容是不知道的。Git 还提供了一个命令,用于显示目前暂存区和工作区之间的差异的。

[qcr@ecs-205826 project]$ git diff test.txt
diff --git a/test.txt b/test.txt
index e6076a0..0891bd4 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1,2 @@
 你好
+hello

【Git原理与使用】-- 基本操作_第5张图片

          git diff [file] 命令用来显示暂存区和工作区文件的差异,显示的格式正是Unix通用的diff格
式。也可以使用  git diff HEAD -- [file] 命令来查看版本库和工作区文件的区别。
[qcr@ecs-205826 project]$ git diff HEAD test.txt
diff --git a/test.txt b/test.txt
index e6076a0..0891bd4 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1,2 @@
 你好
+hello

        此时如果我们将 test.txt 文件进行上传,再进行 diff 命令操作可以发现。暂存区和工作区文件的没有了差异,于是什么都不会说明,而版本库和工作区文件的之间还是有差异的。

[qcr@ecs-205826 project]$ git add test.txt
[qcr@ecs-205826 project]$ git diff test.txt
[qcr@ecs-205826 project]$ git diff HEAD test.txt
diff --git a/test.txt b/test.txt
index e6076a0..0891bd4 100644
--- a/test.txt
+++ b/test.txt
@@ -1 +1,2 @@
 你好
+hello

        使用 status 命令操作,其提示我们需要将修改提交至本地仓库。

[qcr@ecs-205826 project]$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
#	modified:   test.txt
#

        提示了一个在暂存区中修改的文件:test.txt

[qcr@ecs-205826 project]$ git commit -m "新增一行"
[master a8760f6] 新增一行
 1 file changed, 1 insertion(+)
[qcr@ecs-205826 project]$ git diff test.txt
[qcr@ecs-205826 project]$ git diff HEAD test.txt

        工作区、暂存区、版本库全部一致,所以没任何提示。

[qcr@ecs-205826 project]$ git status
# On branch master
nothing to commit, working directory clean

        这一次我们已经提交成功,所以给我们打印的一个状态就是没有什么可以提交了,工作区是干净的。

版本回退

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

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

$ git reset [--soft | --mixed | --hard] [HEAD]
选项功能
选项 具体功能
--soft 参数对于工作区和暂存区的内容都不变,只是将版本库回退到某个指定版本
--mixed 为默认选项,使用时可以不用带该参数。该参数将暂存区的内容退回为指定提交版本内容,工作区文件保持不变。
--hard 将暂存区与工作区都退回到指定版本。切记工作区有未提交的代码时不要用这个命令,因为⼯作区会回滚,你没有提交的代码就再也找不回了,所以使用该参数前⼀定要慎重。

Note:关于 --hard 选项一定需要注意(慎用!!!),如果当前正在工作区中进行开发,一旦回退是会把正在开发的数据全部干掉,并且是不管如何操作都是找不回对应的开发数据的。

  • HEAD 说明:
    • 可直接写成 commit ID,表示指定退回的版本
    • HEAD:表示当前版本
    • HEAD^:上⼀个版本
    • HEAD^^:上上⼀个版本
    • 以此类推...
  • 可以使用 ~ 数字表示:
    • HEAD~0:表示当前版本
    • HEAD~1:上⼀个版本
    • HEAD^2:上上⼀个版本
    • 以此类推...

        现在之前的基础上再提交几个文件(用于实现效果的体现),下述 test1test2test3 是用一句 add 和一句 commit 提交的。

[qcr@ecs-205826 project]$ ls
test1  test2  test3  test.txt
[qcr@ecs-205826 project]$ git log --pretty=oneline
1bba34a04f231d8ad49f3ce5079879144cf6f4db 临时空文件
8f2b55aca176fb578cf39952f776c3da9aa50009 新增一行
e797df72c0669a2b81339ff4854328126d2e6c8e 一行你好,用于学习git

        现在我们尝试利用--hard 选项,回退到对应的提交。

[qcr@ecs-205826 project]$ git reset --hard e797df72c0669a2b81339ff4854328126d2e6c8e
HEAD is now at e797df7 一行你好,用于学习git

        通过上述可以知晓回退成功了,接下来看看当前目录的文件。

[qcr@ecs-205826 project]$ ls
test.txt

        然而发现,该目录下只剩了一个 test.txt 文件,而 test1test2test3 不存在了。原因就是,我们使用的--hard 选项,将工作区也回退了,而 test1test2test3 的提交是在回退的提交之后的,所以一旦回退到某一次提交,那么这次提交之后的所有提交都会被回退

        此时再打印 test.txt 中的内容可以发现。

[qcr@ecs-205826 project]$ cat test.txt 
你好

        回退成功,再打印 log

[qcr@ecs-205826 project]$ git log --pretty=oneline
e797df72c0669a2b81339ff4854328126d2e6c8e 一行你好,用于学习git

        发现我们回退的位子成为第一个了。

回退的回退

        对应我们上一次查的log中,就会有被回退的提交(第一次回退的时候查找的log)。

[qcr@ecs-205826 project]$ git reset --hard 1bba34a04f231d8ad49f3ce5079879144cf6f4db
HEAD is now at 1bba34a 临时空文件
[qcr@ecs-205826 project]$ ls
test1  test2  test3  test.txt
[qcr@ecs-205826 project]$ git log --pretty=oneline
1bba34a04f231d8ad49f3ce5079879144cf6f4db 临时空文件
8f2b55aca176fb578cf39952f776c3da9aa50009 新增一行
e797df72c0669a2b81339ff4854328126d2e6c8e 一行你好,用于学习git

        可以发现全部回来了。而在这里有后悔药可以吃,是因为当前终端下并没有将之前的提交清除掉。

#:如果手残的在想回退的回退时已将上一次log的信息,因为 clear 而消失,是也可以查到之前的 log 信息的,使用 reflog 命令。

[qcr@ecs-205826 project]$ git log --pretty=oneline
e797df72c0669a2b81339ff4854328126d2e6c8e 一行你好,用于学习git
[qcr@ecs-205826 project]$ git reflog
e797df7 HEAD@{0}: reset: moving to e797df72c0669a2b81339ff4854328126d2e6c8e
1bba34a HEAD@{1}: reset: moving to 1bba34a04f231d8ad49f3ce5079879144cf6f4db
e797df7 HEAD@{2}: reset: moving to e797df72c0669a2b81339ff4854328126d2e6c8e
1bba34a HEAD@{3}: commit: 临时空文件
8f2b55a HEAD@{4}: commit: 新增一行
e797df7 HEAD@{5}: reset: moving to e797df72c0669a2b81339ff4854328126d2e6c8e
a8760f6 HEAD@{6}: commit: 新增一行
e797df7 HEAD@{7}: commit (initial): 一行你好,用于学习git

        reflog 命令是用于记录本地的每一次的提交命令。

         其就是对应提交的 commit ID,这么短是因为其就是 commit ID 的一部分,也是可以使用的。

[qcr@ecs-205826 project]$ git log --pretty=oneline
e797df72c0669a2b81339ff4854328126d2e6c8e 一行你好,用于学习git
[qcr@ecs-205826 project]$ git reset --hard 1bba34a
HEAD is now at 1bba34a 临时空文件
[qcr@ecs-205826 project]$ git log --pretty=oneline
1bba34a04f231d8ad49f3ce5079879144cf6f4db 临时空文件
8f2b55aca176fb578cf39952f776c3da9aa50009 新增一行
e797df72c0669a2b81339ff4854328126d2e6c8e 一行你好,用于学习git

        这样的前提下,是因为我们能够找到 commit ID 才能吃后悔药,而在开发的过程中是会有很多的 Git 操作,是迟早会将我们的 commit ID 冲掉的。而一旦找不到 commit ID 就没有后悔药可以吃了。

小结

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

【Git原理与使用】-- 基本操作_第6张图片

        所以版本回退其实就是将 master 中的 commit ID 进行改变,也就是将指向进行改变,于是版本的回退是十分的简单的、快速的。

撤销修改

        如果我们在我们的工作区写了很长时间代码,越写越写不下去,觉得自己写的实在是垃圾,想恢复到上⼀个版本。

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

        这个时候由于我们并没有进行 add 的操作,所以文件只存在于工作区中,在暂存区和版本库中是没有的,这个时候撤销的内容是工作区

[qcr@ecs-205826 project]$ cat test.txt 
你好
hello
不喜欢这行想撤销

        这个时候选择使用编辑文件,然后直接删除这一行,这个方法肯定是没有任何的问题的。但是如果写了很多了,这个时候一行一行的去删除很不现实,并且很容易就忘记了,并且不能保证手动删除的准确性!

         Git 其实还为我们提供了更好的方式,我们可以使用 git checkout -- [file] 命令让工作区的文件回到最近⼀次 add commit 时的状态。 要注意 git checkout -- [file] 命令中的 -- 很重要,切记不要省略,⼀旦省略,该命令就变为其他意思了!

[qcr@ecs-205826 project]$ git checkout -- test.txt 
[qcr@ecs-205826 project]$ cat test.txt 
你好
hello

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

        这个时候由于我们进行了 add 的操作,但是没有进行 commit 操作,所以文件只存在于工作区与暂存区中,在版本库中是没有的,这个时候撤销的内容是工作区、暂存区

[qcr@ecs-205826 project]$ cat test.txt 
你好
hello
不喜欢这行想撤销
[qcr@ecs-205826 project]$ git add test.txt 
[qcr@ecs-205826 project]$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
#	modified:   test.txt
#
        让我们来回忆⼀下学过的 git reset 回退命令,该命令如果使用  --mixed 参数,可以将暂存区的内容退回为指定的版本内容,但工作区文件保持不变,那我们就可以回退下暂存区的内容了! (git reset 回退命令还可以用于回退到当前版本)
        同理 --hard 选项也可以,并且还比  git reset 回退命令少一次操作,此处用 git reset 回退命令。
[qcr@ecs-205826 project]$ git reset HEAD test.txt 
Unstaged changes after reset:
M	test.txt
[qcr@ecs-205826 project]$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add ..." to update what will be committed)
#   (use "git checkout -- ..." to discard changes in working directory)
#
#	modified:   test.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

        这个时候我们就将暂存区中回退了,然后就是与情况一,一样的情况。 

[qcr@ecs-205826 project]$ git checkout -- test.txt 
[qcr@ecs-205826 project]$ cat test.txt 
你好
hello

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

        这个时候由于我们进行了 add commit 的操作,所以文件存在于工作区、暂存区、版本库中,这个时候撤销的内容是工作区、暂存区、版本库

[qcr@ecs-205826 project]$ cat test.txt 
你好
hello
不喜欢这行想撤销
[qcr@ecs-205826 project]$ git add test.txt 
[qcr@ecs-205826 project]$ git commit -m "用于测试修改"
[master a2fd08f] 用于测试修改
 1 file changed, 1 insertion(+)

        不要担心,我们可以 git reset --hard HEAD^ 回退到上⼀个版本!不过,这是有条件的:就是你还没有把自己的本地版本库推送到远程仓库(commit之后对应push操作,后续再讲解)撤销的目的就是:不影响远程仓库的代码。因为我们不知道远程仓库中对应的代码是怎么样的,所以我们想要撤退的操作的前提是远程仓库中确定没有这一份代码,才能真正意义上称作为撤销。

[qcr@ecs-205826 project]$ git reset --hard HEAD^
HEAD is now at 1bba34a 临时空文件
[qcr@ecs-205826 project]$ git status
# On branch master
nothing to commit, working directory clean
[qcr@ecs-205826 project]$ cat test.txt 
你好
hello

小结 

工作区 暂存区 版本库 解决方式
新文件 旧文件 旧文件

1、手动撤销 - 不推荐,易出错

2、git checkout -- [filename]

新文件 新文件 旧文件

git reset --mixed(变为上述情况)

git reset --hard HEAD

新文件 新文件 新文件

前提条件:commit之后没有进行push

git reset --hard HEAD^

删除文件

        在 Git 中,删除也是⼀个修改操作,我们实战⼀下,如果要删除 test.txt 文件,怎么搞?

[qcr@ecs-205826 project]$ ls
test1  test2  test3  test.txt
[qcr@ecs-205826 project]$ rm test3
[qcr@ecs-205826 project]$ ls
test1  test2  test.txt

        但这样直接删除是没有用的,反而徒增烦恼,其只是将我们对应的工作区中的内容进行删除,但是对于本地仓库是并没有删除的, git status 命令会立刻告诉你哪些文件被删除了:

[qcr@ecs-205826 project]$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add/rm ..." to update what will be committed)
#   (use "git checkout -- ..." to discard changes in working directory)
#
#	deleted:    test3
#
no changes added to commit (use "git add" and/or "git commit -a")
        此时,工作区和版本库就不一致了,要删文件,目前除了要删工作区的文件,还要清除版本库的文件。
⼀般走到这里,有两种可能:
  • 确实要从版本库中删除该文件
  • 不小心删错了

        对第⼆种情况,很明显误删,需要使用 git 来进行恢复,很简单,我们刚学过:

[qcr@ecs-205826 project]$ git checkout -- test3
[qcr@ecs-205826 project]$ ls
test1  test2  test3  test.txt
        对于第⼀种情况,很明显是没有删完,我们只删除了工作区的文件。 最正确的方法是,使用  git rm 将文件从暂存区和工作区中删除,并且 commit
[qcr@ecs-205826 project]$ git rm test3
rm 'test3'
[qcr@ecs-205826 project]$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
#	deleted:    test3
#

        可以看出对比前面,其即删除了工作区里的文件,也删除了暂存区中的文件,然后就只需要我们进行 commit 就可以了。

[qcr@ecs-205826 project]$ git commit -m "删除test3"
[master dccd2a4] 删除test3
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 test3

你可能感兴趣的:(Git,git,gitee)