【Git 学习笔记_22】Git 实用冷门操作技巧(上)

第十一章 Git 操作技巧与诀窍

本章相关主题:

  • 活用 git stash(上) ✔️
  • 保存并应用 stash(上) ✔️
  • git bisect 进行调试
  • 使用 git blame 命令
  • 设置彩色命令行界面
  • 自动补全
  • 让 Bash 自带状态信息
  • 更多别名
  • 交互式新增提交
  • 用 Git 的图形界面交互式新增
  • 忽略文件
  • 展示与清理忽略文件

简介

本章将重点梳理 Git 的使用技巧,以便用于日常工作。例如在执行重要任务时被临时打断需要寄存工作区变更内容,或者使用 bisectblame 命令高效定位 bug,抑或是设置命令行的颜色与状态信息等。此外还将进一步梳理实用别名、根据指定的行级变更创建更清爽的 commit 提交、以及忽略文件的相关操作。

11.1 活用 git stash

本节演示如何使用 git stash 命令来快速寄存未提交的更改并再次读取这些内容。该操作在某项重要任务被临时打断(如紧急修复线上 bug)时非常实用,可以快速保存本地更改,恢复当前工作区;待临时任务结束后再还原之前的工作状态。利用 git stash 命令,还可以在寄存本地更改时设置是否保存暂存区(staging area)中的内容。

相关命令:

  • git stash:添加到寄存区(入栈)
  • git stash list:查看寄存区的存放列表
  • git stash pop:取回寄存区的内容(出栈)

实操示例如下:

# repo init
$ git clone https://github.com/PacktPublishing/Git-Version-Control-Cookbook-Second-Edition_tips_and_tricks.git tiptrick
$ cd tiptrick
$ git checkout master
# do some changes
$ echo "Just another unfinished line" >> foo
$ git add foo
$ echo "Another line" >> bar
$ echo "Some content" > new_file
$ git status -s

git status -s

可见,git stash 只对暂存区及加入版本管理的文件生效(new_file 未被寄存)。

# using git stash
$ git stash
Saved working directory and index state WIP on master: b6dabd7 Update foo and bar
$ git status -s
?? new_file

查看 gitk

$ gitk master stash

结果如下:

gitk stash result

图 11-1 放入寄存区后的分支状态

foo 文件作如下修改:

# on Linux (BSD sed)
$ sed -i 's/First line/This is the very first line of the foo file/' foo
$ git add foo
$ git commit -m "Update foo"
[master 3646ef9] Update foo
 1 file changed, 1 insertion(+), 1 deletion(-)
# check stashed contents
$ git stash list
stash@{0}: WIP on master: b6dabd7 Update foo and bar

实测发现,Ubuntu 环境下 sed 命令为 sed -i 's/.../.../' foo

此时再查看分支状态:

$ gitk --reflog

结果如下:

result for gitk --reflog

图 11-2 完成临时任务并提交后的分支状态

完成临时任务后,取回 stash 命令寄存的内容如下:

$ git status -s
?? new_file
$ git stash pop
Auto-merging foo
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
  (use "git push" to publish your local commits)

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   bar
        modified:   foo

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

no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (c008d2f149d0827cbc06879b3097bd690128e260)

值得注意的是,虽然原来暂存区的变更也一并存入了寄存区,但还原时 git 默认只把工作区内的变更还原了。

原理分析

上述演示中,foo 文件的一处修改先放到了寄存区;然后在处理临时任务时,对该文件的另一处内容作了修改并提交;之后再将寄存的变更重新还原到 foo 上,由于没有版本冲突,Git 作了自动合并。

根据 图 11-1 可知,加入寄存区后,Git 分别为索引区(index)及工作区(work area)创建了 commit 节点;这在临时任务提交后也可以佐证,如 图 11-2 所示。

stash 创建的 commit 对象存放在 refs/stash 命名空间下。其中,包含原暂存区信息的 commit 节点,称为 index on master(主分支上的索引信息);另一个包含工作区信息的节点,叫 WIP on master(主分支上正在进行中的工作信息,WIPWork In Progress 的缩写)。这些临时创建的节点信息,可以通过 git 的常规操作将更改重新还原到工作目录下。也就是说,一旦还原过程中发生版本冲突,也可以通过 git 处理冲突的常规流程进行处理。

知识拓展

通过刚才的演示得知,默认情况下,git 寄存或还原当前工作状态时,存在两个问题:

  • 未加入版本控制的文件不会自动 stash 到寄存区;
  • 还原只对工作区生效,尽管寄存了原暂存区的变更;

这两个问题都可以通过手动指定参数来解决。前者使用 git stash --include-untracked 解决;后者通过 git stash pop --index 解决:

$ git clone https://github.com/PacktPublishing/Git-Version-Control-Cookbook-Second-Edition_tips_and_tricks.git tiptrick
$ cd .\tiptrick\
$ echo "Just another unfinished line" >> foo; git add foo; echo "Another line" >> bar;echo "Some content" > new_file;
$ git stash --include-untracked
Saved working directory and index state WIP on master: b6dabd7 Update foo and bar
# Check untracked file (not existed):
$ git status -s
# Check gitk with 'stash' branch
$ gitk stash

查看结果如下:(Gitstash 上又新增一个节点来单独存放 不在版本控制内的 内容变更)

git stash --include-untracked

继续后面的操作,处理并提交临时任务,只是在还原寄存区的变更时,将原暂存区的变更也还原:

$ sed -i 's/First line/This is the very first line of the foo file/' foo
$ git add foo; git commit -m 'Update foo';
$ gitk --reflog

再次查看还原前的分支状态:

new version of gitk --reflog

这时,需要将原暂存区的变更内容一并还原到之前的状态:

$ git stash pop --index
$ git status -s
 M bar
M  foo
?? new_file
$ 

result for git stash pop --index

注意对比之前没加 --index 的输出内容。再次查看 gitk --reflog 予以验证,可以看到还原后的状态中,暂存区不同了:

gitk --reflog

此外,还可以在放入寄存区时,手动指定是否需要保留暂存区的变更:在执行 git stash 时通过标记 --keep-index 实现(也可以加 --include-untracked 包含不受版本控制的变更):

$ git clone https://github.com/PacktPublishing/Git-Version-Control-Cookbook-Second-Edition_tips_and_tricks.git tiptrick
$ cd .\tiptrick\
$ echo "Just another unfinished line" >> foo; git add foo; echo "Another line" >> bar;echo "Some content" > new_file
# keep changes in staging area 
# while stashing away untracked changes, too:
$ git stash --keep-index --include-untracked
Saved working directory and index state WIP on master: b6dabd7 Update foo and bar
# Check result (as expected)
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   foo

11.2 保存并应用 stash

寄存当前工作状态时,寄存区可能会有出现寄存状态,而默认的名称并不能区分要恢复的版本具体是哪个。本节将对这种情况进行处理,演示怎样给寄存区的存放记录命名,并恢复指定的版本:

# init repo
$ git clone https://github.com/PacktPublishing/Git-Version-Control-Cookbook-Second-Edition_tips_and_tricks.git tiptrick
$ cd .\tiptrick\
$ echo "Just another unfinished line" >> foo; git add foo; echo "Another line" >> bar;echo "Some content" > new_file
$ git stash --keep-index --include-untracked
Saved working directory and index state WIP on master: b6dabd7 Update foo and bar
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   foo
$ git stash list
stash@{0}: WIP on master: b6dabd7 Update foo and bar
# create stash and name the stash item
$ git stash save 'Updates to foo'
Saved working directory and index state On master: Updates to foo
$ git stash list
stash@{0}: On master: Updates to foo
stash@{1}: WIP on master: b6dabd7 Update foo and bar
# Create a new stash by changing bar:
$ echo "Another change" >> bar
$ git stash save 'Made another change to bar'
Saved working directory and index state On master: Made another change to bar
# check stash list
$ git stash list
stash@{0}: On master: Made another change to bar
stash@{1}: On master: Updates to foo
stash@{2}: WIP on master: b6dabd7 Update foo and bar

解决了寄存记录的命名问题,就可以在应用寄存信息的同时保留该记录:

# apply stash info
$ git stash apply 'stash@{1}'
# use --quiet to suppresses the status output
$ git stash apply --quiet 'stash@{0}'
$ git stash list
stash@{0}: On master: Made another change to bar
stash@{1}: On master: Updates to foo
stash@{2}: WIP on master: b6dabd7 Update foo and bar

可以看到,寄存列表在成功还原后仍在存在。应用某个寄存区的记录,语法为:git stash apply stash@{stash-no}

发散

寄存列表如何删除呢?执行命令 git stash drop stash@{stash-no} 即可:

$ git stash list
stash@{0}: On master: Made another change to bar
stash@{1}: On master: Updates to foo
stash@{2}: WIP on master: b6dabd7 Update foo and bar
$ git stash drop 'stash@{1}'
Dropped stash@{1} (ef0c0e7de00803e61f7a2a02df581bedb143778b)
$ git stash list
stash@{0}: On master: Made another change to bar
stash@{1}: WIP on master: b6dabd7 Update foo and bar

对比之前的默认版本,手动应用或删除寄存区版本提供了更多的灵活性,可以避免一个操作失误。默认情况下,成功 stash pop 一个版本后,该版本会从寄存区自动删除;但若还原过程受阻(如发生冲突)则会被保留下来,即便用户处理了这个冲突,寄存区的版本仍不会自动删除。这就为后续的版本还原埋下了隐患。而手动控制应用或删除,就可以很好地解决这个问题。

小试牛刀

git stash 命令也能把调式信息应用到项目中。比如在调式代码过程中,为了定位 bug 而在项目中加入了不少调式用的语句,调试结束后不必手动删除所有的调试语句。相反,可以将这些语句通过 git stash save "Debug info stash" 寄存到 git 中并命名。这样在今后再次调试时,可以应用该寄存版快速初始化上一次的调试环境。

  • 9
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安冬的码畜日常

您的鼓励是我持续优质内容的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值