likes
comments
collection
share

Git使用说明书 之 仓库结构

作者站长头像
站长
· 阅读数 56

Git 与 Github

Git 是一种版本控制的工具,Github 狭义上讲就是 Git 的远程仓库。

版本管理:就是管理文件及文件内容变化的历史记录,例如记录添加或更改代码的过程,回滚到特定阶段,恢复误删除的文件等。

远程仓库:是指存储在网络上的代码仓库,除 Github 外,在国内也有一个知名的 Git 的远程仓库,那就是 码云

Linux 的创始人 Linus Torvalds 为了更好的管理Linux内核代码的版本,在2005年开发了Git的原型程序。

说起版本控制工具,绕不开的就是 SVN(Subversion):

⨳ SVM 是集中型的,只有一个远程仓库,而且远程仓库不透明,一般部署在公司内部服务器上,只有授权的开发者可以从远程仓库上拉取代码,然后在本地进行修改。

SVN的每一次提交,都会在远程仓库服务器的 db/revsdb/revprops目录下各创建一个以顺序数字编号(全局版本号)命名的文件。其中,db/revs 目录下的文件记录了与上一个提交之间的差异(字母A表示新增,M表示修改,D表示删除)。在 db/revprops 目录下的文件则保存着提交日志、作者、提交时间等信息。

⨳ Git 是分布式的,每个开发者都可以在 Github 上创建独属于自己的远程仓库,而且开发者可以选择公开自己的仓库,让所有人都能看到,而且当从远程仓库上拉取代码到自己的本地仓库后,也可以在本地进行版本控制操作。

简单来说,Git 既有可以进行版本控制的本地仓库,又有独属于自己的远程仓库,又可以分享自己的远程仓库。

GitHub 的创始人之一 Chris Wanstrath 希望能有一个Git仓库的托管服务让自己与朋友轻松分享代码,而这便成为了GitHub 诞生的契机。

闲话少说,直接当 Git官网上下载安装 git程序就可以使用了:

C:\Users\Cango King>git --version
git version 2.44.0.windows.1

本地仓库初始化

git config 设置你是谁

git config 是 Git 中用于配置各种选项的命令。通过 git config 命令,你可以设置和获取 Git 的配置信息,包括用户信息、仓库信息、行为配置等。

设置用户信息

使用 Git 前,要设置当前用户信息,这样当你提交的时候,Git会知道是谁提交了代码。

git config --global user.name "Your Name"
git config --global user.email "your_email@example.com"

查看配置信息

git config --list

配置git命令别名

git config --global alias.<alias-name> <git-command>

// 如:输入 git st 相当于 git status 
git config --global alias.st status

通过 git config 还可以设置一些行为选项,如换行符的处理、文件名大小写敏感性等。作为急速入门的使用说明书就是不赘述了。

Git 一共有三个配置文件:版本库级别的配置文件(工作目录下)、全局配置文件(用户主目录下)和系统级配置文件(/etc目录下)。

git config 影响的是版本库级别的配置文件;

git config --global 影响的是全局配置文件;

git config --system 影响的是系统配置文件;

git init 创建一个新的本地仓库

当你执行 git init 命令后,Git 会在当前目录下创建一个新的 Git 仓库,并为该仓库生成一个 .git 隐藏目录。

如下我在空文件夹 gitdemo 下执行 git init

Git使用说明书 之 仓库结构

⨳ 这个隐藏目录 .git 就是所谓的版本库(Repository),其内包含了该本地仓库所有版本控制信息。

⨳ 这个文件夹 gitdemo 就是工作区(Working Directory),我们最开始写的代码就在工作区。

整体目录结构如下:

gitdemo
|─.git
    |— description // 文件。包含了对仓库的描述信息
    |— config      // 文件。包含了 Git 仓库的配置信息,例如用户名、邮箱、别名等
    |— HEAD        // 文件。指示当前所在的分支,包含了指向当前分支的引用
    |— hooks       // 文件夹。包含了各种 Git 钩子脚本的样本文件
    |— info        // 文件夹。包含一些仓库的额外信息,比如其内的 `exclude` 文件,用于指定不希望被纳入版本控制的文件或目录
    |— refs        // 文件夹。存储了分支和标签的引用,`heads` 子文件夹存储分支引用,`tags` 子文件夹存储标签引用。
    |— objects     // 文件夹。存储了所有的 Git 对象,包括文件内容、目录结构等。
|— 工作区

在 .git 中还有一个文件 index,被称为暂存区(Staging Area),也被称为索引(Index),这是提交之前的一个临时区域,会在第一次执行向暂存区提交数据时自动生成。

文件 .git/index 实际上就是一个包含文件索引的目录树,这个目录树,记录了文件名和文件的状态信息(时间戳和文件长度等)。

文件的内容保存在Git对象库 .git/objects 目录中,文件索引建立了工作区文件和对象库中对象实体之间的对应。

git status 查看当前工作区状态

git status 是 Git 中用于查看当前工作区状态的命令,它会显示有关暂存区和工作区的信息,包括已修改的文件、已暂存的文件、未跟踪的文件等等。

工作区 的文件或目录,与 暂存区版本库 的版本对比可分为以下几种状态:

未追踪(Untracked files):当前文件只存在于工作区,不在暂存区,也不在版本库中,新建的文件或目录就在这个状态,Git并不能追踪它的变化。

已修改未暂存(Changes not staged for commit):当前文件的工作区和暂存区的版本不一致,这是提示要将工作区的文件提交到暂存区。

已暂存待提交(Changes to be committed):当前文件在暂存区和版本库的版本不一致,这是提示要将暂存区的文件提交到版本库。

没有要提交的东西(Nothing to commit):当前文件在工作区、暂存区和版本库的版本完全一致,啥事都不用做。

通过 git status 命令可以查看工作区某些文件的状态,下面介绍一下将文件版本在工作区、暂存区和版本库之间调整的命令,并通过 git status 查看该文件的状态。

git add 工作区到暂存区

git add 命令可以将工作区的文件添加到暂存区,准备提交到版本库中。基本用法如下:

git add <file>         # 将指定文件添加到暂存区
git add .              # 将当前目录下,所有修改过的文件添加到暂存区
git add --all          # 将所有修改过的文件添加到暂存区,包括新文件和已删除文件
git add -u             # 将所有修改过的文件添加到暂存区,但不包括新文件

▪ 新建一个空文件

Git使用说明书 之 仓库结构

▪ 查看该文件的状态

PS D:\Agit\gitdemo> git status HelloGit.txt
On branch master

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        HelloGit.txt

nothing added to commit but untracked files present (use "git add" to track)

On branch master 说明当前所在的分支是 master 分支,分支的概念下面再聊,现在先搞明白工作区、暂存区与版本库到底是个什么东西。

可以看到该文件是 Untracked files 不能被 Git 追踪变化,而且Git很贴心,提示可以使用 "git add <file>..." 命令将其添加的将被提交(what will be committed)里面,what will be committed 就是暂存区,是下一次要提交文件的目录树。

▪ 将文件添加到暂存区

PS D:\Agit\gitdemo> git add HelloGit.txt
PS D:\Agit\gitdemo> git status HelloGit.txt
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   HelloGit.txt

执行 git add 命令后再看一下文件状态,发现变成了已暂存待提交(Changes to be committed),而且贴心的给出了撤销命令 "git rm --cached <file>...")(从暂存区中删除某些文件)。

git add 命令并不是将整个文件放到暂存区,暂存区只是个 index 文件,只是个文件索引目录树,该目录树只会记录文件名和文件的状态信息,所以通过某一索引可以定位工作区某一个文件。

当对工作区修改(或新增)的文件执行 git add 命令时,会将该文件的文件名和文件的状态信息更新到 index 文件中的索引目录树中。

而真实的文件内容,则会创建一个Git对象,存在于对象库.git/objects 中。与此同时,该文件的对象ID 记录在暂存区 该文件对应的文件索引中,这就建立了工作区的文件和 .git/objects 中Git对象之间的对应。

根据工作区的文件创建 Git对象,就实现了工作区一份文件,暂存区一份文件。

Git使用说明书 之 仓库结构

如上图,当执行完 git add 后,对象库中出现一个文件,目录+文件名为40位的哈希值(前2位为目录名,后38位为文件名),这个 40 位的 Hash值就是对象ID,当执行 git add 时,这个ID 也会保存到 index 文件中,从而建立工作区与对象库的映射。

git commit 暂存区到版本库

暂存区是一个介于工作区和版本库的中间状态,当执行 commit 提交时,实际上是将暂存区的内容提交到版本库中。基本用法如下:

▪ 普通提交

直接运行 git commit 命令来提交你所做的更改。这将会打开默认文本编辑器以编辑提交消息,然后保存并关闭编辑器后,提交就完成了。

git commit

▪ 使用 -m 选项提交

如果你只是做了简单的修改,并且不想打开编辑器来编辑提交消息,你可以使用 -m 选项直接在命令行中指定提交消息。

git commit -m "Commit message"

▪ 提交指定文件

你也可以只提交暂存区(索引)中特定的文件,而不是所有的更改。这可以通过将文件路径包含在命令中来实现。

git commit <file1> <file2> ...

接下来使用 commit 命令将 HelloGit.txt 提交到版本库了:

PS D:\Agit\gitdemo> git commit -m 'First Commit'
[master (root-commit) e5294e2] First Commit
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 HelloGit.txt

commit 提交后的Git返回的信息挺丰富的,下面可以分析一下:

master: 表示当前所在的分支是主分支(通常是默认分支)。

(root-commit): 表示这是一个根提交,即这是该仓库中的第一个提交。

e5294e2: 是这个提交的哈希值,每次提交都会生成唯一的哈希值。

1 file changed: 表示有一个文件被修改了。

0 insertions(+): 表示没有插入任何新的内容(行)到文件中。

0 deletions(-): 表示没有删除任何内容(行)。

create mode 100644 HelloGit.txt: 表示一个新文件 HelloGit.txt 被创建,并且文件的模式为 100644,表示这是一个普通文件。

通过Git返回的信息大概可以看出一个新文件被提交了,master 分支下一篇再聊,那提交的哈希值又是什么?和Git对象ID有关吗?

到对象库中发现,Git 又同时生成了两个对象:

.git\objects
|─ 57
    |— 40beda4765d12a6c871f50ad65717a0f52ab5a 
|— e5
    |— 294e29d0edbbf572048ff559b03eb4e9baf5cf
|— e6    
    |— 9de29bb2d1d6434b8b29ae775ad8c2e48c5391

这几个Git对象描述如下:

HelloGit.txt 的对应的 Git 对象是 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 ,类型是 blob ,最开始由暂存区(index)的目录树管理。

⨳ 当暂存区的代码提交时,会将暂存区目录树创建成 Git 对象,类型是 tree,即 5740beda4765d12a6c871f50ad65717a0f52ab5a

⨳ 当暂存区的代码提交到 master 分支时, 会创建本次提交的唯一标识对象,类型是 commit,即 e5294e29d0edbbf572048ff559b03eb4e9baf5cf

这三个文件都是 Git 对象,只是类型不一样,它们之间的关系是,commit 指向 tree,表示本次提交的目录树是啥,tree 指向 blob,表示该目录树管理的文件是啥。

git commit 的在官方文档上写的是向版本库中记录变化(Record changes to the repository),根据上述的分析,可以理解提交,就是将暂存区的目录树会写到对象库中,然后 master 指向这个目录树。

这样的话,master分支管理一份文件索引目录树,暂存区(index)管理一份目录树。这样再执行 git add 修改暂存区的目录树时,也不会影响 master 分支。

还有一个问题,commit对象的ID是e5294e29d0edbbf572048ff559b03eb4e9baf5cf,但为啥Git再执行完 commit 后会返回 e5294e2 呢? 这是因为Git对象的哈希值太长了,如果使用前几位就能找到唯一一个Git对象,就不需要写这么长。

版本比较

git diff 命令用于比较工作区中的文件、暂存区中的文件的版本库中文件之间的差异。

基本用法如下:

// 比较工作区和暂存区之间的差异:
git diff

// 比较暂存区和版本库之间的差异:
git diff --cached

// 比较工作区和版本库之间的差异:
git diff HEAD

// 比较指定文件在工作区和版本库之间的差异:
git diff <file>

// 比较指定文件在暂存区和版本库之间的差异:
git diff --cached <file>
 

// 比较版本库两个提交之间的差异:
git diff <commit1> <commit2> // 其中 `<commit1>` 和 `<commit2>` 是提交的哈希值或者分支名

通过差异大致就能看出各个区中文件的内容,下面就使用 HelloGit.txt 文件看一下文件的差异,现在三个区文件的都一样,先往文件中写入一些内容,添加到暂存区。

PS D:\Agit\gitdemo> cat HelloGit.txt
11111
PS D:\Agit\gitdemo> git add HelloGit.txt
PS D:\Agit\gitdemo>

最新的 HelloGit.txt 文件提交到暂存区时,会生成一个新的Blob对象,index管理的目录树指向该 Blob 对象。

.git\objects
|─ 56
    |— b6510f1d6b862ca30ce2e7c05b48760ba28fd7 // 暂存区管理的 HelloGit.txt 的 Git 对象
|─ 57
    |— 40beda4765d12a6c871f50ad65717a0f52ab5a // 暂存区目录树
|— e5
    |— 294e29d0edbbf572048ff559b03eb4e9baf5cf // master 提交版本
|— e6    
    |— 9de29bb2d1d6434b8b29ae775ad8c2e48c5391 // master 分支管理的 HelloGit.txt 的 Git 对象

再往 HelloGit.txt 中写入一些内容,不再提交到暂存区,使得该文件在三个区中的版本都不一致。

PS D:\Agit\gitdemo> cat .\HelloGit.txt
11111
22222

目前 HelloGit.txt 存在两个不同的Blob对象和一个工作区原始文件:

master 分支管理的目录树指向的 Blob 内容为:空;

⨳ 暂存区 index 管理的目录树指向的 Blob 内容为:11111

⨳ 工作区原始文件内容为: 11111\n22222

git diff 比较工作区 和 暂存区

PS D:\Agit\gitdemo> git diff HelloGit.txt
diff --git a/HelloGit.txt b/HelloGit.txt
index 56b6510..699568b 100644
--- a/HelloGit.txt
+++ b/HelloGit.txt
@@ -1 +1,2 @@
-11111
\ No newline at end of file
+11111
+22222
\ No newline at end of file

a/HelloGit.txt 表示源文件,Git对象哈希值为 56b6510...,即暂存区的版本;b/HelloGit.txt 表示修改后的文件,Git对象哈希值为 99568b...(虽然还没被添加到对象库,但哈希值可以提前生成出来),即工作区的版本。

a/HelloGit.txt 表示源文件,即暂存区的版本;b/HelloGit.txt :表示修改后的文件,即工作区中的版本。

@@ -1 +1,2 @@ 表示修改的范围,-(减号)表示在 A 中出现但在 B 中被删除,+(加号)表示在 B 中出现但在 A 中缺失。

git diff --cached 比较暂存区 和 master分支

PS D:\Agit\gitdemo> git diff --cached HelloGit.txt
diff --git a/HelloGit.txt b/HelloGit.txt
index e69de29..56b6510 100644
--- a/HelloGit.txt
+++ b/HelloGit.txt
@@ -0,0 +1 @@
+11111
\ No newline at end of file

这里的源文件 a/HelloGit.txt 为 master 分支管理的版本,修改后的文件 b/HelloGit.txt 为暂存区的文件版本。

git diff HEAD 比较工作区 和 master分支

PS D:\Agit\gitdemo> git diff HEAD HelloGit.txt
diff --git a/HelloGit.txt b/HelloGit.txt
index e69de29..699568b 100644
--- a/HelloGit.txt
+++ b/HelloGit.txt
@@ -0,0 +1,2 @@
+11111
+22222
\ No newline at end of file

这里的源文件 a/HelloGit.txt 还是 master 分支管理的版本,修改后的文件 b/HelloGit.txt 为工作区的文件版本。

--cached 选项是比较暂存区和版本库中的文件不同我还能理解, cache 不就是缓存的意思嘛,那这个命令中的 HEAD 是什么意思呢?

HEAD 实际是指向 master 分支的一个“游标”,一个“指针”。我们已经知道了每次 commit,都会生成一个 提交的唯一标识对象——commit对象。每次提交生成的 commit 对象可以组成一条相互连接的链条(版本链),每次 commit 都是将 commit对象 作为节点,插入到版本链合适的位置。

因为每次提交的版本都在版本链上,所以可以找到任意一次提交 commit,进行版本回退。

HEAD 则是指向当前版本库某一次 commit 的指针,如果不手动更改 HEAD 执指向,那它默认就指向最新一次提交。所以说版本库是个很大的范围,而 HEAD 才是版本库当前 commit

不妨养成这样一个好习惯:在执行 git commit 命令之前先执行 git diff HEAD 命令,查看本次提交与上次提交之间有什么差别,等确认完毕后再进行提交。

查看命令

git ls-files 列出工作区与暂存区管理的文件

git ls-files命令可以显示包括已追踪(tracked)和未追踪(untracked)、被修改(modified)、暂存(staged)等状态的文件。这个命令可以帮助我们快速了解当前Git仓库的文件情况。

以下是 git ls-files 命令的基本用法和含义:

git ls-files [<options>] [<file>...]

<file>: 可选参数,表示要列出的文件路径或文件名。如果提供了文件名,则只会列出匹配该文件名的文件。

常用的 <options> 包括:

-c--cached:显示已添加到暂存区的文件,即被追踪的文件。

-s 或 --stage: 显示暂存区文件的模式位、对象ID和阶段编号。

-o 或 --others: 显示未添加到暂存区的文件。

-i 或 --ignored: 显示被 Git 忽略的文件。

-m 或 --modified: 显示已修改待添加到暂存区的文件的文件。

比如我想看一下暂存区目录树管理的 HelloGit.txt 文件对应的对象ID是啥,我就可以:

PS D:\Agit\gitdemo> git ls-files -s HelloGit.txt
100644 56b6510f1d6b862ca30ce2e7c05b48760ba28fd7 0       HelloGit.txt

文件模式位为 100644,表示这是一个普通文件;对象ID为 56b6510f1d6b862ca30ce2e7c05b48760ba28fd7,前边已经讲过了,阶段编号为 0,表示这个文件在暂存区的当前状态是未冲突的。

这里提一嘴被Git忽略的文件,工作区里的文件,在执行 git add dir 时会将指定目录 dir 下的所有文件(包括子目录中的文件)添加到暂存区。

但有些文件,是不需要提交的,比如 Java 程序编译后的 class 文件。

要将这些文件忽略也很简单,只需要在工作区根目录创建一个 .gitignore 文件即可,并将其提交到版本库。.gitignore 文件内容如下:

# 忽略编译产生的文件 
bin/  
target/ 
# 忽略 IDE 自动生成的文件 
.idea/ 
*.iml 
# 忽略日志文件 
*.log 
# 忽略临时文件 *.tmp 
# 忽略编译结果 *.class

前面介绍 .git 目录时也有一个忽略文件 .git/info/exclude,这个忽略文件也可以进行配置,只是它是独享的(仅对自己本地仓库有效),而提交到版本库的 .gitignore 是共享的(当仓库共享 clone 给他人,他人也有这个文件)。

git show 查看Git对象介绍

git show 命令用于显示提交或其他对象的详细信息。如 commit 对象:

PS D:\Agit\gitdemo> git show e5294e
commit e5294e29d0edbbf572048ff559b03eb4e9baf5cf
Author: Cango King <806784622@qq.com>
Date:   Mon Apr 22 13:59:31 2024 +0800

    First Commit

diff --git a/HelloGit.txt b/HelloGit.txt
new file mode 100644
index 0000000..e69de29

git ls-tree 查看目录树管理的内容

git ls-tree 命令用于列出指定 Git 树对象的内容,这个命令对于检查特定提交或分支的文件结构和内容非常有用。

▪ 查看 HEAD 指向 commit 的目录树

PS D:\Agit\gitdemo> git ls-tree HEAD
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    HelloGit.txt

再次验证了 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 是 master 分支管理的目录树指向的 HelloGit.txt 的 Git 对象。

▪ 查看暂存区的目录树

要想看暂存区的目录树,得先将 index 变成 Git 对象,前边讲过 git commit 命令会将其变成Git对象,这里再介绍一个命令:git write-tree 。这个命令可以将当前暂存区的状态写入一个树对象(tree object)。

PS D:\Agit\gitdemo> git write-tree
755b1d57113d92534e749e8aa3ea74b1c6119d65

PS D:\Agit\gitdemo> git ls-tree 755b1d57113d92534e749e8aa3ea74b1c6119d65
100644 blob 56b6510f1d6b862ca30ce2e7c05b48760ba28fd7    HelloGit.txt

再次验证了 56b6510f1d6b862ca30ce2e7c05b48760ba28fd7 是暂存区 index 目录树指向的 HelloGit.txt 的 Git 对象。

git cat-file 查看Git对象的内容

git cat-file 用于检查 Git 对象(objects)的内容。

常用选项如下:

-t:显示由Git对象的类型;

-p:Pretty-print(漂亮地打印)Git对象的内容。

下面试一下这个命令:

▪ 查看 HEAD 指向 commit

PS D:\Agit\gitdemo> git cat-file -t HEAD
commit

PS D:\Agit\gitdemo> git cat-file -p HEAD
tree 5740beda4765d12a6c871f50ad65717a0f52ab5a
author XXXX
committer XXXX

▪ 查看 index 管理 的目录树

PS D:\Agit\gitdemo> git write-tree
755b1d57113d92534e749e8aa3ea74b1c6119d65

PS D:\Agit\gitdemo> git cat-file -p 755b  // 暂存区目录树
100644 blob 56b6510f1d6b862ca30ce2e7c05b48760ba28fd7    HelloGit.txt

▪ 查看文档对象

PS D:\Agit\gitdemo> git cat-file -t 56b6510 // 暂存区管理的 HelloWorld.txt 
blob

PS D:\Agit\gitdemo> git cat-file -p 56b6510 
11111

这个命令对于 Git 内部的操作和调试非常有用,但在日常使用中可能不太常见。

Git命令如果要使用对象ID哈希值,不必把40位的哈希值写全,只采用开头的部分(4位以上),只要不与现有的其他哈希值冲突即可。

撤销操作

git reset 重置

git reset 命令用于将当前分支的 HEAD 指针移动到指定的提交。关于分支与 HEAD 将在下一章详细讲解,这里先简单介绍一下。

对于当前 master 分支来说,执行 git reset HEAD 就是将 HEAD 指针移动到 HEAD 指向处,也就是说 HEAD 指向不变。

git reset 命令处理移动 HEAD 指针外,不同选项会影响不同的区域:

--soft:只移动 HEAD 指针,保留暂存区(index)和工作区中的文件不变;

--mixed(默认):移动 HEAD 指针外,重置暂存区域,但不改变工作区中的文件;

--hard:移动 HEAD 指针外,重置暂存区域,重置工作区;

这里的重置(reset)意思是将 暂存区工作区 恢复到与 HEAD 指向 提交 一致的状态。

那使用 git reset HEAD 命令就可以不移动 HEAD 指向提交,但让提交版本的目录树覆盖暂存区中的目录树。

PS D:\Agit\gitdemo> git reset HEAD  // reset 暂存区
Unstaged changes after reset:
M       HelloGit.txt

PS D:\Agit\gitdemo> git diff --cached  // 比较暂存区与版本库的区别,啥也没输出

PS D:\Agit\gitdemo> git write-tree     // 将暂存区目录树写成 GIT 对象,发现变成了commit 管理的 tree ID
5740beda4765d12a6c871f50ad65717a0f52ab5a

PS D:\Agit\gitdemo> git cat-file -p 5740 
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    HelloGit.txt
PS D:\Agit\gitdemo> git cat-file -p e69d
PS D:\Agit\gitdemo>

如上,执行完 git reset HEAD 命令后,暂存区与版本库中的文件再无差别,原因就在于二者管理的 tree 对象是一个。

git restore 恢复

reset 命令恢复相应工作目录仅仅是移动HEAD 节点的附加功能,而这里的 restore 则是专门干恢复这件事的。

restore 命令使用格式如下:

git restore [–source=<树>] [–staged] [–worktree] <file_name>

旨在从源(–source)中恢复暂存区(–staged)或工作区(-worktree)工作树中的一些内容(<file_name>)。

–source:恢复源,如果是恢复暂存区(–staged)默认是 HEAD。如果是恢复工作区(-worktree)默认是 index。

–staged:仅恢复暂存区;

-worktree:(默认选项)恢复工作区;

现在 HEAD 指向 commit 和 暂存区中的 HelloWorld.txt 都是空文件。

现在用暂存区作为源恢复工作区的 HelloWorld.txt

PS D:\Agit\gitdemo> git restore HelloGit.txt

PS D:\Agit\gitdemo> git diff HelloGit.txt
PS D:\Agit\gitdemo> // 无任何差别

用记事本打开 HelloWorld.txt 文件,发现也是空文件了。

git checkout 检出

checkout 命令非常丰富,可以创建/切换分支、移动 HEAD 指针,文件覆盖...

这里只介绍有关 checkout 检出的功能,毕竟其他功能都有专门干这个事的命令:

git checkout [HEAD]:汇总显示工作区、暂存区与HEAD的差异;

git checkout [--] filename:用暂存区中 filename 文件来覆盖工作区中的 filename 文件。-- 为分隔符,可写可不写;

git checkout [--] .:用暂存区的所有文件直接覆盖本地文件;

git checkout branch -- filename: 维持HEAD 的指向不变。用 branch 所指向的提交中的 filename 替换暂存区和工作区中相应的文件。

看起来 checkout 可以从暂存区或别的分支检出文件,但现在还没介绍分支,这里就试一下用暂存区检出文件。

HelloWorld.txt 文件现在在工作区、暂存区和 HEAD 指向都是空文件:

// 空文件
PS D:\Agit\gitdemo> cat HelloGit.txt
PS D:\Agit\gitdemo>

// 往空文件中写入一行
PS D:\Agit\gitdemo> cat HelloGit.txt
cccccc

// 汇总显示工作区、暂存区与HEAD的差异
PS D:\Agit\gitdemo> git checkout
M       HelloGit.txt

// 从暂存区检出文件
PS D:\Agit\gitdemo> git checkout -- .\HelloGit.txt

// 工作区文件被覆盖
PS D:\Agit\gitdemo> cat .\HelloGit.txt
PS D:\Agit\gitdemo>

清理命令

git rm 移除

因为Git本地仓库有工作区、暂存区、版本库这些区域,所以文件也不仅仅在工作区存在。

若手动右键点删除,remove 工作区的文件,那对暂存区和版本库的文件是没有任何影响的。

当然可以在工作区删除文件后,使用 git add -u 将对该文件的删除记录添加到暂存区,但 Git 提供了更方便的命令。

git rm 基本用法如下

git rm <file> :从工作区和暂存区移除文件

git rm --cached <file> 仅从暂存区中删除某一个文件,不影响工作区。

git rm --cached <file> 命令用于将文件从 Git 的暂存区(Index)中移除,但保留在工作目录中。这意味着文件将不再被跟踪,该命令通常在你想将某些文件从版本控制中排除,但又不想删除它们的情况下使用。

下面就尝试一从暂存区中删除 HelloWorld.txt 文件:

PS D:\Agit\gitdemo> git rm --cached HelloGit.txt
rm 'HelloGit.txt'

PS D:\Agit\gitdemo> git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        deleted:    HelloGit.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        HelloGit.txt


删除文件后,使用 git status 查看仓库状态,可以看到 HelloWorld.txt 文件已经从暂存区中删除,并不再追踪了,该删除会随着 commit 提交到版本库。

git status 的返回还是很贴心的,提示使用 git restore --staged <file>... 命令可以从 HEAD 恢复暂存区中的文件,使用 git add <file>... 可以将未追踪的文件添加到暂存区。

git mv 移动

先恢复下暂存区的 HelloWorld.txt 文件。

PS D:\Agit\gitdemo> git restore --staged .\HelloGit.txt
PS D:\Agit\gitdemo> git status
On branch master
nothing to commit, working tree clean
PS D:\Agit\gitdemo>

和 Linux 命令 mv 一样,git 的 mv 命令既可以移动又可以重命名,重要的是 Git 会保持跟踪变化后的文件:

git mv <源文件/目录> <目标文件/目录>

下面就试着将 HelloGit.txt 重命名为 HiGit.txt:

PS D:\Agit\gitdemo> git mv HelloGit.txt HiGit.txt
PS D:\Agit\gitdemo> git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    HelloGit.txt -> HiGit.txt

可以看到当文件在工作区被重命名后,暂存区也跟着修改了。

那我直接在工作区重命名,不使用 git mv 操作文件会怎样呢?

PS D:\Agit\gitdemo> mv HiGit.txt HelloGit.txt
PS D:\Agit\gitdemo> git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    HelloGit.txt -> HiGit.txt

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:    HiGit.txt

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        HelloGit.txt

可以看到暂存区还是 HiGit.txt 版本,但工作区被追踪的 HiGit.txt 已经删除,重命名后的 HelloGit.txt 并没有被追踪。

git clean 清理

git clean 命令是用于从工作目录中删除未跟踪的文件或目录。

如果只想看看执行 git clean 会清理哪些文件,可以加上-n--dry-run)选项,如果是你想要清理的文件,则可以加上 -f--force)选项强制清除。

PS D:\Agit\gitdemo> git clean -n
Would remove HelloGit.txt

PS D:\Agit\gitdemo> git clean -f
Removing HelloGit.txt

PS D:\Agit\gitdemo> git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    HelloGit.txt -> HiGit.txt

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:    HiGit.txt

远程仓库

讲到这里,大家对工作区、暂存区目录树,tree对象,blob对象,commit对象应该有了大致了解。现在再介绍一下远程仓库。

git remote 远程

git remote  命令用于管理 Git 仓库的远程仓库。常用选项如下:

git remote add <remote_name> <remote_url>:添加一个新的远程仓库,指定远程仓库的名称和 URL。

git remote -v:显示所有远程仓库,并显示远程仓库的 URL。

git remote rm <remote_name>:移除指定的远程仓库。

git remote rename <old_name> <new_name>:将一个远程仓库重命名。

现在就在码云上新建一个仓库,并将本地仓库与其关联:

Git使用说明书 之 仓库结构

使用 add 选项为本地仓库添加远程仓库:

PS D:\Agit\gitdemo> git remote add origin  https://gitee.com/Cango_King/gitdemo.git

这里的 origin 是一个别名,也可以使用其他名称来表示远程仓库,但远程仓库通常被命名为 origin

使用 -v 选项查看本地仓库关联的远程仓库:

PS D:\Agit\gitdemo> git remote -v
origin  https://gitee.com/Cango_King/gitdemo.git (fetch)
origin  https://gitee.com/Cango_King/gitdemo.git (push)

上述显示了,本地仓库可以 fetchpush 的远程仓库的 URL。哪 fetchpush 是啥呢?

git push 推送

git push 命令用于将本地仓库中的代码推送到远程仓库:

git push origin <branch_name>

因为本地只有一个 master 分支,所以这里直接将 master 分支推送到远程仓库:

PS D:\Agit\gitdemo> git branch
* master

PS D:\Agit\gitdemo> git push origin master
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 215 bytes | 11.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Powered by GITEE.COM [GNK-6.4]
To https://gitee.com/Cango_King/gitdemo.git
 * [new branch]      master -> master

* [new branch] master -> master 表示本地的 master 分支被成功推送到远程的 master 分支。到码云上看看去:

Git使用说明书 之 仓库结构

发现 HelloGi.txt 已经在远程仓库了,而且这个文件是我在 8 天前提交到的本地版本库,这个信息也被远程仓库记录了,可见 git push 是将版本库的文件推送到远程仓库的。

Total 3 表示一共推送了三个Git对象,一个 HelloGit.txt 对应的 blob 对象,一个 commit 对象,一个 commit 对象管理的 tree 对象,非常合理。

git fetch 拉取

git fetch 命令用于从远程仓库中获取最新的更新,但并不自动合并到本地仓库,而是将这些更新保存在本地仓库中的一个隐藏分支中。语法如下:

git fetch [options] [repository] [refspec]

现在在码云上新建一个 README.md 文件,介绍一下该仓库是干啥的。

Git使用说明书 之 仓库结构

然后使用git fetch origin master 拉取远程仓库 originmaster 分支到本地:

PS D:\Agit\gitdemo> git fetch origin
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 1007 bytes | 1024 bytes/s, done.
From https://gitee.com/Cango_King/gitdemo
   e5294e2..4502088  master     -> origin/master

PS D:\Agit\gitdemo> git branch --all
* master
  remotes/origin/master

远程分支拉到本地后,名字变成了 origin/master,可以使用 git diff 看一下远程分支与本地 master 分支的差异:

PS D:\Agit\gitdemo> git diff HEAD origin/master
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8bdf0d2
--- /dev/null
+++ b/README.md
@@ -0,0 +1,6 @@
+# gitdemo^M
+^M
+#### 介绍^M
+git命令操作指南^M
+^M
+^M

事实上远程分支不是真正意义上的分支,是类似于里程碑一样的引用。如果针对远程分支执行检出命令,会使得头指针 HEAD 处于分离头指针状态。那什么是里程碑、什么是头指针分离,详见分支篇。

git pull 拉取与合并

执行 git pull 命令时,实际上是先运行 git fetch 拉取远程仓库的更新,然后再运行 git merge 或 git rebase 将远程分支的更改合并到本地分支。语法如下:

git pull <远程主机名> <远程分支名>:<本地分支名>

<本地分支名> 就是 HEAD 指针指向的 master 分支可以省略,下面就看一下 pull 的效果,有关分支还是放到分支篇去讲解:

PS D:\Agit\gitdemo> git  branch
* master
PS D:\Agit\gitdemo> git pull origin master
From https://gitee.com/Cango_King/gitdemo
 * branch            master     -> FETCH_HEAD
Updating e5294e2..4502088
Fast-forward
 README.md | 6 ++++++
 1 file changed, 6 insertions(+)
 create mode 100644 README.md

合并完之后,可以看到 commit 管理两个文件:

PS D:\Agit\gitdemo> git ls-tree HEAD
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    HelloGit.txt
100644 blob 8bdf0d24a1013fa546d49c48735aaf7b129c7292    README.md

git clone 克隆

clone 命令用于克隆一个仓库到一个新的目录,常用于将远程仓库克隆到本地。例如,可以使用以下命令克隆一个名为 example 的项目:

git clone https://example.com/example.git

这将在当前目录下创建一个名为 example 的文件夹,并将远程仓库的内容复制到该文件夹中。

一般情况下,我们都会使用该命名创建本地 git 仓库,并建立与远程仓库的关联。

总结

Git 仓库分为本地仓库远程仓库,本地仓库又有工作区暂存区版本库这三个部分,版本库又会因为每次 commit 提交会创建不同的版本(提交节点)。

在Git中,主要有四种类型的对象:

Blob对象:用于存储文件的内容,每个文件对应一个Blob对象。

Tree对象:用于存储目录结构和文件索引,每个目录对应一个Tree对象。

Commit对象:用于存储提交信息和指向Tree对象的指针,每个提交对应一个Commit对象。

Tag对象:用于存储标签信息和指向Commit对象的指针,每个标签对应一个Tag对象。

Commit -> Tree -> Blob

  └── Tag

现在应该只有 Tag 对象还不了解,Git 会为每个追踪的文件创建对应的 blob 对象,并由 tree 对象管理这些 blob 对象,暂存区可以看作是一个 tree,每次提交创建的 commit 对象也可以看作是持有 tree 的引用。这样就使得 Git 可以同时存在多版本。

工作区与暂存区相关命令:

git status:查看当前工作区状态

git diff:比较工作区 和 暂存区的差异

git add :将文件添加到暂存区(生成blob对象,并有暂存区的tree管理)

git restore --source index --worktree :从暂存区中恢复文件到工作区

git checkout --:从暂存区检出文件,覆盖工作区

git rm --cached:删除暂存区中的文件

git mv :移动或重命名文件或目录,并将这些更改在暂存区和工作区中生效

git clean -n:强制清除工作区中未跟踪的文件

暂存区与版本库相关命令:

git diff --cached :比较暂存区和最后一次提交之间的差异

git commit:将暂存区的内容提交到版本库中

git reset HEAD:让提交版本的目录树覆盖暂存区中的目录树

版本库与远程仓库相关命令:

git push :将本地仓库中的代码推送到远程仓库:

转载自:https://juejin.cn/post/7370955971018162186
评论
请登录