首页 > 其他分享 >Learn Git in 30 days——第 13 天:暂存工作目录与索引的变更状态

Learn Git in 30 days——第 13 天:暂存工作目录与索引的变更状态

时间:2023-09-07 20:24:49浏览次数:50  
标签:13 git demo 30 stash Git commit txt 暂存

写的非常好的一个Git系列文章,强烈推荐

原文链接:https://github.com/doggy8088/Learn-Git-in-30-days/tree/master/zh-cn

有没有遇过这种情境,某个系统开发写到一半,结果被老板或客戶「插单」,被要求紧急修正一个现有系统的 Bug 或添加一个功能,眼前的程序即将完成,老板的「急件」又不能拖,一个未完成的软件开发状态外加紧急调整的需求,这简直是软件品质的一大考验。如果你有这种困扰,那么 Git 可以漂亮的帮你完成任务。

认识 git stash 指令

我们知道使用 Git 版控的时候,有区分「工作目录」与「索引」。工作目录里面会有你改到一半还没改完的文件(尚未加入索引),也有新增文件但还没加入的文件(尚未加入索引)。而放在索引的资料,则是你打算通过 git commit 建立版本 (建立 commit 物件) 的内容。

当你功能开发到一半,被紧急插单一定手忙脚乱,尤其是手边正改写到一半的那些代码不知该如何是好。在 Git 里有个 git stash 指令,可以自动帮你把改写到一半的那些文件建立一个「特殊的版本」(也是一个 commit 物件),我们称这些版本为 stash 版本,或你可以直接称他为「暂存版」。

建立暂存版本

我们手边改到一半的文件,可能会有以下状态:

  • 新增文件 (尚未列入追踪的文件) (untracked files)
  • 新增文件 (已经加入索引的文件) (tracked/staged files)
  • 修改文件 (尚未加入索引的文件) (tracked/unstaged files)
  • 修改文件 (已经加入索引的文件) (tracked/staged files)
  • 删除文件 (尚未加入索引的文件) (tracked/unstaged files)
  • 删除文件 (已经加入索引的文件) (tracked/staged files)

若要将这些开发到一半的文件建立一个「暂存版」,你有两个选择:

  • git stash 会将所有已列入追踪(tracked)的文件建立暂存版
  • git stash -u 会包括所有已追踪或未追踪的文件,全部都建立成暂存版

注: git stash 也可以写成 git stash save,两个指令的结果是一样的,只是 save 参数可以忽略不打而已。

我们来看看一个简单的例子。我们先通过以下指令快速建立一个拥有两个版本的 Git 仓库与工作目录:

mkdir git-stash-demo
cd git-stash-demo
git init

echo. > a.txt
git add .
git commit -m "Initial commit"

echo 1 > a.txt
git add .
git commit -m "a.txt: set 1 as content"
 

目前的「工作目录」是干净的,没有任何更新到一半的文件:

C:\git-stash-demo>git log
commit 95eff6b19a9494667985ed5da37427bb08b8cdd7
Author: Will <[email protected]>
Date:   Fri Oct 11 08:17:15 2013 +0800

    a.txt: set 1 as content

commit 346fadefdd6ed2c562201b5ca37d1e4d26b26d54
Author: Will <[email protected]>
Date:   Fri Oct 11 08:17:14 2013 +0800

    Initial commit

C:\git-stash-demo>git status
# On branch master
nothing to commit, working directory clean

C:\git-stash-demo>dir
 磁盘区 C 中的磁盘是 System
 磁盘区序号:  361C-6BD6

 C:\git-stash-demo 的目录

2013/10/11  上午 08:17    <DIR>          .
2013/10/11  上午 08:17    <DIR>          ..
2013/10/11  上午 08:17                 4 a.txt
               1 个文件               4 位元组
               2 个目录   9,800,470,528 位元组可用
 

接着我新增一个 b.txt,再将 a.txt 的内容改成 2,如下:

C:\git-stash-demo>type a.txt
1

C:\git-stash-demo>echo 2 > a.txt

C:\git-stash-demo>type a.txt
2

C:\git-stash-demo>echo TEST > b.txt

C:\git-stash-demo>dir
 磁盘区 C 中的磁盘是 System
 磁盘区序号:  361C-6BD6

 C:\git-stash-demo 的目录

2013/10/11  上午 08:55    <DIR>          .
2013/10/11  上午 08:55    <DIR>          ..
2013/10/11  上午 08:54                 4 a.txt
2013/10/11  上午 08:55                 7 b.txt
               2 个文件              11 位元组
               2 个目录   9,704,288,256 位元组可用

C:\git-stash-demo>git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   a.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b.txt
no changes added to commit (use "git add" and/or "git commit -a")
 

现在我们用 git status 得出我们有两个文件有变更,一个是 a.txt 处于 "not staged" 状态,而 b.txt 则是 "untracked" 状态。

这时,我们利用 git stash -u 即可将目前这些变更全部储存起来 (包含 untracked 文件),储存完毕后,这些变更全部都会被重置,新增的文件会被删除、修改的文件会被还原、删除的文件会被加回去,让我们目前在工作目录中所做的变更全部回复到 HEAD 状态。这就是 Stash 帮我们做的事。如下所示:

C:\git-stash-demo>git stash -u
Saved working directory and index state WIP on master: 95eff6b a.txt: set 1 as c
ontent
HEAD is now at 95eff6b a.txt: set 1 as content

C:\git-stash-demo>git status
# On branch master
nothing to commit, working directory clean
 

在建立完成「暂存版」之后,Git 会顺便帮我们建立一个暂存版的「参考名称」,而且是「一般参考」,在 .git\refs\stash 储存的是一个 commit 物件的「绝对名称」:

C:\git-stash-demo>dir .git\refs\
 磁盘区 C 中的磁盘是 System
 磁盘区序号:  361C-6BD6

 C:\git-stash-demo\.git\refs 的目录

2013/10/11  上午 08:57    <DIR>          .
2013/10/11  上午 08:57    <DIR>          ..
2013/10/11  上午 08:57    <DIR>          heads
2013/10/11  上午 08:57                41 stash
2013/10/11  上午 08:17    <DIR>          tags
               1 个文件              41 位元组
               4 个目录   9,701,650,432 位元组可用
 

我们用 git cat-file -p stash 即可查出该物件的内容,这时你可以发现它其实就是个具有三个 parent (上层 commit 物件) 的 commit 物件:

C:\git-stash-demo>git cat-file -p stash
tree 86cf41ab650d8d0ce5fdd003bb7b722a917438a2
parent 95eff6b19a9494667985ed5da37427bb08b8cdd7
parent b79c4650e72ad4627d691a2d6cfb192626e24e94
parent 9b4e4a100776783dc76d16c3872235e6314d15e3
author Will <[email protected]> 1381453062 +0800
committer Will <[email protected]> 1381453062 +0800

WIP on master: 95eff6b a.txt: set 1 as content
 

有三个 parent commit 物件的意义就在于,这个特殊的暂存版是从另外三个版本合并进来的,然而这三个版本的内容,我们一样可以通过相同的指令显示其内容:

C:\git-stash-demo>git cat-file -p 95ef
tree eba2ef4205738a5015fc47d9cfe634d7d5eae466
parent 346fadefdd6ed2c562201b5ca37d1e4d26b26d54
author Will <[email protected]> 1381450635 +0800
committer Will <[email protected]> 1381450635 +0800

a.txt: set 1 as content

C:\git-stash-demo>git cat-file -p b79c
tree eba2ef4205738a5015fc47d9cfe634d7d5eae466
parent 95eff6b19a9494667985ed5da37427bb08b8cdd7
author Will <[email protected]> 1381453061 +0800
committer Will <[email protected]> 1381453061 +0800

index on master: 95eff6b a.txt: set 1 as content

C:\git-stash-demo>git cat-file -p 9b4e
tree b583bfe854b66756dd0f8ee96cab0c898193b5fd
author Will <[email protected]> 1381453062 +0800
committer Will <[email protected]> 1381453062 +0800

untracked files on master: 95eff6b a.txt: set 1 as content
 

从上述执行结果你应该可以从「消息记录」的地方清楚看出这三个版本分別代表那些内容:

  1. 原本工作目录的 HEAD 版本
  2. 原本工作目录里所有追踪中的内容 (在索引中的内容)
  3. 原本工作目录里所有未追踪的内容 (不在索引中的内容)

也就是说,他把「原本工作目录的 HEAD 版本」先建立两个暂时的分支,这两个分支分別就是「原本工作目录里所有追踪中的内容」与「原本工作目录里所有未追踪的内容」之用,并在个別分支建立了一个版本以产生 commit 物件并且给予预设的 log 内容。最后把这三个分支,合并到一个「参照名称」为 stash 的版本 (这也是个 commit 物件)。不仅如此,他还把整个「工作目录」强迫重置为 HEAD 版本,把这些变更与新增的文件都给还原,多的文件也会被移除。

取回暂存版本

由于「工作目录」已经被重置,所以变更都储存到 stash 这里,哪天如果你想要把这个暂存文件取回,就可以通过 git stash pop 重新「合并」回来。如下所示:

C:\git-stash-demo>git status
# On branch master
nothing to commit, working directory clean

C:\git-stash-demo>git stash pop
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   a.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b.txt
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (0e5b72c96fcf693e0402c40cd58f980bb3ff7efd)
 

执行完毕后,所有当初的工作目录状态与索引状态都会被还原。事实上 Git 骨子里是通过「合并」的功能把这个名为 stash 的版本给合并回目前分支而已。最后,它还会自动将这个 stash 分支给删除,所以称它为【暂存版】非常贴切!

建立多重暂存版

Git 的 stash 暂存版可以不只一份,你也可以建立多份暂存档,以供后续使用。不过,在正常的开发情境下,通常不会有太多暂存版才对,会有这种情況发生,主要有两种可能:

  1. 你的开发习惯太差,导致累积一堆可能用不到的暂存版。
  2. 你老板或客戶「插单」的问题十分严重,经常改到一半就被迫插单。(这就是身为 IT 人的 BI 啊~~~XD) (BI = Business Intelligence 或另一层意思... Well, you know....)

我们延续上一个例子,目前工作目录的状态应该是有两个文件有变化,我们用 git status -s 取得工作目录的状态(其中 -s 代表显示精简版的状态):

C:\git-stash-demo>git status -s
 M a.txt
?? b.txt
 

现在,我们先建立第一个 stash 暂存版:

C:\git-stash-demo>git stash save -u
Saved working directory and index state WIP on master: 95eff6b a.txt: set 1 as content
HEAD is now at 95eff6b a.txt: set 1 as content
 

然后通过 git stash list 列出目前所有的 stash 清单,目前仅一份暂存版:

C:\git-stash-demo>git stash list
stash@{0}: WIP on master: 95eff6b a.txt: set 1 as content
 

而且你可以看到建立暂存版之后,工作目录是干净的。此时我们在建立另一个 new.txt 文件,并且再次建立暂存版:

C:\git-stash-demo>git status -s

C:\git-stash-demo>echo 1 > new.txt

C:\git-stash-demo>git status -s
?? new.txt

C:\git-stash-demo>git stash save -u
Saved working directory and index state WIP on master: 95eff6b a.txt: set 1 as c
ontent
HEAD is now at 95eff6b a.txt: set 1 as content
 

我们在再一次 git stash list 就可以看到目前有两个版本:

C:\git-stash-demo>git stash list
stash@{0}: WIP on master: 95eff6b a.txt: set 1 as content
stash@{1}: WIP on master: 95eff6b a.txt: set 1 as content
 

你应该会很纳闷,都没有自订的注解,过了几天不就忘记这两个暂存档各自的修改项目吗?没错,所以你可以自订「暂存版」的记录消息。我们通过 git stash save -u <message> 指令,就可自订暂存版的注解:

C:\git-stash-demo>git stash -h
usage: git core\git-stash list [<options>]
   or: git core\git-stash show [<stash>]
   or: git core\git-stash drop [-q|--quiet] [<stash>]
   or: git core\git-stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]
   or: git core\git-stash branch <branchname> [<stash>]
   or: git core\git-stash [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
                       [-u|--include-untracked] [-a|--all] [<message>]]
   or: git core\git-stash clear

C:\git-stash-demo>git stash pop
Already up-to-date!
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       new.txt
nothing added to commit but untracked files present (use "git add" to track)
Dropped refs/stash@{0} (5800f37937aea5fb6a1aba0d5a1598a940e70c96)

C:\git-stash-demo>git stash save -u "新增 new.txt 文件"
Saved working directory and index state On master: 新增 new.txt 文件
HEAD is now at 95eff6b a.txt: set 1 as content

C:\git-stash-demo>git stash list
stash@{0}: On master: 新增 new.txt 文件
stash@{1}: WIP on master: 95eff6b a.txt: set 1 as content
 

这时,如果你直接执行 git stash pop 的话,他会取回最近的一笔暂存版,也就是上述例子的 stash@{0} 这一项,并且把这一笔删除。另一种取回暂存版的方法是通过 git stash apply 指令,唯一差別则是取回该版本 (其实是执行合并动作) 后,该暂存版还会留在 stash 清单上。

如果你想取回「特定一个暂存版」,你就必须在最后指名 stash id,例如 stash@{1} 这样的格式。例如如下范例,我使用 git stash apply "stash@{1}" 取回前一个暂存版,但保留这版在 stash 清单里:

C:\git-stash-demo>git stash list
stash@{0}: On master: 新增 new.txt 文件
stash@{1}: WIP on master: 95eff6b a.txt: set 1 as content

C:\git-stash-demo>git stash apply "stash@{1}"
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   a.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b.txt
no changes added to commit (use "git add" and/or "git commit -a")

C:\git-stash-demo>git stash list
stash@{0}: On master: 新增 new.txt 文件
stash@{1}: WIP on master: 95eff6b a.txt: set 1 as content
 

如果确定合并正确,你想删除 stash@{1} 的话,可以通过 git stash drop "stash@{1}" 将特定暂存版删除。

C:\git-stash-demo>git stash drop "stash@{1}"
Dropped stash@{1} (118cb8a7c0b763c1343599027d79f7b20df57ebf)

C:\git-stash-demo>git stash list
stash@{0}: On master: 新增 new.txt 文件
 

如果想清理掉所有的暂存版,直接下达 git stash clear 即可全部删除。

C:\git-stash-demo>git stash list
stash@{0}: On master: 新增 new.txt 文件

C:\git-stash-demo>git stash clear

C:\git-stash-demo>git stash list
 

今日小结

Git 的 stash (暂存版) 机制非常非常的实用,尤其是在 IT 业界插单严重的工作环境下 (不只台湾这样,世界各地的 IT 业界应该也差不多),这功能完全为我们量身打造,非常的贴心。在 Subversion 里就没有像 Git 这么简单,一个指令就可以把工作目录与索引的状态全部存起来。

本篇文章也试图通过指令了解 stash 的核心机制,其实就是简单的「分支」与「合并」而已,由此可知,整套 Git 版本控制机制,大多是以「分支」与「合并」的架构在运作。

我重新整理一下本日学到的 Git 指令与参数:

  • git stash
  • git stash -u
  • git stash save
  • git stash save -u
  • git stash list
  • git stash pop
  • git stash apply
  • git stash pop "stash@{id}"
  • git stash apply "stash@{id}"
  • git stash drop "stash@{id}"
  • git stash clear

标签:13,git,demo,30,stash,Git,commit,txt,暂存
From: https://www.cnblogs.com/songzhenhua/p/17685947.html

相关文章

  • GitHub workflows env All In One
    GitHubworkflowsenvAllInOne$GITHUB_ENVdocsGITHUB_ENVenvironmentfile#把变量和值`>>`追加到GITHUB_ENV环境变量文件中echo"{environment_variable_name}={value}">>"$GITHUB_ENV"steps:-name:Setthevalueid:step_......
  • 网络错误码 10013 错误问题分析
    前言10013以一种访问权限不允许的方式做了一个访问套接字的尝试。 原因绑定一个处于CLOSE_WAIT状态的端口,会产生该错误重现先找到一个处于CLOSE_WAIT状态的端口49724C:\Users\admin>netstat-ano|findstrCLOSE_WAITTCP192.168.11.149:49724123.60.175.170:80CLO......
  • gitee官网创建仓库和git操作【命令行】
     创建git仓库:  用户信息获取-git获取全局配置gitconfig--list--获取用户名gitconfiguser.name--获取邮箱gitconfiguser.email--创建仓库文件夹mkdirmemorandumcdmemorandum--git初始化gitinit--创建文件touchREADME.md--文件加入git控制gitaddREADME......
  • gitee官网创建仓库和git操作【TortoiseGit小乌龟】
     操作流程:登陆官网创建仓库,本地git克隆后添加文件后提交。没有账号要先注册账号。 官网地址:https://gitee.com/  新建仓库    创建后:  点击“初始化readme文件”按钮。  到本地克隆:    输入gitee的账号密码: 拉取完成: 拉取后本地文......
  • gitee官网创建仓库和git操作【vscode】
    gitee官网创建仓库和git操作【vscode】   创建成功后可以去你的目标路径下,选择git bash设置全局账号信息(第一次需要)。   打开vscode。 Ctrl+shift+p(快捷键) 打开,选择Git:克隆。     回车:  打开 选择是: 然后去路径查看,已经拉取......
  • git私房菜
    文章目录1、公司项目开发Git协作流程2、合并相关的操作3、Git常用命令总结公司中如何使用Git协同开发的?本文将具体介绍开发模式,以及一些常用命令。1、公司项目开发Git协作流程公司一个完整的项目出来,项目的推进是在主分支master上进行的,如下图:整个项目的核心就是master主分支我们......
  • [题解] AtCoder Beginner Contest 308 A~G
    AtCoderBeginnerContest308A~GA.NewSchemevoidMain(){vector<i64>a(8);for(auto&x:a)cin>>x;if(!is_sorted(a.begin(),a.end())&&!all_of(a.begin(),a.end(),[](auto&x){returnx%25!=0||!(100......
  • SI3262是13.56MHZsoc刷卡芯片集成集成刷卡+触摸+ACD超低功耗门锁方案
    13.56mhz刷卡soc芯片SI3262集成刷卡+触摸+ACD超低功耗,ACD模式刷卡距离可达到5cm以上,非常适用于小体积门锁,密码锁,柜锁,接下来介绍一下这款芯片的具体功能。优势1.超低功耗,最低功耗达1.7uA(MCU模块处于掉电模式,读卡器模块处于硬掉电模式);2.典型ACD模式功耗为4.1uA(MCU模块处于掉......
  • 【售价68元】天嵌T113核心板上新
    核心板参数表:CPU架构全志Cortex-A7主频800MHz内存128MBDDR3存储8GB eMMc5.1(4G/16G/32G可选)操作系统Linux电源输入3.1V~5V尺寸38*38mm接口方式邮票孔层数4层无铅工艺LVDS/RGB/DSI支持双路8位LVDS,1080P;RGB,18bit,1080P;支持24bit3、MIPI_DSI,4通道,1080P;LVDS、RGB和MIPI_D......
  • CF1374E2 Reading Books(hard version) 题解
    CF1374E2ReadingBooks(hardversion)这道题是在CF1374E1ReadingBooks(easyversion)的基础上出的,而且仅仅增加了一个\(m\)的限制,下面的做法也是基于简单版的思路而想到的。建议先尝试简单版,然后尝试此题。题意简述:有\(n\)本书,每本书有一个阅读时间\(t_i\),和属性\(......