git 学习笔记 8
start at 2023/03/01.

令人头疼的期初期末考试周终于结束了(放在期初的期末考试周),接下来终于可以做想做的事情了,比如说,读完这本书,作为第一本被我读完的图书馆书,你一定非常荣幸吧!

修改历史记录

使用 --amend 参数可以修改最后一次 Commit 的信息,但只能修改最后一次,如果要改动更加早的信息就没法使用这个办法了,下面就介绍一下 git rebase 的另一个强大的互动模式 git reabse -i

启动互动模式

首先来看一下 git 记录

$ git log --oneline
1baa9df (HEAD -> master) modify d.txt
9cb9203 modify b.txt
73765e1 add d.txt
834f147 add c.txt
7362b74 add b.txt
6c34fff modify a.txt
332971d add a.txt

我们使用rebase的互动模式的时候必须指定某个 commit,我们指定最初的 commit 332971d,意思是修改的范围为 332971d 到最后一条 commit,貌似 332971d 无法被修改

$ git rebase -i 332971d

然后我们会进入 vim,下面是 vim 页面显示的信息

pick 6c34fff modify a.txt
pick 7362b74 add b.txt
pick 834f147 add c.txt
pick 73765e1 add d.txt
pick 9cb9203 modify b.txt
pick 1baa9df modify d.txt

# Rebase 332971d..1baa9df onto 332971d (6 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
#         create a merge commit using the original merge commit's
#         message (or the oneline, if no original merge commit was
#         specified); use -c <commit> to reword the commit message
# u, update-ref <ref> = track a placeholder for the <ref> to be updated
#                       to this position in the new commits. The <ref> is
#                       updated at the end of the rebase
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#

能看到有两部分区域,一部分贴着 commit 列表,如果你观察仔细的话,会发现这里的 commit 列表的顺序和 git log 的顺序相反,这就意味着 rebase 的 commit 时间顺序是从上到下,这点要注意

另一边贴着注释说明,没错,主要介绍的其实就是注释说明里的东西

  • pick,表示维持这条 commit, 什么都不变
  • reword,表示修改这条 commit 的说明
  • edit,表示 rebase 停止到这条 commit,一般用来进行具体修改
  • squash,表示合并到前一条 commit
  • drop,删除该条 commit

具体使用方法是修改 commit 前面的单词,比如说我想修改下面的第一条 commit 就可以这样(缩写和不缩写都是可以的)

r 6c34fff modify a.txt
pick 7362b74 add b.txt
pick 834f147 add c.txt
pick 73765e1 add d.txt
pick 9cb9203 modify b.txt
pick 1baa9df modify d.txt

编辑完之后使用 :wq 退出交互模式

退出之后会马上弹出一个新的 vim 界面,这是你用来修改指定 commit 信息的页面

modify a.txt

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Tue Mar 7 18:35:50 2023 +0800
#
# interactive rebase in progress; onto 332971d
# Last command done (1 command done):
#    reword 6c34fff modify a.txt
# Next commands to do (5 remaining commands):
#    pick 7362b74 add b.txt
#    pick 834f147 add c.txt
# You are currently editing a commit while rebasing branch 'master' on '332971d'.
#
# Changes to be committed:
#	modified:   a.txt
#

修改好之后 :wq 即可,使用 git log --oneline 查看当前状态,发现第二条 commit 信息已经被我们修改了

$ git log --oneline
00ebd6a (HEAD -> master) modify d.txt
695c0c4 modify b.txt
43b4909 add d.txt
1ae01db add c.txt
689dd82 add b.txt
ea3ebd0 modiiify a.txt
332971d add a.txt

如果你想同时修改多个 commit 信息也是可以的,它会按照时间先后顺序一个一个跳出修改 commit 信息的界面,仔细观察你会发现修改了第二条 commit 之后很多 commit 的 SHA-1 都发生了变化,这是一个需要记住的现象,原理的话思考一下 git 底层原理你可能就能想明白(SHA-1 的值是由什么决定的?)

如果你想撤销修改,可以使用 git reset ORIG_HEAD --hard 回到修改前的状态

下面展示如何使用 rebase 的交互模式把多个 commit 合并为一条 commit, 使用的关键字是 squash 缩略为 s ,下面合并那几条 add 的 commit,注意上面那条现象(早的 commit 在上面)

pick ea3ebd0 modiiify a.txt
pick 689dd82 add b.txt
s 1ae01db add c.txt
s 43b4909 add d.txt
pick 695c0c4 modify b.txt
pick 00ebd6a modify d.txt

:wq 之后进入编写新的 commit 界面

# This is a combination of 3 commits.
# This is the 1st commit message:

add b.txt

# This is the commit message #2:

add c.txt

# This is the commit message #3:

add d.txt

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# Date:      Tue Mar 7 18:36:27 2023 +0800
#
# interactive rebase in progress; onto 332971d
# Last commands done (4 commands done):
#    squash 1ae01db add c.txt
#    squash 43b4909 add d.txt
# Next commands to do (2 remaining commands):
#    pick 695c0c4 modify b.txt
#    pick 00ebd6a modify d.txt
# You are currently rebasing branch 'master' on '332971d'.
#
# Changes to be committed:
#	new file:   b.txt
#	new file:   c.txt
#	new file:   d.txt
#

修改 1st commit 的信息后 :wq 就完事了

$ git log --oneline
181b3d0 (HEAD -> master) modify d.txt
3839de8 modify b.txt
5fc87ae add b, c, d.txt
ea3ebd0 modiiify a.txt
332971d add a.txt

处理多条的时候注意看清楚信息,一般来说都是从最后往前修改的

接下来演示将一个 commit 拆成多个 commit ,我们就把之前的操作给手动回档一下

使用 e(dit)

pick ea3ebd0 modiiify a.txt
e 5fc87ae add b, c, d.txt
pick 3839de8 modify b.txt
pick 181b3d0 modify d.txt

使用 :wq 之后进入待 rebase continue 状态,HEAD 停止在了 5fc87ae

Stopped at 5fc87ae...  add b, c, d.txt
You can amend the commit now, with

  git commit --amend

Once you are satisfied with your changes, run

  git rebase --continue

因为要拆散,所以就要回到前一个 commit 节点

$ git reset HEAD^
$ git status
interactive rebase in progress; onto 332971d
Last commands done (2 commands done):
   pick ea3ebd0 modiiify a.txt
   edit 5fc87ae add b, c, d.txt
Next commands to do (2 remaining commands):
   pick 3839de8 modify b.txt
   pick 181b3d0 modify d.txt
  (use "git rebase --edit-todo" to view and edit)
You are currently editing a commit while rebasing branch 'master' on '332971d'.
  (use "git commit --amend" to amend the current commit)
  (use "git rebase --continue" once you are satisfied with your changes)

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

然后我们就一个一个添加这些文件吧

$ git log --oneline
d546340 (HEAD) add d.txt
9df0f92 add c.txt
f3a8368 add b.txt
ea3ebd0 modiiify a.txt
332971d add a.txt

完事之后,使用 git rebase --continue 自动档完成接下来的事件,过程有点像处理冲突

$ git rebase --continue
Successfully rebased and updated refs/heads/master.
$ git log --oneline
c7909c6 (HEAD -> master) modify d.txt
f069fe5 modify b.txt
d546340 add d.txt
9df0f92 add c.txt
f3a8368 add b.txt
ea3ebd0 modiiify a.txt
332971d add a.txt

利用相同的原理就能做到很多事情了,比如说在某两条 commit 之间增加新的 commit(虽然这样做有可能会发生冲突

其实 pick 关键字也能完成对历史的修改,比如说换 commit 的位置,下面换了第二条和第三条的位置

pick ea3ebd0 modiiify a.txt
pick 9df0f92 add c.txt
pick f3a8368 add b.txt
pick d546340 add d.txt
pick f069fe5 modify b.txt
pick c7909c6 modify d.txt

:wq 保存,commit 的顺序发生了变化

$ git log --oneline
f5bca1e (HEAD -> master) modify d.txt
543cd8e modify b.txt
6dd1fd7 add d.txt
f8ba4ec add b.txt
25cad87 add c.txt
ea3ebd0 modiiify a.txt
332971d add a.txt

删除 commit 的方式也和之前那些类似,使用 drop 关键字即可,同样直接删除对应 commit 所在的行也是可以的

后果

修改历史都是有可能会发生冲突的,你删除了后面要修改的内容,不发生冲突才怪

所以修改历史的时候都要小心点,尽量少修改吧

Revert

reset rebase 类似,revert 也是用来后悔的工具,不过与前者不同,Revert 不会改变历史

$ git log --oneline
6f5637b (HEAD -> master) add c.txt
ce674de modify b.txt
f9c4478 add b.txt
779a2c6 modify a.txt
da3b8d4 add a.txt

我们现在想后悔 ce674de 那条修改,可以在 revert 命令后面加上 --no-edit 参数,这样就不用自己填写 commit 信息了,而使用默认的 revert 信息

$ git revert ce674de
[master 8f9556e] Revert "modify b.txt"
 1 file changed, 1 deletion(-)
$ git log --oneline
8f9556e (HEAD -> master) Revert "modify b.txt"
6f5637b add c.txt
ce674de modify b.txt
f9c4478 add b.txt
779a2c6 modify a.txt
da3b8d4 add a.txt

发现并没有改变历史,而是增加了一条取消 revert 的 commit,大概就是把那条 commit 的修改反过来做吧

取消 revert 倒是有两种方法,一种是直接 reset, 另一种是蠢方法,revert 那条 revert 的 commit(像取消了你的取消,搁这交康呢

$ git revert HEAD
[master 7b7eb50] Revert "Revert "modify b.txt""
 1 file changed, 1 insertion(+)
$ git log --oneline
7b7eb50 (HEAD -> master) Revert "Revert "modify b.txt""
8f9556e Revert "modify b.txt"
6f5637b add c.txt
ce674de modify b.txt
f9c4478 add b.txt
779a2c6 modify a.txt
da3b8d4 add a.txt

使用场景

一般来说,只有多人开发的时候才会使用 revert, 因为团队之间可能会不允许修改历史,如果是自己一个人开发的时候使用 revert 就太温柔了,直接用 reset 会方便很多

整理这一章节大概花了三节晚自修吧,直接和大一一起晚自修确实舒服,因为那个教室肯定不用上课(www

上了几天课感觉状态又有点松散,上课的时候既不听课也没法专心做手头的事情,还是晚上这段时间好(买一杯无糖单奶美式瑞幸,拿着心爱的 svp13,带着 sony 塞子 在心爱的 manjaro 上专一的做事

不过效率说不上高吧,应该放点 lofi,今天循环了一晚上的 「bye-bye show」,真的好听啊,BiSH 的最后一首歌(其实下个月的新番有 BiSH 唱的 OP, 所以严格来说是倒数第二首,不过这首算是很正式的告别曲,好像去看一场 live 啊~

这学期的话应该得把双拼学了,spring 学了,准备准备实习的面试

加油!

2023/03/07
> CLICK TO back <