Git基础06 — 日志与版本回退

前言

本文档您将学习如何使用 Git 进行版本回退以及日志方面的查看。

commit id

在版本回退之前,您需要知道 commit id,commit id 指的是每一次提交之后,都会有一个 ID 号作为标识,例如我这三次的提交:

PS > cd E:\git-test\

PS > git log
commit 47d9368938c852c99931df28d18925273c8c19b1 (HEAD -> master)
Author: xxxxx
Date:   Sat Mar 1 12:56:36 2025 +0800

    tmp-file.txt --> Tmp-File.txt

commit ae6cc9de54f827c652df0002d7fa563bd4e19016
Author: xxxxx
Date:   Sat Mar 1 12:44:38 2025 +0800

    First commit

commit 9a703559413238e09c1c1a4fb6221e370a572247
Author: xxxxx
Date:   Sat Mar 1 11:06:43 2025 +0800

    测试文件

git log

git log 是一个强大的命令,语法为:

git log [<options>] [<revision-range>] [[--] <path>…​]

常用选项有:

  • --oneline – 单行显示 commit id 以及提交的说明
  • -p – 以补丁的方式显示每次更新的差异性
  • --stat – 显示每次提交后增减行数的统计信息
  • --no-merges – 显示整个提交的历史记录,但跳过合并过的记录

按照特定条件筛选日志内容

  1. 按照日期时间的先后顺序

    • --before=<date> – 显示特定日期时间之前的提交,例如 git log --before="2025-03-09 13:20:30"
    • --after=<date> – 显示特定日期时间之后的提交

    选项值 date 除了支持自然日期时间之外,还支持一些其他格式,如相对时间(比如 "2 hours ago","2 weeks ago")、以秒为单位的 UNIX 时间戳等,但比较少用到,使用自然日期时间即可。

  2. 按照显示的数量

    • -n <number> – 显示最近的 n 条记录
  3. 按照作者

    • --author=<pattern> – 按照正则表达式筛选作者。比如 git log --author="Frank Lee | Jack Chen" -E
    • --regexp-ignore-case – 忽略正则表达式中的大小写
    • -E – 启用扩展正则表达式

    默认情况下,--author=<pattern> 选项使用 BRE(basic regular express,基础正则表达式),若要使用 ERE(extend regular express,扩展正则表达式),请再添加 -E 选项。

  4. 按照提交的信息

    • --grep=<pattern> – 按照正则表达式筛选提交信息
    • --regexp-ignore-case – 忽略正则表达式中的大小写
    • -E – 启用扩展正则表达式

    默认情况下,--grep=<pattern> 选项使用 BRE(basic regular express,基础正则表达式),若要使用 ERE(extend regular express,扩展正则表达式),请再添加 -E 选项。

  5. 按照特定的文件或目录

    众所周知,在 bash 当中,可使用 -- 表示某一个命令选项的结束,同样的的规则也适用于 git log,比如:

    # 查看 public 目录下的所有日志信息
    Shell > git log -- public/
    
    # 也可以针对一个或多个文件
    Shell > git log -- tmp.text README.md
  6. 按照分支

    分支我们还没有说明,分为本地分支与远程分支。比如:

    # 查看本地分支 master 的日志信息
    Shell > git log master 
    
    # 也可以同时指定多个分支
    Shell > git log master dev
    ## 或者
    Shell > git log --branches=master,dev
    
    # 分支之间的对比
    ## 反向比较:存在于 dev 分支中但还没有合并到 master 的提交记录,即显示 dev 分支比 master 分支多出的提交记录
    Shell > git log master..dev
    ## 对称比较:三个 "..." 表示两个分支不重叠的所有提交(即两个分支的独有提交)
    Shell > git log master...dev
  7. 按照增减文件内容的字符串

    • -S <string> – 比如 git log -S "First Line",这非常适合那些需要精准定位修改时是哪一次 commit 的
    • --pickaxe-regex – 将给定的 \<string>视为扩展的 POSIX 正则表达式以进行匹配
  8. 按照正则表达式匹配文件增减内容的字符串

    • -G <regex>
    • --regexp-ignore-case – 忽略正则表达式中的大小写
    • -E – 启用扩展正则表达式

    我们可以通过以下例子来看 -S 和 -G 之间的区别:

    PS > cd E:\git-test\
    
    PS > new-Item lab-file.txt
    
    # PowerShell 中的换行符为 `n
    PS > echo "hit = frotz(nitfol, mf2.ptr, 1, 0);" > lab-file.txt
    
    PS > git add ./
    
    PS > git commit -m "lab-file初始内容"
    
    # 将文件中的该行删除并使用以下的文本替换掉:
    PS > echo "return frotz(nitfol, two->ptr, 1, 0);" > .\lab-file.txt
    
    PS > git add ./
    
    PS > git commit -m "修改后的lab-file文件"
    
    PS > git log -p
    commit 8a77771a1c837c9e4a06014af7a4801fd982260c (HEAD -> master)
    Author: xxxxx
    Date:   Sun Mar 9 18:41:08 2025 +0800
    
        修改后的lab-file文件
    
    diff --git a/lab-file.txt b/lab-file.txt
    index 5516f96..f14d785 100644
    --- a/lab-file.txt
    +++ b/lab-file.txt
    @@ -1 +1 @@
    -hit = frotz(nitfol, mf2.ptr, 1, 0);
    +return frotz(nitfol, two->ptr, 1, 0);
    ...
    
    # 若使用 -G,这两次的 commit 都会正常出现
    PS > git log -G "frotz\(nitfol" --oneline
    8a77771 (HEAD -> master) 修改后的lab-file文件
    f20abbc lab-file初始内容
    
    # 但若使用 -S 的正则表达式,则只会有上一次提交记录被筛选出来
    ## 为什么呢?因为 frotz(nitfol 这个字符串的出现次数没有变化,修改之前是1次,修改之后还是1次
    PS > git log -S "frotz\(nitfol"  --pickaxe-regex --oneline
    f20abbc lab-file初始内容

    如下说明:

    选项 说明 正则表达式
    -G 在文件内容变更前后满足正则表达式即可 原生支持
    -S 在文件内容变更前后需要满足字符串的次数变化,若匹配的字符串在修改前后无次数变化,则不会筛选出具体的日志 需要 –pickaxe-regex

以上的筛选条件可以组合使用,但需要注意范围的由大到小。

众所周知,由于历史的发展,正则表达式主要有两大流派:

  • POSIX
    • BRE(basic regular express,基础正则表达式)
    • ERE(extend regular express,扩展正则表达式)
    • POSIX character class
  • PCRE (Perl Compatible Regular Expressions):在各种编程语言中最常见的。

版本回退

使用命令 git reset --hard <commit-id> 即可,commid-id 可通过 git log 查询到。

PS > git log --oneline
8a77771 (HEAD -> master) 修改后的lab-file文件
f20abbc lab-file初始内容
47d9368 tmp-file.txt --> Tmp-File.txt
ae6cc9d First commit
9a70355 测试文件

假设我需要将版本回退到 lab-file.txt 这个文件出现之前的内容,通过查询可知,我们需要的 commit id 为 47d9368:

PS > git reset --hard 47d9368
HEAD is now at 47d9368 tmp-file.txt --> Tmp-File.txt

PS > ls

    Directory: E:\git-test

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---            2025/3/1    12:43             12 Tmp-File.txt

一旦进行版本回退,则在此版本之后进行 commit 提交的日志会查询不到:

PS > git log --oneline
47d9368 (HEAD -> master) tmp-file.txt --> Tmp-File.txt
ae6cc9d First commit
9a70355 测试文件

若您还需要查阅它们,可通过以下 git reflog 命令查询 commit id,如下:

PS > git reflog
47d9368 (HEAD -> master) HEAD@{0}: reset: moving to 47d9368
8a77771 HEAD@{1}: commit: 修改后的lab-file文件
f20abbc HEAD@{2}: commit: lab-file初始内容
...

HEAD 动态指针

Git 底层原理当中,其实最重要的就是这个 "动态指针",英文叫HEAD。在正常分支状态下,HEAD ‌通过分支指针间接指向最新提交‌。HEAD 是一个特殊指针,其指向的位置会随着分支的切换或提交的变化而变动。

  1. 正常情况(HEAD 通过分支指针间接指向最新提交)

        HEAD(动态指针)
            │
            ▼
        [master](分支指针)
            │
            ▼
        Commit C3(最新提交)

    说明如下:

    1. HEAD 指向了 master,在磁盘上,指的是项目目录下 .git\HEAD 这个文件,其文件内容为 ref: refs/heads/master
    2. 分支指针 master 指向了最新提交,指的是项目目录下 .git/refs/heads/master 这个文件,其文件内容为具体的 commit id (commit id 即哈希值)
    3. 在 C3 上新提交 C4 之后,master 分支指针会更新到 C4,HEAD 仍通过 master 间接指向 C4
  2. 分离 HEAD (即 HEAD 直接指向具体的 commit id,Detached HEAD)

    若使用者直接将 HEAD 指向 commit id,则会出现一种被称为 Detached HEAD 的情况。以下两种操作方式可进入这种情况:

    旧版本的操作:

    PS > git checkout <commit-id>

    新版本的操作:

    PS >  git switch --detach <commit-id>

    在 Detached HEAD 状态下,你在当前状态下做的任何提交都不会和当前活跃分支关联,这时可选择以下三种方式的其中一种来进行下一步操作:

    1. 直接在当前提交记录上新创建分支
    2. 在当前提交记录的基础上再做一些修改并提交然后绑定到分支(如下示例)
    3. 直接切换分支放弃 Detached HEAD 状态下的修改提交
    # 假设不小心将 HEAD 指向了具体的 commit id
    PS > git switch -d ae6cc9d
    HEAD is now at ae6cc9d First commit
    
    PS > git status
    HEAD detached at ae6cc9d
    nothing to commit, working tree clean
    
    PS > cat .\tmp-file.txt
    First Line
    
    # 做了一些修改
    PS > echo "Second Line" >> .\tmp-file.txt
    
    PS > cat .\tmp-file.txt
    First Line
    Second Line
    
    PS > git add ./
    PS > git commit -m "tmp-file文件添加第二行"
    
    # 创建新分支指向修改后的提交记录
    PS > git branch new-branch
    
    PS > git switch new-branch
    
    PS > git status
    On branch new-branch
    nothing to commit, working tree clean
    
    PS > git log --oneline
    b7354a2 (HEAD -> new-branch) tmp-file文件添加第二行
    ae6cc9d First commit
    9a70355 测试文件
  3. 切换到新分支

    HEAD 指向新的分支指针,分支指针指向最新的提交,此时 HEAD ‌依然通过分支指针间接指向最新提交。

Q:上面的例子进行了版本回退,这里的 HAED@{1} 和 HEAD@{2} 是什么意思?

这里的数字表示动态指针相对最近一次移动的上一个位置,0 表示当前指针的位置,1 表示指针上一次指到的位置,以此类推。

Avatar photo

关于 陸風睿

GNU/Linux 从业者、开源爱好者、技术钻研者,撰写文档既是兴趣也是工作内容之一。Q - "281957576";WeChat - "jiulongxiaotianci",Github - https://github.com/jimcat8
用一杯咖啡支持我们,我们的每一篇[文档]都经过实际操作和精心打磨,而不是简单地从网上复制粘贴。期间投入了大量心血,只为能够真正帮助到您。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇