git stash实现原理

一、git stash的man手册中对该命令的说明

git需要保存的内容:
1、本地修改并且已经通过git add添加到缓存区的
2、本地修改但是还没有添加到缓存区的
3、可能还包括本地添加但是没有track的

DISCUSSION
A stash is represented as a commit whose tree records the state of the working directory, and its first parent is the commit at HEAD when the stash was created. The tree of the
second parent records the state of the index when the stash is made, and it is made a child of the HEAD commit. The ancestry graph looks like this:

.----W
/ /
-----H----I

where H is the HEAD commit, I is a commit that records the state of the index, and W is a commit that records the state of the working tree.

二、当前git状态

当前文件夹中,add.txt文件已经通过git add添加到了暂存区,test.txt文件在本地进行了修改但是还没有添加到暂存区,而untracked.txt是新添加的文件并且还没有通过git add添加到暂存区。其中head中的内容直接指向的就是这个版本库中对test.txt的第一次修改。
tsecer@harry: git status
On branch master
Your branch is based on 'origin/master', but the upstream is gone.
(use "git branch --unset-upstream" to fixup)

Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: add.txt

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: test.txt

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

tsecer@harry: cat .git/refs/heads/master
c4a4b6388fe600fdc3bfde958b6c0dee9ff82b6f
tsecer@harry:

三、把当前内容stash

1、执行stash之后新生成的object

这里要注意到的是head的内容并没有变化,但是在stash中增加了一个新的文件内容。
tsecer@harry: git stash -u
Saved working directory and index state WIP on master: c4a4b63 commit first test.txt
tsecer@harry: cat .git/refs/heads/master
c4a4b6388fe600fdc3bfde958b6c0dee9ff82b6f
tsecer@harry: cat .git/refs/stash
9610b5ac0053e646ff30b59bdfd30220c60019f3
tsecer@harry:

2、生成object的内容

看下新stash中内容,可以看到stash中对应的是一次提交,它有三个父节点,一个是当前的HEAD提交,
tsecer@harry: git cat-file -p 9610b5ac0053e646ff30b59bdfd30220c60019f3
tree b2cb409c5f82d45c6a4df10935f50c385f9154cc
parent c4a4b6388fe600fdc3bfde958b6c0dee9ff82b6f
parent e0ebcb0cda7ad9ce49710396c5b2bed54b055b87
parent f5568b19b7fa78b7fbe593d2f6338e12c1593379
author tsecer <tsecer@harry> 1593055039 +0800
committer tsecer <tsecer@harry> 1593055039 +0800

WIP on master: c4a4b63 commit first test.txt

stash提交的两个父节点的内容 ,可以看到日志中提示第一个是index文件的内容,从注释上看,这个index文件包含的是当前index内容,其实也就是暂存区stage中的内容,对应的就是已经git add但是没有commit的内容
tsecer@harry: git cat-file -p e0ebcb0cda7ad9ce49710396c5b2bed54b055b87
tree 97dea04cf45b6cf424030c30a473fa3421e4b582
parent c4a4b6388fe600fdc3bfde958b6c0dee9ff82b6f
author tsecer <tsecer@harry> 1593055039 +0800
committer tsecer <tsecer@harry> 1593055039 +0800

index on master: c4a4b63 commit first test.txt
这个自动生成的提交日志也描述这个地方是本地新添加但是还没有git add的untracked文件。
tsecer@harry: git cat-file -p f5568b19b7fa78b7fbe593d2f6338e12c1593379
tree 3cda92c897126e3a8303c6e9007677752d1ff03b
author tsecer <tsecer@harry> 1593055039 +0800
committer tsecer <tsecer@harry> 1593055039 +0800

untracked files on master: c4a4b63 commit first test.txt
tsecer@harry:

3、缺少的内容

这里其实还缺少一个,就是本地修改但是未提交的内容,事实上,由于新生成的提交包括除HEAD外两个父节点,加上这次提交本身,所以总共包含了三个部分,分别对应“已添加” “未跟踪” 和“已修改”三种类型的stash。

四、详细看下三个提交内容

1、自动生成的提交本身

可以看到,它包含了当前工作区中所有修改内容,不管是不是已经通过git add添加到暂存区。
其树节点为b2cb409c5f82d45c6a4df10935f50c385f9154cc,内容为
tsecer@harry: git ls-tree b2cb409c5f82d45c6a4df10935f50c385f9154cc
100644 blob 6f9e7afb3afe34b7896d73dfec5885032997fa69 add.txt
100644 blob 5664e262760bcfb014eb7dd19a6aa1241cd360f2 test.txt
tsecer@harry: git cat-file -p 6f9e7afb3afe34b7896d73dfec5885032997fa69
added to stage
tsecer@harry: git cat-file -p 5664e262760bcfb014eb7dd19a6aa1241cd360f2
0
1
2
3
4
5
6
7
8
9
00
11
22
33
44
55
66
77
88
99

2、第一个父节点

这个节点包含的是当前暂存区index中的完成内容,可以看到,其中保存的test.txt还是首次提交的内容,也就是和HEAD版本中内容一致。
tsecer@harry: git cat-file -p e0ebcb0cda7ad9ce49710396c5b2bed54b055b87
tree 97dea04cf45b6cf424030c30a473fa3421e4b582
parent c4a4b6388fe600fdc3bfde958b6c0dee9ff82b6f
author tsecer <tsecer@harry> 1593055039 +0800
committer tsecer <tsecer@harry> 1593055039 +0800

index on master: c4a4b63 commit first test.txt
tsecer@harry: git ls-tree 97dea04cf45b6cf424030c30a473fa3421e4b582
100644 blob 6f9e7afb3afe34b7896d73dfec5885032997fa69 add.txt
100644 blob 8b1acc12b635c26f3decadeaa251729d3ce512e9 test.txt
tsecer@harry: git cat-file -p 6f9e7afb3afe34b7896d73dfec5885032997fa69
added to stage
tsecer@harry: git cat-file -p 8b1acc12b635c26f3decadeaa251729d3ce512e9
0
1
2
3
4
5
6
7
8
9

3、第二个父节点

这个包含的是本地新添加的但是没有通过add添加到暂存区的文件,这个是git stash的-u选项生成。
tsecer@harry: git cat-file -p f5568b19b7fa78b7fbe593d2f6338e12c1593379
tree 3cda92c897126e3a8303c6e9007677752d1ff03b
author tsecer <tsecer@harry> 1593055039 +0800
committer tsecer <tsecer@harry> 1593055039 +0800

untracked files on master: c4a4b63 commit first test.txt
tsecer@harry: git ls-tree 3cda92c897126e3a8303c6e9007677752d1ff03b
100644 blob d1b880281d1432605cc99b212557c85518f703b5 untracked.txt
tsecer@harry: git cat-file -p d1b880281d1432605cc99b212557c85518f703b5
untrack
tsecer@harry:

五、源代码中的数据结构

其中的w对应的就是stash文件中显示的内容,b_commit对应HEAD中内容,i_commit对应当前暂存区index内容,w_tree则对应当前工作目录中文件内容。
git-master\builtin\stash.c
/*
* w_commit is set to the commit containing the working tree
* b_commit is set to the base commit
* i_commit is set to the commit containing the index tree
* u_commit is set to the commit containing the untracked files tree
* w_tree is set to the working tree
* b_tree is set to the base tree
* i_tree is set to the index tree
* u_tree is set to the untracked files tree
*/
struct stash_info {
struct object_id w_commit;
struct object_id b_commit;
struct object_id i_commit;
struct object_id u_commit;
struct object_id w_tree;
struct object_id b_tree;
struct object_id i_tree;
struct object_id u_tree;
struct strbuf revision;
int is_stash_ref;
int has_u;
};

 

posted on 2020-07-04 16:23  tsecer  阅读(971)  评论(0编辑  收藏  举报

导航