8

我在哪里可以获得有关 git stash 在内部如何工作的信息?我对类似于9.2 Git Internals - Git Objects of git-scm 一书中关于 git 对象的详细解释感兴趣。

编辑:我正在根据从该链接收到的信息更新我的问题。我下面描述的逻辑是否正确?
HEAD 在分支 br1 上。该分支上的最后一次提交“br1-c0”具有以下树:

somefile.txt(里面的文字 - '一些文字') anotherfile.txt

我已修改 somefile.txt 以将“更新的文本”作为内部文本。我正在存储更改:

1)创建一个提交,它具有以下树:somefile.txt(内部文本 - '更新文本') anotherfile.txt 并且具有提交'br1-c0'和文件索引状态的链接。

2) 工作树恢复为“br1-c0”提交。

4

2 回答 2

5

Git 是开源的,所以源代码;)(或谷歌)

在任何情况下,存储都是提交列表。您可以通过创建存储来查看它们是如何构建的:

 # git stash --keep-index
 # git stash list
 stash@{0}: WIP on master: dafe337 sss
 # git log 'stash@{0}' | cat
 commit 7f86a90fb4e57590d6fe5026b7408306a757132a
 Merge: dafe337 2881ede
 Author: Maciej Piechotka <uzytkownik2@gmail.com>
 Date:   Fri Aug 30 09:27:10 2013 +0200

     WIP on master: dafe337 sss

 commit 2881ede55d619570a82bb7312257c4e43bd3b334
 Author: Maciej Piechotka <uzytkownik2@gmail.com>
 Date:   Fri Aug 30 09:27:10 2013 +0200

     index on master: dafe337 sss

 commit dafe33716c2e5aee994612c88d8142f1163c624e
 Author: Maciej Piechotka <uzytkownik2@gmail.com>
 Date:   Fri Aug 30 09:25:40 2013 +0200

     sss

Sss 是第一次提交(HEAD),其余两次提交是保存当前索引(分阶段更改),合并包含未分阶段更改:

% git show 2881ede55d619570a82bb7312257c4e43bd3b334
commit 2881ede55d619570a82bb7312257c4e43bd3b334
Author: Maciej Piechotka <uzytkownik2@gmail.com>
Date:   Fri Aug 30 09:27:10 2013 +0200

    index on master: dafe337 sss

diff --git a/test.c b/test.c
index b9a1dd0..7beafd5 100644
--- a/test.c
+++ b/test.c
@@ -1 +1,2 @@
 dddd
+fff
% git show 7f86a90fb4e57590d6fe5026b7408306a757132a
commit 7f86a90fb4e57590d6fe5026b7408306a757132a
Merge: dafe337 2881ede
Author: Maciej Piechotka <uzytkownik2@gmail.com>
Date:   Fri Aug 30 09:27:10 2013 +0200

    WIP on master: dafe337 sss

diff --cc test.c
index b9a1dd0,7beafd5..551a609
--- a/test.c
+++ b/test.c
@@@ -1,1 -1,2 +1,3 @@@
  dddd
+ fff
++ggg

现在 stash 列表是一个现有结构 - reflog(nb本身有用的结构),名称是... stash。所以 stash 实际上是作为一个带有移动头的分支实现的,我们感兴趣的是 reflog。为了让它更有趣,我创建了第二个 stash,它创建了 commit 0dee308c461955e13a864c9a904a69d611e82730

% git reflog stash | cat
7f86a90 stash@{0}: WIP on master: dafe337 sss
% cat .git/refs/stash
0dee308c461955e13a864c9a904a69d611e82730
% cat .git/logs/refs/stash
0000000000000000000000000000000000000000 7f86a90fb4e57590d6fe5026b7408306a757132a Maciej Piechotka <uzytkownik2@gmail.com> 1377847630 +0200 WIP on master: dafe337 sss
7f86a90fb4e57590d6fe5026b7408306a757132a 0dee308c461955e13a864c9a904a69d611e82730 Maciej Piechotka <uzytkownik2@gmail.com> 1377847983 +0200 WIP on master: dafe337 sss
于 2013-08-30T07:37:01.047 回答
0

我写了一篇博客文章Git Stash Internals与我对这个主题的理解。
我在下面分享它的要点,希望这可以帮助你和其他人。欢迎反馈。

git status

On branch master
Changes to be committed:
    (use "git restore --staged <file>..." to unstage)
                modified:   CONTRIBUTING.md

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:   README.md

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

有3个部分:

  • Changes to be committed表示 索引的内容。( CONTRIBUTING.md)
  • Changes not staged for commit表示在工作目录中 修改的跟踪文件。( )README.md
  • Untracked files表示 git 还不知道的文件 ( LICENSE)

在这个例子中,我假设只创建了一个存储并且我stash@{0}用来引用它。

下表描述了根据使用的 git stash 命令隐藏了哪些文件以及在哪里(提交)。

Git 存储命令 修改后的跟踪文件(工作目录) 指数 未跟踪的文件 忽略
README.md CONTRIBUTING.md LICENSE temp/stash.out
git stash ☑️ stash@{0} ☑️ stash@{0}^2 ⤬ </td> ⤬</td>
git stash -u ☑️ stash@{0} ☑️ stash@{0}^2 ☑️ stash@{0}^3 ⤬</td>
git stash -a ☑️ stash@{0} ☑️ stash@{0}^2 ☑️ stash@{0}^3 ☑️ stash@{0}^3

现在,让我们看一下每个git stash命令的作用。

git stash

默认情况下,git stash预留:

  • 任何被修改且未被忽略的跟踪文件:README.md
  • 指数:CONTRIBUTING.md

它不会分别保留未跟踪或忽略的文件,例如LICENSEtemp/stash.out.

git status

On branch master
Your branch is up-to-date with 'origin/master'.
Untracked files:
    (use "git add <file>..." to include in what will be committed)

    LICENSE

nothing added to commit but untracked files present (use "git add" to track)

相反,如果我想存储已跟踪和未跟踪的文件,这就是我当前正在进行的所有工作(README.mdLICENSE)加上索引的内容(CONTRIBUTING.md),那么我需要使用git stash -u,如下所示:

git stash -u
git status

On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

要同时存储被忽略的文件,您可以git stash -a改用。

存储堆栈

添加 stash 时,git 创建一个stash commit,将其推送到 stash 堆栈的顶部。这会将现有的存储条目向下移动(如果有)。引用stash@{0}始终表示存储堆栈的顶部。每次您存放其他东西时,它都会被向下推,因此:

  • stash@{0}表示最近创建的存储,
  • stash@{1}表示创建的倒数第二个存储,
  • stash@{2}表示创建的倒数第三个存储,依此类推。

现在我们知道了 stashed 是什么,让我们看一下它在内部的存储方式。

现在我们已经设置好了场景,让我们来看看事情是如何在幕后工作的。

藏匿处有什么?

让我们弄清楚我们最近的存储库中有什么。
本节假设我们git stash -u在示例的存储库中运行,所以我们最终得到了这个日志。

git stash - 日志

现在让我们看一下 stash@{0} 提交。

git log --format=raw -1 stash@{0}

commit 49482afa4ab999deada67c65dc5d38be89aed867
tree 936c8b08ac5a8e91bb6cc38387d2cca93167e0ae
parent 031ca106c13b1603675ea1ce8da8b3da852e27cd
parent b558b9e7621fe508c7c18713cd62c78e80e2017e
parent dfac0d769262fa4b8ea40003d24052c4509a7f3a
author Eric Bouchut <ebouchut@gmail.com> 1627056522 +0200
committer Eric Bouchut <ebouchut@gmail.com> 1627056522 +0200

    WIP on master: 031ca10 Add README

请注意,存储提交的父级按顺序列出(第一个 ( 031ca10),然后是第二个 ( b558b9e),然后是第三个 ( dfac0d7))。

git stash 提交父母

在这种情况下,隐藏提交stash@{0}( 49482a) 是与 3 个父级的合并提交,因为我们隐藏了未跟踪的文件(默认为 2 个父级)。
它还包含在存储时修改的工作目录的非忽略文件。

让我们见见家长:

  • stash@{0}^1( 031ca10) 表示存储提交的第一个父级。
    这是存储时的当前提交 ( HEAD)。
  • stash@{0}^2( b558b9e) 表示存储提交的第二个父级。
    它包含存储时索引中存在的变更集。索引又名
    。作为集结地。这是您添加的文件在提交之前存储的位置。git add
  • stash@{0}^3( dfac0d7) 表示存储提交的第三个父级。
    它包含存储时工作树中存在的未跟踪文件( -u) 和忽略文件( )。仅当您使用任何or选项时才创建它。-a
    git stash-u-a

为什么我们需要深入研究它的内部运作git stash

直到 2.32 版本,git 没有提供一种简单的方法来列出和显示隐藏提交中未跟踪的文件。这就是为什么我们需要了解git stash内部结构才能做到这一点。您现在已准备好了解接下来会发生什么。

存储提交的工作目录中的修改文件

以下是如何在最近的存储提交的工作目录中列出修改的文件:

 git log -m --first-parent -1  --format='' --name-only 'stash@{0}'

在这里,我们深入研究合并提交 ( -m) 并仅关注第-1一个父 ( ) 的第一个提交 ( --first-parent),即存储提交本身。

ℹ️ 默认情况下,git log 不显示有关合并提交的任何父级的详细信息,除非我们使用-m并且当我们使用此选项时,它会显示每个父级的请求。由于这不是我们想要的,我们只限制第一个父母。

无论出于何种原因,即使使用--name-onlygit log 除了文件名外,也会显示非请求信息(提交 SHA1、日期和作者)。我在 git 版本 2.32.0 中注意到了这个问题。这就是为什么我--format=''用作解决方法来删除它们。

现在,这里是如何查看 最近一次存储提交的工作目录的修改文件中的更改:

git log -m --first-parent -1   -p 'stash@{0}'


# Stashed Files of a Stash Commit

The command below **lists** the **staged files** of the most recent stash commit.

```lang-shell
git log  --name-only -1 --format='' 'stash@{0}^2'

为了 在此存储提交中获取(更改集中的)索引的内容:

git log  -1  -p 'stash@{0}^2'

隐藏提交的未跟踪文件

以下是如何在最近的存储提交中列出跟踪的文件。

git 版本 2.32 开始git show,现在可以--only-untracked选择列出存储的未跟踪文件。

ℹ️ 如果您曾经git stash -a也存储被忽略的文件,这也会列出被忽略的文件。

git stash show --only-untracked --name-only 'stash@{0}'

在 git 版本 2.32 之前,我们应该使用以下两种替代方法之一:

git show --name-only 'stash@{0}^3:'

请注意末尾的冒号 ( :)。

git ls-tree -r 'stash@{0}^3' --name-only

以下是如何在最近的存储提交中查看未跟踪文件(以及忽略的文件(如果有))的内容。

git 版本 2.32开始,您可以 --only-untracked使用git show.

git stash show --only-untracked -p 'stash@{0}'

在 git 版本 2.32 之前,请改用:

git log -p 'stash@{0}^3'
于 2021-08-10T13:10:26.280 回答