本文介绍了被“承诺"和“未修改的"相同?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我从

解决方案

TL; DR

Tracking-ness不是所列的三个状态的子集,并且所列的三个状态不足以描述(或真正理解)Git的工作方式.

这三个状态"有点像个谎言,这可能就是页面上说的原因:

(重点是我的).我认为Pro Git本书在这里有一定的弊端,因为我认为他们出于某些良好原因正试图从您的一切初衷中隐藏Git的 index 的存在.但是在同一段中,他们介绍了临时区域的概念,它实际上只是索引的另一个名称.

实际上,这实际上是每个文件通常有三个.当前提交中有一个副本,索引/暂存区中有一个副本,工作树中有第三个副本.

从版本控制系统设计的角度来看,

中间副本(索引中的副本)不是必要的.Mercurial是另一个非常类似于Git的版本控制系统,每个文件只有两个副本:已提交的一个和工作树的一个.这个系统更容易思考和解释.但是出于各种原因, Linus Torvalds决定应该在提交和工作树之间插入第三份副本.

了解已提交的文件副本具有特殊的冻结,只读,压缩,仅Git的文件格式(虽然您不需要,Git称为 blob对象),这很有用.大部分时间都知道).由于此类文件是冻结的/只读的,因此Git可以在使用同一文件副本的每次提交中 share 共享它们.这样可以节省大量的磁盘空间:一个10兆字节文件的一次提交最多占用10兆字节(取决于压缩),但是使用同一文件进行第二次提交,而新副本则占用零个额外字节:它只是重复使用现有副本.无论您提交了多少 more 次提交,只要您继续重复使用旧文件,就不会占用更多空间来存储文件.Git只是继续重复使用原始版本.

事实上,关于提交的所有内容都将被永久冻结.任何提交的任何部分-文件,作者信息,日志消息中的拼写错误都无法更改.您能做的最好的事情就是进行一个新的改进的不同提交,该提交可以解决拼写错误或其他问题.然后,您可以使用新的和改进的提交,而不是旧的和糟糕的提交,但是新的提交是具有不同哈希ID的其他提交.哈希ID是提交的真实名称(就此而言,是提交快照附带的Blob对象的真实名称).

因此,提交是永久性的且是只读的.提交中的文件被压缩为只读,仅Git的冻干格式.由于提交历史记录,因此可以永久保留历史记录,以防万一您想回头看看某人的行为,时间和原因.但这根本无法完成任何实际工作.您需要使文件具有可延展性,柔韧性,可塑性,易处理性,柔韧性,腻子.您需要使用处理您的文件.简而言之,您需要一棵工作树,您可以在其中完成您的实际工作.

当您进行 git checkout 提交时,Git会将冻干的副本提取到该工作树中.现在您的文件都在那里,您可以在其中使用和更改它们.您可能认为 git commit 将从工作树中获取更新的文件并提交它们,例如,Mercurial的 hg commit 就是这样做的-但不,那不是什么Git确实如此.

相反,Git在提交的副本和工作树副本之间插入每个文件的此第三副本.第三个副本位于Git有时称为 index 的实体中,有时称为 staging区域,有时也称为 cache -三个一件事的名称是-冻干的Git 格式,但重要的是,由于它不在 commit 中,因此您可以随时覆盖它.这就是 git add 的作用:它将工作树中的普通文件取出,进行冷冻干燥,然后将其填充到索引中,以代替之前该名称下的任何内容.

如果 git add 之前的索引中没有文件 ,那么现在就可以了.而且,如果它在索引中 ……无论如何,Git将工作树文件压缩为适当的冻干格式,然后将其填充到索引中,因此现在索引副本已匹配工作树副本.如果工作树副本与提交的副本匹配(适当地采用任何冻干或补水的方式),则所有三个副本都匹配.如果不是,则可能有两个匹配的 副本. 但这不是唯一的可能性 ,它们只是主要的三种,我们稍后会看到.


这些原因多数归因于性能.Git的 git commit 比Mercurial的 hg commit 快数千倍.其中一些是因为Mercurial主要是用Python编写的,但很大一部分是因为Git的索引.

更确切地说,提交将持久保存,直到没有人再可以通过哈希ID找到它们为止.当您从旧的,糟糕的提交切换到新的和改进的副本时,可能会发生这种情况.之后,如果旧的和糟糕的提交确实无法找到(而不是仅仅从偶然的观察中隐藏),则有资格被Git的垃圾收集器 git gc删除.


对于每个文件,请检查其三个副本中的状态

您已经通过 git checkout 选择了一些提交作为当前( HEAD )提交.Git发现此提交有一定数量的文件.它已将它们全部提取到索引工作树中.假设您只有文件 README.md main.py .他们现在是这样的:

  HEAD索引工作树--------- --------- ---------README.md README.md README.mdmain.py main.py main.py 

从此表中很难分辨出哪个文件具有哪个版本,所以让我们添加一个版本号:

  HEAD索引工作树--------- --------- ---------README.md(1)README.md(1)README.md(1)main.py(1)main.py(1)main.py(1) 

这与Pro Git书的第一个状态相匹配.

现在,您修改工作树中的文件之一.(这是您可以使用普通的非Git命令看到并使用的唯一文件.)假设您将 README.md 的版本2放入工作树:

  HEAD索引工作树--------- --------- ---------README.md(1)README.md(1)README.md(2)main.py(1)main.py(1)main.py(1) 

Git现在会说您没有对 README.md 进行提交的更改.真正的意思是,如果我们进行两个比较-从 HEAD 与索引开始,然后继续进行索引与工作树-我们会在第一次比较中看到相同,而在第二次比较中会有所不同.这与Pro Git书的已修改但未上演"状态相符.

如果现在运行 git add README.md ,则Git将冻结干燥更新的工作树版本2 README.md 并覆盖索引中的一个:

  HEAD索引工作树--------- --------- ---------README.md(1)README.md(2)README.md(2)main.py(1)main.py(1)main.py(1) 

表中的一个细微变化是,现在,在比较中, HEAD -vs-index显示 README.md 已更改,而index-vs-work-tree显示它们是相同的.Git称这种情况为为提交所做的更改.这与Pro Git书的修改和上演"状态相匹配.

如果您现在提交 new ,Git将立即打包索引 中的所有内容,即版本号 main.py 和版本2 README.md -并使用这些文件进行新提交.然后它将进行调整,以使 HEAD 表示 new 提交,而不是您先前签出的提交.因此,现在,即使 old 提交仍然具有两个文件的版本1格式,您现在仍然拥有:

  HEAD索引工作树--------- --------- ---------README.md(2)README.md(2)README.md(2)main.py(1)main.py(1)main.py(1) 

,现在 README.md 的所有三个副本都匹配.

但是假设您现在在工作树中更改 README.md 来制作版本3,然后 git add 即可:

  HEAD索引工作树--------- --------- ---------README.md(1)README.md(3)README.md(3)main.py(1)main.py(1)main.py(1) 

然后再更改一些 README.md ,以使版本4与之前的所有三个版本不同:

  HEAD索引工作树--------- --------- ---------README.md(1)README.md(3)README.md(4)main.py(1)main.py(1)main.py(1) 

当我们现在比较 HEAD -vs-index时,我们看到 README.md 已为提交准备了 ,但是当我们比较索引时与工作树相比,我们看到它也没有上演提交.这不符合这三个州中的任何一个州,但有可能!

已跟踪与未跟踪

不幸的是,这是极具误导性的.实际上,跟踪文件非常简单地是当前索引中的任何文件.注意,该指数是可延展的.它现在可能具有 README.md 版本3,但是您可以将其 README.md 替换为另一个版本,甚至可以删除完全 README.md .

如果您删除 README.md ,则会得到:

  HEAD索引工作树--------- --------- ---------README.md(1)README.md(4)main.py(1)main.py(1)main.py(1) 

版本3现已消失.因此,工作树中的 README.md 现在是一个未跟踪的文件.如果在运行 git commit README.md 返回到被跟踪的位置,因为它位于索引中.

由于 git checkout 从您签出的提交中填充了索引(和工作树),所以说 并不是错误的可能会跟踪最后一次提交中的文件.但是,正如我在这里所说的那样,这具有误导性.跟踪度是文件在索引中的函数.到达那里的方式与跟踪程度无关.


从技术上讲,Git在其对象数据库中仍将冻干副本作为blob对象,但是如果没有其他人使用该冻干副本,则它现在可以进行垃圾收集了,并且可以随时消失.


Git从索引进行新提交;新的提交会参考较旧的提交

我们已经在上面提到了其中一些内容,但是让我们再看一遍,因为这对于理解Git至关重要.

Git中的每个提交(实际上是任何类型的每个对象)都有特定于该特定提交的哈希ID.如果您写下哈希ID,然后再次键入所有哈希ID,只要该提交仍位于Git的所有对象"的主数据库中,则Git可以使用该哈希ID查找提交.

每个提交中还存储了一些较早提交的哈希ID.通常,这只是一个先前的哈希ID.前一个哈希ID是提交的 parent .

只要您(或Git)拥有这些哈希ID之一,我们就说您(或Git)具有指向基础对象的指针.因此,每个提交均指向其父级.这意味着,给定一个只有三个提交的小型存储库,我们可以绘制这些提交.如果我们使用单个大写字母代替我们的提交哈希ID,则结果对人类更有用,尽管我们当然会很快用完ID(因此,我们绘制的不仅仅是几个提交):

  A< -B< -C 

此处 C last 提交.我们必须以某种方式知道其哈希ID.如果这样做,我们可以让Git从数据库中获取实际的提交,而 C 保留其先前的提交 B 的哈希ID.我们可以让Git使用它来钓出 B 并找到 A 的哈希ID.我们可以使用它来获取 A 本身,但是这次没有 no 以前的哈希ID.不可能是: A 是第一个提交.没有早先的提交使 A 指向其后.

所有这些指针根据需要始终指向向后.提交后,任何提交的任何部分都无法更改,因此 B 可以保留 A 的ID,但不能将 A 更改为将 B 的ID填充到 A 中. C 可以指向 B ,但是我们不能更改 B 使其指向 C .但是我们要做的只是记住 C 的真实哈希ID,而这是分支名称出现的地方.

让我们选择名称 master ,然后让Git在该名称下保存 C 的哈希ID.由于该名称包含一个哈希ID,因此该名称指向 C :

  A--B--C<-主 

(出于懒惰和/或其他原因,我已停止在提交中以箭头的形式绘制连接器.没关系,因为它们无法更改,并且我们知道它们指向后方.)

现在,让我们使用 git checkout master 来检出提交 C ,该操作将从用提交 C保存的文件中填充索引和工作树:

  git checkout master 

然后,我们将修改一些文件,使用 git add 将它们复制回索引,最后运行 git commit . git commit 命令将收集我们的姓名和电子邮件地址,从我们或 -m 标志获取日志消息,添加当前时间,并通过以下方式进行新的提交立即保存索引中的所有内容.这就是为什么我们必须首先将文件 git add 到索引中.

此新提交将具有提交 C 的哈希ID作为新提交的父级.写出提交的动作将计算新提交的哈希ID,但是我们将其称为 D .现在我们有了:

  A--B--C<-主\d 

但是现在Git做了一些非常聪明的事情:它将 D 的哈希ID写入 name master ,这样, master现在指向 D :

  A--B--C\D<-大师 

,现在提交 D 是最后一次提交.我们需要记住的只是名称 master ;Git会为我们记住哈希ID.

git commit -a 怎么样?

Git确实可以使用 git commit -a 来提交工作树中的所有内容.但这实际上是实际上在执行提交之前运行 git add -u :对于当前实际上在索引中的每个文件,Git会检查索引中的每个文件以查看工作树副本是否不同,如果存在,Git将该文件添加到索引中.然后它从索引进行新的提交.

每个文件的中间第三个副本(索引中的一个副本)是为什么,您必须一直 git add .作为Git的新用户,它通常会妨碍您.尝试使用 git commit -a 解决它,并假装它不存在.但这最终会使您陷入困境,导致索引出现问题,从而使无法跟踪的文件与无法跟踪的文件完全无法解释.

此外,索引的存在允许各种巧妙的技巧,例如 git add -p ,对于某些工作流程实际上非常有用且实用,因此这不是一个坏主意了解索引.您可以将其保留很多,以备后用,但请记住,有一个中间的冻干副本,并且 git status 运行两个比较- HEAD -vs-index,然后是index-vs-work-tree,这一切都更加有意义.


这也是一个谎言:Git实际上为此情况创建了临时索引.临时索引从真实索引的副本开始,然后Git在此处添加文件.但是,如果提交工作一切顺利,则临时索引将成为 the 索引(即原来的真实主索引),因此将其添加到临时索引中具有相同的效果.出现的唯一时间是提交 失败,或者,如果您偷偷摸摸,则当您进入存储库状态检查 git时commit -a 仍在进行中.

如果使用 git commit --only ,则图片会变得更加复杂,这将使两个成为临时索引(索引?).但是,让我们不要去那里.:-)

I read about The Three States in Git from https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3FIt says here that Git has three main states that your files can reside in: committed, modified, and staged.

Then, I also read about the two states: tracked or untracked from https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-RepositoryHere it says that each file in your working directory can be in one of two states: tracked or untracked. Tracked files are files that were in the last snapshot; they can be unmodified, modified, or staged.

Are the states mentioned from the The Three States similar with the sub states of tracked files? Is committed and unmodified the same?

These images shows they are the same?

解决方案

TL;DR

Tracked-ness is not a subset of the listed three states, and the listed three states are not sufficient to describe (or understand, really) how Git works.

Long

This "three states" thing is a bit of a white lie, which is probably why the page says:

(emphasis mine). It's my opinion that the Pro Git book is doing a bit of disservice here, as I think they are trying—for some good reasons—to hide the existence of Git's index from your initial view of everything. But in the same very same paragraph, they introduce the idea of the staging area, which is really just another name for the index.

In fact, what's really going on here is that there are normally three copies of each file. One copy is in the current commit, a middle copy is in the index / staging-area, and a third copy is in your work-tree.

The middle copy—the one in the index—is not necessary, from a version-control-system design point of view. Mercurial is another version control system that is very much like Git, and it has only two copies of each file: the committed one, and the work-tree one. This system is much easier to think about and to explain. But for various reasons, Linus Torvalds decided that you should have a third copy, wedged in between the commit and the work-tree.

It's useful to know that committed copies of files are in a special frozen, read-only, compressed, Git-only file format (which Git calls a blob object though you don't need to know that most of the time). Because such files are frozen / read-only, Git can share them across every commit that uses the same copy of the file. This can save enormous amounts of disk space: one commit of a ten megabyte file takes up to ten megabytes (depending on compression), but make a second commit with the same file and the new copy takes zero extra bytes: it just re-uses the existing copy. No matter how many more commits you make, as long as you keep re-using the old file, it takes no more space to store the file. Git just keeps re-using the original instead.

In fact, everything about a commit is frozen forever. No part of any commit—no file, no author information, no spelling error in the log message—can ever be changed. The best you can do is make a new and improved, different commit, that fixes the spelling error or whatever. Then you can use the new and improved commit instead of the old and lousy one, but the new commit is a different commit, with a different hash ID. The hash IDs are the true names of the commits (and, for that matter, of the blob objects that go with the commit snapshot).

So commits are permanent and read-only. The files inside commits are compressed into a read-only, Git-only, freeze-dried format. Since commits are history, this keeps the history around forever, in case you ever want to look back at it to see what someone did, when, and why. But it's no good at all for getting any actual work done. You need files to be malleable, pliable, plastic, tractable, flexible, putty in your hands. You need to work with your files. In short, you need a work tree, where you can do your actual work.

When you git checkout a commit, Git extracts the freeze-dried copies into this work-tree. Now your files are all there where you can use them and change them. You would think that git commit would take the updated files from the work-tree and commit them—that's what Mercurial's hg commit does, for instance—but no, that's not what Git does.

Instead, Git inserts this third copy of each file in between the committed copy and the work-tree copy. This third copy, which is in the entity that Git sometimes calls the index, sometimes calls the staging area, and occasionally calls the cache—three names for one thing—is in the freeze-dried Git format, but importantly, since it's not in a commit, you can overwrite it any time. That's what git add does: it takes an ordinary file you have in your work-tree, freeze-dries it, and stuffs that into the index in place of whatever was in the index under that name before.

If the file wasn't in the index before your git add, well, now it is. And if it was in the index ... well, in either case, Git compressed the work-tree file into the appropriate freeze-dried format and stuffed that into the index, so now the index copy matches the work-tree copy. If the work-tree copy matches the committed copy (modulo any freeze-drying or rehydrating as appropriate), all three copies match. If not, you probably have two copies that match. But these aren't the only possibilities—they're just the main three, as we'll see in a moment.


Most of these reasons come down to performance. Git's git commit is thousands of times faster than Mercurial's hg commit. Some of that is because Mercurial is written mostly in Python, but a lot of it is because of Git's index.

More precisely, commits persist until nobody can find them by hash ID any more. That can happen when you switch from an old and lousy commit to a new and improved copy. After that, the old and lousy commits, if they're truly un-findable (as opposed to merely hidden from casual observation), are eligible to be removed by Git's garbage collector, git gc.


For each file, examine its state in the three copies

You've already picked some commit as the current (HEAD) commit, via git checkout. Git found that this commit has some number of files; it has extracted them all to both the index and the work-tree. Suppose you have just the files README.md and main.py. They are now like this:

  HEAD           index        work-tree
---------      ---------      ---------
README.md      README.md      README.md
main.py        main.py        main.py

It's pretty hard to tell from this table which file has which version, so let's add a version number:

  HEAD           index        work-tree
---------      ---------      ---------
README.md(1)   README.md(1)   README.md(1)
main.py(1)     main.py(1)     main.py(1)

This matches up with the Pro Git book's first state.

Now you modify one of the files in your work-tree. (These are the only files you can see and work on with ordinary non-Git commands.) Let's say you put version 2 of README.md into the work-tree:

  HEAD           index        work-tree
---------      ---------      ---------
README.md(1)   README.md(1)   README.md(2)
main.py(1)     main.py(1)     main.py(1)

Git will now say that you have changes not staged for commit to README.md. What this really means is that if we do two comparisons—starting with HEAD vs index, then moving on to index vs work-tree—we see same in first compare, different in second. This matches up with the Pro Git book's "modified but not staged" state.

If you now run git add README.md, Git will freeze-dry the updated work-tree version-2 README.md and overwrite the one in the index:

  HEAD           index        work-tree
---------      ---------      ---------
README.md(1)   README.md(2)   README.md(2)
main.py(1)     main.py(1)     main.py(1)

The one small subtle change in the table is that now, in the comparison, HEAD-vs-index shows README.md changed, while index-vs-work-tree shows them to be the same. Git calls this situation changes staged for commit. This matches up with the Pro Git book's "modified and staged" state.

If you make a new commit now, Git will package up whatever is in the index right now—i.e., the version one main.py and the version 2 README.md—and make the new commit using those files. Then it will adjust things so that HEAD means the new commit, instead of the one you had checked out earlier. So now, even though the old commit still has both files in their version-1 form, you now have:

  HEAD           index        work-tree
---------      ---------      ---------
README.md(2)   README.md(2)   README.md(2)
main.py(1)     main.py(1)     main.py(1)

and now all three copies of README.md match.

But suppose you change README.md in the work-tree now to make a version 3, then git add that:

  HEAD           index        work-tree
---------      ---------      ---------
README.md(1)   README.md(3)   README.md(3)
main.py(1)     main.py(1)     main.py(1)

Then you change README.md some more to make a version 4, different from all three previous versions:

  HEAD           index        work-tree
---------      ---------      ---------
README.md(1)   README.md(3)   README.md(4)
main.py(1)     main.py(1)     main.py(1)

When we now compare HEAD-vs-index, we see that README.md is staged for commit, but when we compare index vs work-tree, we see that it's also not staged for commit. This doesn't match any of the three states—but it's possible!

Tracked vs untracked

This, unfortunately, is highly misleading. In fact, a tracked file is very simply any file that is in the index right now. Note that the index is malleable. It may have README.md version 3 in it right now—but you can replace that README.md with another version, or even remove that README.md entirely.

If you remove that README.md you get:

  HEAD           index        work-tree
---------      ---------      ---------
README.md(1)                  README.md(4)
main.py(1)     main.py(1)     main.py(1)

Version 3 is just gone now. So now the README.md that's in the work-tree is an untracked file. If you put a version—any version—of README.md back into the index before running git commit, README.md goes back to being tracked, because it's in the index.

Since git checkout fills in the index (and the work-tree) from the commit you check out, it's not wrong to say that files that were in the last commit are probably tracked. But as I say here, it's misleading. The tracked-ness is a function of the file being in the index. How it got there is not relevant to the tracked-ness.


Technically, Git still has the freeze-dried copy as a blob object in its object database, but if no one else uses that freeze-dried copy, it's eligible for garbage collection now, and could go away at any time.


Git makes new commits from the index; new commits refer back to older ones

We already mentioned some of this above, but let's go over it again because it's crucial to understanding Git.

Each commit—really, each object of any kind—in Git has a hash ID specific to that one particular commit. If you write down the hash ID, and type it all in again, Git can use that hash ID to find the commit, as long as the commit is still in Git's master database of "all objects ever".

Each commit also has some number of earlier-commit hash IDs stored inside it. Usually that's just one previous hash ID. This one previous hash ID is the commit's parent.

Whenever you (or Git) has one of these hash IDs in hand, we say that you (or Git) has a pointer to the underlying object. So each commit points to its parent. This means that given a small repository with, say, just three commits, we can draw the commits. If we use single uppercase letters to stand in for our commit hash IDs, the result is a lot more useful to humans, though of course we'll run out of IDs pretty fast (so let's not draw more than just a few commits):

A <-B <-C

Here C is the last commit. We have to somehow know its hash ID. If we do, we can have Git fetch the actual commit from the database, and C holds the hash ID of its predecessor commit B. We can have Git use that to fish B out and find the hash ID of A. We can use that to fish out A itself, but this time, there's no previous hash ID. There can't be: A was the very first commit; there was no earlier commit for A to point back to.

All these pointers always point backwards, by necessity. No part of any commit can change after we make it, so B can hold A's ID, but we can't change A to stuff B's ID into A. C can point to B but we can't change B to make it point to C. But all we have to do is remember the real hash ID of C, and this is where branch names come in.

Let's pick the name master and have Git save C's hash ID under that name. Since the name holds a hash ID, the name points to C:

A--B--C   <-- master

(For laziness and/or other reasons, I've stopped drawing the connectors in the commits as arrows. That's OK, because they can't change and we know they point backwards.)

Now let's check out commit C, using git checkout master, which fills in our index and work-tree from the files saved with commit C:

git checkout master

Then we'll modify some files, use git add to copy them back into the index, and last, run git commit. The git commit command will collect our name and email address, get a log message from us or from the -m flag, add the current time, and make a new commit by saving whatever is in the index right now. That's why we had to git add the files to the index first.

This new commit will have commit C's hash ID as the new commit's parent. The act of writing out the commit will compute the hash ID for the new commit, but we'll just call it D. So we now have:

A--B--C   <-- master
       \
        D

But now Git does something extremely clever: it writes D's hash ID into the name master, so that master now points to D:

A--B--C
       \
        D   <-- master

and now commit D is the last commit. All we need to remember is the name master; Git remembers the hash IDs for us.

What about git commit -a?

Git does have a way to commit whatever is in your work-tree, using git commit -a. But what this really does is, in effect, to run git add -u right before doing the commit: for every file that's actually, currently, in the index, Git checks to see if the work-tree copy is different, and if so, Git adds that file to the index. Then it makes the new commit from the index.

This intermediate, third copy of every file—the one in the index—is why you have to git add all the time. As a new user of Git, it mostly gets in your way. It's tempting to work around it with git commit -a, and pretend it doesn't exist. But that eventually leaves you stranded when something fails with a problem with the index, and it leaves tracked-vs-untracked files entirely inexplicable.

Also, the presence of the index allows for all kinds of neat tricks, like git add -p, that are actually pretty useful and practical for some work-flows, so it's not a bad idea to learn about the index. You can leave a lot of this for later, but just remember that there's this intermediate freeze-dried copy, and that git status runs two comparisons—HEAD-vs-index, then index-vs-work-tree—and it all makes much more sense.


This, too, is a white lie: Git actually makes a temporary index for this case. The temporary index starts as a copy of the real index, and then Git adds the files there. However, if all goes well with the commit, the temporary index becomes the index—the real, main index, as it were—so adding to the temporary index has the same effect. The only time this shows up is when the commit fails, or, if you're sneaky enough, when you go in and inspect the repository state while the git commit -a is still in progress.

The picture gets even more complicated if you use git commit --only, which makes two temporary indexes (indices?). But let's just not go there. :-)

这篇关于被“承诺"和“未修改的"相同?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-06 03:33