Skip to content

第4章 Git基本命令

4.1 Git目录

git init——初始化仓库

$ git init
Initialized empty Git repository in /Users/hirocaster/github/github-book

如果初始化成功,执行了 git init命令的目录下就会生成 .git 目录。这个 .git 目录里存储着管理当前目录内容所需的仓库数据。

这个目录是隐藏起来的。Mac可以通过**CMD+SHIFT+.**来显示隐藏文件

在 Git 中,我们将这个目录的内容称为“附属于该仓库的工作树”。文件的编辑等操作在工作树中进行,然后记录到仓库中,以此管理文件的历史快照。如果想将文件恢复到原先的状态,可以从仓库中调取之前的快照,在工作树中打开。开发者可以通过这种方式获取以往的文件。

其他参数:

  • --initial-branch 初始化的分支
  • --bare 创建一个裸仓库(纯git目录,没有工作目录)
  • --template 通过模板来创建预先构建好的自定义git目录

GIT树

.GIT
├─hooks
├─info
├─logs
│  └─refs
│      ├─heads
│      └─remotes
│          └─origin
├─objects
│  ├─info
│  ├─pack
└─refs
    ├─heads
    ├─remotes
      └─origin
    └─tags
  • .git

这是 Git 仓库的核心目录,用于存储 Git 仓库的所有元数据和对象数据库,是 Git 能够正常工作的基础。

  • hooks

    • 功能:存放钩子脚本的目录。钩子脚本是 Git 在特定事件发生时自动执行的脚本,如提交前、提交后、推送前等。这些脚本可以用于执行各种自动化任务,例如代码检查、邮件通知等。

    • 常见文件:比如 pre-commit(提交前执行)、post-commit(提交后执行)、pre-push(推送前执行)等钩子脚本文件。这些文件默认是可执行的,当相应的 Git 事件触发时,Git 会自动运行这些脚本。

  • info

    • 功能:存放一些全局性的信息,这些信息适用于整个仓库。

    • 常见文件exclude 文件,它类似于 .gitignore 文件,用于定义应该被 Git 忽略的文件模式。不过 exclude 文件中的规则不会被推送到远程仓库,而 .gitignore 文件中的规则会被推送到远程仓库。

  • logs

    • 功能:记录 Git 仓库的变更日志,包括分支的更新历史等。
    • refs
    • heads
      • 功能:存放本地分支的日志文件。每个本地分支都有一个对应的日志文件,记录了该分支上每次提交的信息,如提交的哈希值、提交者、提交时间等。这些日志文件可以帮助用户查看分支的更新历史,了解分支的演变过程。
    • remotes
      • origin
      • 功能:存放远程仓库 origin 的分支日志文件。这些文件记录了从远程仓库 origin 拉取的分支的更新历史,与本地分支的日志文件类似。通过这些日志文件,用户可以查看远程分支的变更情况,以便更好地进行代码同步和协作。
  • objects

    • 功能:存储 Git 仓库的所有对象,包括 blob(文件内容对象)、tree(目录结构对象)和 commit(提交对象)等。
    • info
    • 功能:存放关于对象数据库的一些额外信息,如对象的 alternates(替代位置)等。
    • pack
    • 功能:存放打包的对象文件。为了提高存储效率和传输速度,Git 会将多个对象打包成一个包文件。这些包文件包含了多个对象的数据,并且经过了压缩处理。通过使用包文件,Git 可以更高效地存储和传输大量的对象数据。
  • refs

    • 功能:存放对 Git 仓库中对象的引用,这些引用指向仓库中的特定对象,如提交、分支等。
    • heads
    • 功能:存放本地分支的引用。每个本地分支都有一个对应的引用文件,文件名与分支名相同,文件内容是该分支所指向的提交对象的哈希值。通过这些引用文件,Git 可以快速定位到分支所对应的提交对象,从而实现分支的管理和操作。
    • remotes
    • origin
      • 功能:存放远程仓库 origin 的分支引用。这些引用文件记录了远程分支所指向的提交对象的哈希值,与本地分支的引用类似。通过这些引用,用户可以了解远程分支的当前状态,并且在进行拉取、推送等操作时,Git 会根据这些引用信息来同步本地和远程仓库的数据。
    • tags
    • 功能:存放标签的引用。标签是对特定提交对象的命名引用,通常用于标记软件的发布版本等重要里程碑。每个标签都有一个对应的引用文件,文件内容是该标签所指向的提交对象的哈希值。通过这些标签引用,用户可以方便地查找和引用特定版本的代码。

工作区 & 暂存区

graph LR
    A["Working Directory"] -- Stage Fixes --> C["Staging Area"]
    C -- Commit --> B[".git directory (Repository)"]
    B -- Checkout the project --> A

在 Git 中,工作流程主要涉及三个区域:工作区(Working Directory)、暂存区(Staging Area)和 Git 仓库(.git directory)。

工作区(Working Directory)

  • 定义:这是你在文件系统中看到的目录,包含你的项目文件。
  • 操作:你在这里进行文件的编辑和修改。

暂存区(Staging Area)

  • 定义:位于工作区和 Git 仓库之间的一个中间区域。
  • 操作:在这里,你可以选择哪些修改将被提交到 Git 仓库。

Git 仓库(.git directory)

  • 定义:这是 Git 用来存储项目元数据和对象数据库的地方。
  • 操作:所有的提交(Commit)都保存在这里。

工作流程

  1. Checkout the project

    • 从 Git 仓库中检出项目到工作区。
    • 这一步通常在你克隆仓库时完成。
  2. Stage Fixes

    • 在工作区中对文件进行修改后,你可以将这些修改添加到暂存区。
    • 使用 git add 命令将修改添加到暂存区。
  3. Commit

    • 将暂存区中的修改提交到 Git 仓库。
    • 使用 git commit 命令完成提交。

4.2 Git 配置

不同级别的git配置

  • --system

    • 写入选项:写入到系统范围的 $(prefix)/etc/gitconfig 而不是仓库的 .git/config

    • 读取选项:仅从系统范围的 $(prefix)/etc/gitconfig 读取配置,而不是从所有可用文件读取。

    • 范围:系统上**所有**用户

  • --global

    • 写入选项:写入到全局 ~/.gitconfig 文件而不是仓库的 .git/config 文件。如果存在 $XDG_CONFIG_HOME/git/config 文件且 ~/.gitconfig 文件不存在,则写入到 $XDG_CONFIG_HOME/git/config 文件。

    • 读取选项:仅从全局 ~/.gitconfig$XDG_CONFIG_HOME/git/config 读取配置,而不是从所有可用文件读取。

    • 范围:系统上的**当前**用户

  • --local

    • 写入选项:写入到仓库的 .git/config 文件。这是默认行为。

    • 读取选项:仅从仓库的 .git/config 读取配置,而不是从所有可用文件读取。

每个级别的配置可能重复,但是低级别的配置会覆盖高级别的配置。

常见配置

  • 用户名配置
git config --global user.name “liaoxingju
git config --global user.email liaoxingju@bytedance.com
  • instead of 配置
git config --global url.git@github.com:.insteadOf https://github.com/

设置一个 URL 代替规则,使得在使用 git clone 或其他需要 URL 的 Git 命令时,自动将 https://github.com/ 替换为 git@github.com:

  • 命令别名配置
git config --global alias.cin "commit --amend --no-edit

git config --global alias.cin "commit --amend --no-edit":这条命令设置了一个别名 cin,当你输入 git cin 时,Git 会执行 commit --amend --no-edit 命令。--amend 选项用于修改最近一次提交,而 --no-edit 选项表示不重新编辑提交信息,直接使用上一次的提交信息。

4.3 Git Remote

除了基本配置,还有一种配置是 remote 配置,这个表示我们 本地和远端仓库的关联信息 ,remote 一般分为 htp 和 ssh 两种。

常见配置

  • 查看 remote
git remote -v
  • 添加remote
git remote add origin ssh git@github.com:git/git.git
git remote add origin http https://github.com/git/git.git
  • 设置不同的fetch和push的URL

原因:

  1. 不同的访问权限

    你可能需要从内部服务器(只有读取权限)拉取代码,但向外部服务器(有写入权限)推送代码。

  2. 不同的协议

    你可能需要通过 HTTPS 拉取代码,但通过 SSH 推送代码,或者反之。

  3. 备份或镜像

    你可能需要将代码推送到多个远程仓库,例如一个用于日常开发,另一个用于备份。

git remote set-url origin <fetch-url>
git remote set-url --push origin <push-url>
git remote get-url origin # 查看 fetch URL
git remote get-url origin --push # 查看 push URL

HTTP Remote

当你在使用 Git 进行版本控制时,经常需要与远程仓库进行交互,如 GitHub、GitLab 等。这些远程仓库通常通过 HTTP(S) 协议进行访问。为了简化与远程仓库的交互过程,Git 提供了 HTTP 远程仓库的免密配置功能。以下是关于 HTTP 远程仓库及其免密配置的详细讲解:

HTTP 远程仓库是指使用 HTTP 或 HTTPS 协议访问的 Git 仓库。这种方式 不需要在本地配置 SSH 密钥,而是通过用户名和密码进行认证 。这使得 HTTP 远程仓库在某些场景下(如公共仓库或临时协作)更加方便。

免密配置

免密配置允许 Git 在一定时间内自动使用之前保存的用户名和密码,而不需要每次交互都手动输入。这对于频繁与远程仓库交互的场景非常有用。

内存缓存

Shell复制

git config --global credential.helper 'cache --timeout=3600'
  • 解释:这条命令配置 Git 在内存中缓存用户名和密码,超时时间为 3600 秒(即 1 小时)。这意味着在 1 小时内,Git 会自动使用缓存的凭据进行认证,而不需要再次输入用户名和密码。

  • 优点:不需要在磁盘上保存敏感信息,相对安全。

  • 缺点:超时后需要重新输入用户名和密码。

硬盘存储

Shell复制

git config --global credential.helper "store --file /path/to/credential-file"

如果不指定目录,Git 默认使用 ~/.git-credentials 文件。

  • 解释:这条命令配置 Git 将用户名和密码存储在指定的文件中。如果不指定文件路径,Git 会默认使用 ~/.git-credentials 文件。

  • 优点:可以永久保存凭据,不需要频繁输入用户名和密码。

  • 缺点:需要确保存储文件的安全性,因为其中包含了敏感信息。

将密钥信息存在指定文件中

具体格式:${scheme}://${user}:${password}@github.com

  • 解释:当使用硬盘存储方式时,用户名和密码会以特定的格式存储在指定的文件中。格式为 ${scheme}://${user}:${password}@github.com,其中 ${scheme} 是访问远程仓库时使用的协议(如 https),${user} 是用户名,$password 是密码。

  • 安全性:虽然这种方式可以方便地管理凭据,但需要注意文件的安全性。建议将文件权限设置为仅所有者可读写,以防止未经授权的访问。

SSH Remote

SSH(Secure Shell)是一种网络协议,用于加密方式远程登录到服务器并执行命令。在 Git 中,SSH 同样用于安全地与远程仓库通信,特别是当涉及到推送(push)和拉取(pull)操作时。使用 SSH 与远程仓库交互的一个主要好处是可以实现免密访问,即不需要每次交互都输入用户名和密码。

URL

SSH 远程仓库的 URL 通常以 git@github.com: 开头,后面跟着仓库的路径。例如:

git@github.com:git/git.git

这个 URL 指定了远程仓库的位置,其中 git 是用户名,github.com 是服务器地址,git/git.git 是仓库路径。

免密配置

SSH 免密配置通过公私钥机制实现。具体步骤如下:

  1. 生成 SSH 密钥对:使用 ssh-keygen 命令生成一对公私钥。如果系统上还没有 SSH 密钥,这个命令会生成一个新的密钥对,并询问你保存密钥的文件位置和密码短语(passphrase)。

    Shell复制

    ssh-keygen -t ed25519 -C "your_email@example.com"
    

    这个命令会生成一个类型为 ed25519 的密钥对,并将公钥保存在 ~/.ssh/id_ed25519.pub 文件中。

  2. 添加 SSH 公钥到 GitHub:将生成的公钥(~/.ssh/id_ed25519.pub 文件的内容)添加到 GitHub 账户的 SSH 密钥部分。这通常可以在 GitHub 的设置(Settings)中找到。

  3. 测试 SSH 连接:使用 ssh 命令测试与 GitHub 的 SSH 连接是否成功。

    Shell复制

    ssh -T git@github.com
    

    如果连接成功,你会看到一条欢迎消息。

Github公开密钥创建

SSH 密钥类型

SSH 密钥有几种不同的类型,包括:

  • dsa:数字签名算法,由于安全性问题,现在不推荐使用。

  • rsa:一种广泛使用的公钥加密算法,也因为一些安全问题而不被推荐。

  • ecdsa:椭圆曲线数字签名算法,提供了与 RSA 相当的安全性,但密钥尺寸更小。

  • ed25519:一种基于 EdDSA 的公钥签名方案,提供了更好的安全性和性能,是目前推荐的密钥类型。

4.4 git add——向暂存区中添加文件

如果只是用 Git 仓库的工作树创建了文件,那么该文件并不会被记入 Git 仓库的版本管理对象当中。因此我们用 git status命令查看README.md 文件时,它会显示在 Untracked files 里。

要想让文件成为 Git 仓库的管理对象,就需要用 git add命令将其加入暂存区(Stage 或者 Index)中。暂存区是提交之前的一个临时区域。

$ git add README.md
$ git status
# On branch master
# Initial commit
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: README.md

Note

以下是一个具体的实验案例:

touch readme.md
tree .git
git status
git add readme.md # 添加到暂存区
git status
git commit -m "Add readme.md"

4.5 git commit——保存仓库的历史记录

git commit命令可以将当前暂存区中的文件实际保存到仓库的历史记录中。通过这些记录,我们就可以在工作树中复原文件。

  • ……记述一行提交信息
$ git commit -m "First commit"
[master (root-commit) 9f129ba] First commit
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 README.md

-m参数后的 "First commit"称作提交信息,是对这个提交的概述。

  • ……记述详细提交信息

刚才我们只简洁地记述了一行提交信息,如果想要记述得更加详细,请不加 -m,直接执行 git commit命令。执行后编辑器就会启动,并显示如下结果。

# 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:
# (use "git rm --cached <file>..." to unstage)
#
# new file: README.md
#

在编辑器中记述提交信息的格式如下:

  • 第一行:用一行文字简述提交的更改内
  • 第二行:空行
  • 第三行以后:记述更改的原因和详细内容

只要按照上面的格式输入,今后便可以通过确认日志的命令或工具看到这些记录。

在以 #(井号)标为注释的 Changes to be committed(要提交的更改)栏中,可以查看本次提交中包含的文件。将提交信息按格式记述完毕后,请保存并关闭编辑器,以 #(井号)标为注释的行不必删除。随后,刚才记述的提交信息就会被提交。

Vim编辑器中,

  • ……中止提交

如果在编辑器启动后想中止提交,请将提交信息留空并直接关闭编辑器,随后提交就会被中止。

  • ……

  • 查看提交后的状态

执行完 git commit命令后再来查看当前状态。

$ git status
# On branch master
nothing to commit, working directory clean

当前工作树处于刚刚完成提交的最新状态,所以结果显示没有更改。

对象模型——objects

  • Blob

    • 存储文件的内容。
    • 每个文件的内容都对应一个唯一的 Blob 对象。
  • Tree

    • 存储文件的目录信息。
    • 每个目录及其文件结构都对应一个 Tree 对象。
  • Commit

    • 存储提交信息。
    • 每个 Commit 对象包含一个指向 Tree 对象的指针,以及作者、提交者、提交信息等元数据。
    • 一个 Commit 对应唯一版本的代码。

如何串联 Commit、Tree 和 Blob?

  1. 通过 Commit 找到 Tree 信息

    每个 Commit 都会存储对应的 Tree ID。

    git cat-file -p <commit-hash> # 查看 Commit 信息,找到对应的 Tree ID
    
  2. 通过 Tree 存储的信息,获取到对应的目录树信息

shell git cat-file -p <tree-hash> # 查看 Tree 信息,找到文件列表和 Blob ID

  1. 从 Tree 中获得 Blob 的 ID,通过 Blob ID 获取对应的文件内容

shell git cat-file -p <blob-hash> # 查看 Blob 信息,获取文件内容

图示说明

复制

commit 98ca9 ├── tree 92ec2 │ └── blob 5b1d3 README │ └── blob 911e7 LICENSE │ └── blob cba0a test.rb

  • Commit 98ca9:初始提交,包含三个文件。

    • Tree 92ec2:包含三个文件的目录结构。

      • Blob 5b1d3:README 文件的内容。

      • Blob 911e7:LICENSE 文件的内容。

      • Blob cba0a:test.rb 文件的内容。

其他

git status——查看仓库的状态

$ git status
# On branch master
# Initial commit
nothing to commit (create/copy files and use "git add" to track)

结果显示了我们当前正处于 master 分支下。接着还显示了没有可提交的内容。所谓提交(Commit),是指“记录工作树中所有文件的当前状态”。

尚没有可提交的内容,就是说当前我们建立的这个仓库中还没有记录任何文件的任何状态。这里,我们建立 README.md 文件作为管理对象,为第一次提交做前期准备。

$ touch README.md
$ git status
# On branch master
#
(use "git add <file>..." to include in what will
# Initial commit
### Untracked files:# be committed)#
# README.md
nothing added to commit but untracked files present (use "git add" to
track)

git log——查看提交日志

commit 栏旁边显示的“9f129b……”是指向这个提交的哈希值。Git 的其他命令中,在指向提交时会用到这个哈希值。

Author 栏中显示我们给 Git 设置的用户名和邮箱地址。Date 栏中显示提交执行的日期和时间。再往下就是该提交的提交信息。

  • ……只显示提交信息的第一行
--pretty=short
  • ……只显示指定目录、文件的日志

只要在 git log命令后加上目录名,便会只显示该目录下的日志。

如果加的是文件名,就会只显示与该文件相关的日志。

  • ……显示文件的改动

如果想查看提交所带来的改动,可以加上 -p参数,文件的前后差别就会显示在提交信息之后。

git diff——查看更改前后的差别

git diff命令可以查看工作树、暂存区、最新提交之间的差别。单从字面上可能很难理解,各位不妨跟着笔者的解说亲手试一试。

当我们在刚刚提交的 README.md 中写点东西……

  • ……查看工作树和暂存区的差别
$ git diff
diff --git a/README.md b/README.md
index e69de29..cb5dc9f 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1 @@
+# README

由于我们尚未用 git add命令向暂存区添加任何东西,所以程序只会显示工作树与最新提交状态之间的差别。

这里解释一下显示的内容。“+”号标出的是新添加的行,被删除的行则用“-”号标出。我们可以看到,这次只添加了一行。

用 git add命令将 README.md 文件加入暂存区。

$ git add README.md
  • ……查看工作树和最新提交的差别

如果现在执行 git diff命令,由于工作树和暂存区的状态并无差别,结果什么都不会显示。要查看与最新提交的差别,请执行以下命令。

$ git diff HEAD
diff --git a/README.md b/README.md
index e69de29..cb5dc9f 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1 @@
+# README

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

Linux补充-touch

touch 是一个常见的 Unix/Linux 命令,用于 创建空文件更新文件的时间戳。它的基本功能是与文件的访问和修改时间相关,常用于文件的管理和操作。

1. 创建文件

如果指定的文件不存在,touch 会创建一个空的文件。该文件会有一个默认的大小(通常是 0 字节),并且文件的内容是空的。

示例:

bash touch example.txt

  • 如果 example.txt 文件不存在,touch 将创建一个新的空文件。
  • 如果文件已存在,touch 不会改变文件内容,只会更新文件的时间戳。

2. 更新文件的时间戳

touch 还可以用于更新文件的访问时间(atime)和修改时间(mtime)到当前时间。该命令不会改变文件内容,仅修改文件的时间信息。

示例:

bash touch example.txt

  • 如果 example.txt 已存在,执行后会更新该文件的修改时间和访问时间为当前时间。
  • 如果文件不存在,则创建一个新的空文件。

3. 选项

touch 命令还支持多个选项来控制其行为。

  • -c--no-create:如果文件不存在,不创建文件,仅更新已有文件的时间戳。

    touch -c example.txt
    
  • -t:允许你手动设置时间戳,而不是使用当前时间。你需要指定一个特定的时间格式。

    touch -t 202312251230 example.txt
    

    这会将 example.txt 的时间戳设置为 2023 年 12 月 25 日 12:30。

  • -d--date:使用指定的日期和时间设置文件的时间戳。

    touch -d "2023-12-25 12:30" example.txt
    

    这将文件 example.txt 的时间戳设置为给定的日期和时间。

  • -r:根据另一个文件的时间戳来设置目标文件的时间戳。

    touch -r reference.txt target.txt
    

    这会将 target.txt 的时间戳设置为 reference.txt 的时间戳。

4. 使用场景

  • 创建空文件:用于快速创建一个空的文件,特别是在编写脚本或初始化项目时。例如,创建一个 README.md 文件,初始化一个日志文件等。

  • 更新文件时间戳:在某些版本控制系统(如 Git)中,touch 可以用于更新文件的时间戳,使其被认为是最近修改的,从而触发后续的操作。

  • 批量操作:可以在批量操作中通过 touch 更新多个文件的时间戳。例如,在多个文件上执行时间戳更新操作,而不更改文件内容。

5. 总结

touch 是一个简单却强大的命令,通常用于以下两种场景:

  • 创建空文件,如果指定文件不存在。
  • 更新文件时间戳,而不修改文件内容。

通过其各种选项,touch 可以灵活地应用于不同的文件管理任务。