首页 > 解决方案 > 正在创建分支而不添加

问题描述

我想为我的存储库创建一个新的分支,因为我遇到了以前的问题。

但是,我正在尝试的任何方法都不起作用。当我 (venv) C:\..\..\PycharmProjects\WebP1\DEMOPROJECT>git ls-tree --name-only hope 在创建新分支时运行时,我得到

.gitignore
DEMOAPP
DEMOPROJECT
__init__.py
__pycache__
db.sqlite3
manage.py
media
static

这不是我想要的,因为我希望 gitignore 忽略它

*.log
*.pot
*.pyc
__pycache__/
local_settings.py
db.sqlite3
media

我已经尝试过--orphan分支,但它似乎总是让我回到我不再想要的错误提交。出于某种我不想发生的原因,DEMOPROJECT 文件夹也被制作成一个子模块。请问我该如何解决?有什么add我需要做的吗?

标签: git

解决方案


正如Gabriel 所说,您从现有提交创建一个新分支,因此它将从现有提交的内容开始。您试图git checkout --orphan避免从现有提交开始,这一个有效且有时是明智的做法,但这可能不是您想要的。它失败了,因为即使那样,您也是从现有索引和 work-tree开始的。

(我们可以解决这个问题:

出于某种我不想发生的原因,DEMOPROJECT 文件夹也被设为子模块。

稍后,虽然单独的问题更合适。而且,如果你已经知道接下来的几节,你可以跳到关于索引的那一节。)

对 Git 来说重要的是提交

如果你打算使用 Git,你需要习惯 Git 的小怪癖。其中之一是分支——或者更准确地说,分支名称——对 Git 来说并不是那么重要。重要的是提交。首先准确了解提交的作用和代表的内容,然后引入分支名称至关重要。

每个提交都包含一组文件的快照。那部分非常简单。快照是您的git ls-tree输出中显示的内容——尽管从技术上讲git ls-tree,这里已经总结了整个子树的文件;您需要git ls-tree -r查看快照中的所有文件。

提交包含一些元数据:例如,创建提交的人的姓名和电子邮件地址,以及日期和时间戳。(实际上,其中有两个。)它包含一个日志消息,您在提交时提供该消息:您应该输入一些东西来告诉未来 - 您为什么要提交此提交。而且——非常重要的是——每个提交都包含一定数量的父提交哈希 ID(通常只有一个)。

每一次提交,一旦做出,都有一个唯一的哈希 ID。Universe 1中的每个 Git也同意提交获得哈希 ID,并且没有其他提交获得哈希 ID。一旦分配了哈希 ID,提交中的任何内容都不会改变。(原因是这个哈希 ID 实际上是提交对象内容的加密校验和。这就是宇宙中每个 Git 能够同意这个提交获得这个哈希 ID 的方式。)

您已经看到了这些哈希 ID:它们是大而丑陋的字符串,例如745f6812895b31c02b29bdfe4ae8e5498f776c26. 它们对人类没有用,但对 Git 有用。Git 使用哈希 ID 来查找实际提交,然后从中找到文件,依此类推。

提交中存储的哈希 ID 让 Git 可以找到在此特定提交之前的提交。也就是说,给定一系列具有随机散列 ID 的提交,我们可以用单个大写字母代表真正的散列 ID 来绘制它们,我们可以这样绘制它们:

... <-F <-G <-H

H最后一次提交的哈希 ID。它存储了较早提交的哈希 ID G,因此 Git 可以加载提交G。如果 Git 确实加载了它,则G存储先前提交的哈希 ID F,依此类推。我们说H 指向 GG指向F,等等。


1实际上,这仅限于与另一个 Git交换此提交的每个 Git。如果两个 Git 存储库从未相遇,它们可以对不同的提交使用相同的哈希 ID。他们会的机会很小:目前是 2 160分之一。但是,如果两个 Git 存储库确实会面并交换提交——就像git push你提交到 GitHub 时那样——它们不会重用哈希 ID:每个提交对于每个提交都是唯一的。


分支名称指向一个提交

这就是分支名称的用武之地。像这样的分支名称master只包含一个哈希 ID。我们确保它拥有最新的哈希 ID,因此它master指向H

... <-F <-G <-H   <--master

与提交本身不同,名称 可以更改。为了进行新的提交,我们让 Git 写出提交的内容——所有文件的快照、日志消息、来自 的您的姓名git config --get user.name等等,将新提交的父哈希设置为H. 结果是我们将调用一些新的唯一哈希 ID I

...--F--G--H   <-- master
            \
             I

既然I存在,Git 只需将哈希 ID 写入 name master

...--F--G--H
            \
             I   <-- master

现在I是分支中的最后一次提交。(现在我们可以把它画成一条直线,并I指向H。请注意,我们根本没有改变H:我们只是添加了I,指向现有的H。)

这就是分支的增长方式:我们让 Git 添加一个的提交。Git 写出快照以及元数据。新提交的父提交是当前提交——我们已经签出的那个。新的提交获得一个新的、唯一的哈希 ID。作为提交操作的最后一步,Git 将新的哈希 ID写入某个分支名称——我们已经签出的分支。

请注意,当我们进行第一次提交时,我们没有现有的提交。所以第一次提交的父级根本不存在。这个新的提交没有父母;Git 将此称为根提交。这使我们获得了存储库中的第一个提交,因此名称master也可以存在:

A   <-- master

之后,下一个提交BA作为其父提交,Git 将写入B名称master

这也是如何git checkout --orphan工作的。它所做的是进行设置,以便您进行的下一次提交将像往常一样使用它将使用的任何内容,但没有父级:它将是根提交。因此,如果您有:

A--B--C--D--E--F--G--H   <-- master

并做:

git checkout --orphan newbranch
git commit

你得到:

A--B--C--D--E--F--G--H   <-- master

I   <-- newbranch (HEAD)

(当我们有多个分支时,将特殊名称HEAD准确地附加到一个分支上很有用,这样我们就可以看到哪一个是当前分支。)

问题是,这个新提交与现有提交I存储相同的快照H,除非您更改要存储的快照。

索引是每个新快照的来源

Git 当然有提交,这些提交是永远冻结的。提交中的文件以特殊的、压缩的、只读的、仅限 Git 的格式存储。我喜欢将这些文件称为“冻干文件”。这对于存档非常有用,但对于完成任何工作完全没有用。

因此,Git 能够从提交中提取文件到工作区。这个工作区,称为您的工作树工作树或这些名称的一些变体,具有您计算机的普通格式的普通文件和文件夹/目录。这使您可以与他们一起工作。这是你工作的地方。

可以在工作树中包含不想放入下一次提交的文件。但是你的下一个提交在哪里?一些版本控制系统说你的工作树是你的下一个提交。这非常简单易用,所以它不是 Git 所做的。:-)

相反,在 Git 中,您的下一次提交存储在 Git 调用的不同地方,即indexstaging area,或(现在很少)cache。这三个名字都涵盖一件事。索引中的内容是将进入下一次提交的所有文件。这些文件处于冻干形式,可以提交,但与提交的文件不同,您可以将它们替换为新的和不同的文件,甚至完全删除它们。

你不能直接看到索引。好吧,你可以,但它有点尴尬。如果您只有几个文件,请运行git ls-files --stage以查看索引中的内容。如果你有很多文件,这会产生很多输出!下面是 Git 存储库中 Git 本身的一个片段:

100644 6e69877f25791632d98bf7b109a2eaebd04c96af 0       ws.c
100644 9f6c65a5809754717f8c51f809eae78f435bcd12 0       wt-status.c
100644 77dad5b92048851c622a35d8b34d802fbd0ecac6 0       wt-status.h
100644 8509f9ea223a1282a367874c3e3a3ef0c351a30f 0       xdiff-interface.c
100644 ede4246bbd3397086f90217539a2d07a35a4b986 0       xdiff-interface.h
100644 032e3a9f41a2f79eaab78ae36666b8b6218b3899 0       xdiff/xdiff.h
100644 1f1f4a3c7808435f73b0ffd1c35d5b0572516b6c 0       xdiff/xdiffi.c

请注意,此输出中没有目录/文件夹。那是因为 Git 不存储目录。它只是存储文件。如果需要保存文件,Git 只需创建一个目录/文件夹。例如,文件xdiff/xdiff.h是Git,但对于您的计算机,该文件在名为. 因此,如果必须,Git 将首先将 Git 的文件写入您计算机的-within- 。xdiff.hxdiffxdiffxdiff/xdiff.hxdiff.hxdiff

跟踪文件是索引中的文件

假设您的工作树中有一个名为main.pyc. 如果该文件也在索引中,main.pyc则被跟踪。如果此文件不在索引中,则该文件未跟踪

当你git checkout进行一些提交时,Git 会将提交中的所有文件复制到索引中,然后复制到你的工作树中。当您git checkout进行其他提交时,Git 会根据需要切换所有索引和工作树文件。

您可以随时git add在工作树文件上运行。这会将工作树文件复制到索引。如果之前有,那就换了。如果以前不存在,则已添加。

您还可以随时git rm在任何文件名上运行。这将删除索引副本工作树副本。

而且,当然,您可以运行git commit. 每当您这样做时,Git 都会立即打包索引中的所有内容使用它来进行新的提交。新提交的父提交是当前提交——您在开始时签出的提交。新提交成为当前分支(您签出的那个)中的最后一个提交。

.gitignore文件只能忽略未跟踪的文件

如果一个文件被跟踪——记住,这意味着它现在在索引中——列出该文件是.gitignore没有效果的。

因此,如果您不想提交文件,但它已经在索引中,则必须将其从索引中删除:

git rm --cached file

--cached选项告诉 Git,即使它从索引中删除文件,它也应该单独保留工作树副本。因此,在删除之后,下一次提交将不会拥有该文件,而不会被跟踪。

但是,有一个问题:如果此提交连接回某个较早的提交,并且您曾经检查过较早的提交,则该文件会将文件写入索引和您的工作树。然后,当您切换回没有该文件的新提交时,Git 将从索引和您的工作树中删除该文件。

请注意,如果您要创建一个初始提交没有先前提交--orphan的新分支,那会有所帮助。但是,分支仍然存在,并且仍然具有包含该文件的提交。

结论(在我们进入子模块之前)

可能想要--orphan,但如果您真的想要它,也许您只想从一个全新的 Git 存储库开始。

在任何情况下,如果您希望在以后的提交中不跟踪文件,如果它们现在在索引中,则必须将它们从索引中删除。您可以使用git rm,将它们从索引工作树中删除;或者您可以使用git rm --cached,它将它们从索引中删除,而不是从您的工作树中删除。

一旦文件未被跟踪,您可以将它们列出.gitignore以确保它们将来不会被添加索引中,即使它们在您的工作树中。这也不再git status抱怨未跟踪的文件。请注意,在检出期间,检出任何具有该文件的旧提交会将其放入索引中。切换回没有该文件的较新提交将从索引和工作树中删除该文件。确保这没问题,否则在查看旧提交时要非常小心。

子模块

我只会简单地谈到这一点,因为子模块可能会变得非常复杂。

在 Git 存储库中,不是工作树的东西通常都隐藏在.git工作树顶层命名的目录/文件夹中。

如果你的工作树的某个子目录/子文件夹有一个.git目录,你的 Git 会假设其他一些 Git正在控制你的树的那个部分。然后你的 Git 将决定它不应该对任何这些进行源代码控制。相反,您的 Git 将添加对其他 Git 存储库中提交的引用。此引用采用索引中的gitlink的形式。如果你使用,你可以用(gitlink)git ls-files --stage把它看成一个“文件” 。mode 160000

为了让 Git这样做,请确保您的工作树都不是它自己的存储库:没有子文件.git夹里面有文件夹。使用子模块时,Git 会将.git文件夹迁移到超级项目的.git文件夹中,替换为一个文件.git,其内容是超级项目的路径.git,因此如果子文件夹有.git 文件,这也可以触发子模块代码。


推荐阅读