为什么 github 不删除我移动的文件

Mel*_*lly 0 git github

所以我使用 git 从本地机器远程到 Github,当我第一次提交时,目录是web/main,然后我将其推送到 github。之后我改变了主意,因此我将该main文件夹移动到一个名为 的新文件夹中backend,并将目录更改为web/backend/main. 请记住,我移动了它,因此web/main它不应该再存在,并且它在我的本地计算机中不存在,但在github中,该文件夹仍然存在。这是故意的吗?

git 状态:

$ git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean
Run Code Online (Sandbox Code Playgroud)

git 推送:

$ git push
Everything up-to-date
Run Code Online (Sandbox Code Playgroud)

编辑:

我可能应该早点指出这一点,但是在第一次提交的manage.py文件夹旁边有一个 python 文件mainweb/

IMSoP 还指出,manage.py当我们在 github 中复制其他文件时,会被重命名。所以我检查了本地计算机上的第一次提交,奇怪的是我找不到main文件夹,这很奇怪,因为在我的 github 的第一次提交中它就在那里。

更新:

所以我决定从头开始重做整个过程

  • 我做的第一件事是创建与第一次提交相同的目录,即web/main

  • 然后我在本地机器上初始化了 git repogit add .并 运行git commit -m "first commit"

  • 将其推送到我的 github 存储库中

这就是奇怪的事情发生的地方。我将main文件夹和manage.py文件移动到backend,运行 git status ,这就是它所说的

 $ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        deleted:    web/manage.py

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

no changes added to commit (use "git add" and/or "git commit -a")  
Run Code Online (Sandbox Code Playgroud)

所以它基本上没有检测到我的web/main文件夹已经消失,即使它没有出现在我的本地计算机中

先感谢您

tor*_*rek 5

克隆您的存储库(https://github.com/happyprogrammer-code/gittest-django-simpleapp),我发现其中有两个提交:

\n
$ git log --all --decorate --oneline --graph\n* fbef865 (HEAD -> master, origin/master, origin/HEAD) organizing the files in to backend file\n* 018aed1 first commit\n
Run Code Online (Sandbox Code Playgroud)\n

这是 commit 中的内容018aed1

\n
$ git ls-tree -r HEAD^\n100644 blob b7c617c5cf9a85407c312d9e32da17de165cf81c    .gitignore\n100644 blob 94ff3859fbf86be732f633254a110a860a8bbb65    requirements.txt\n100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    web/main/__init__.py\n100644 blob 63356bd12bf3370b9b4e6411d6b24e1fe5681e99    web/main/asgi.py\n100644 blob 905914bb8d0aa8951948ba5f1a5167ea4627e35b    web/main/settings.py\n100644 blob e1637d6a6898f22e07fcfaee419c67565997e788    web/main/urls.py\n100644 blob e1c99c8fb064bb93d7710bdf80e80231fc95343a    web/main/wsgi.py\n100644 blob fbda2b3121972503edd5072a6f077c6f744abe77    web/manage.py\n
Run Code Online (Sandbox Code Playgroud)\n

这是 commit 中的内容fbef865

\n
$ git ls-tree -r HEAD\n100644 blob b7c617c5cf9a85407c312d9e32da17de165cf81c    .gitignore\n100644 blob 94ff3859fbf86be732f633254a110a860a8bbb65    requirements.txt\n100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    web/backend/main/__init__.py\n100644 blob 63356bd12bf3370b9b4e6411d6b24e1fe5681e99    web/backend/main/asgi.py\n100644 blob 905914bb8d0aa8951948ba5f1a5167ea4627e35b    web/backend/main/settings.py\n100644 blob e1637d6a6898f22e07fcfaee419c67565997e788    web/backend/main/urls.py\n100644 blob e1c99c8fb064bb93d7710bdf80e80231fc95343a    web/backend/main/wsgi.py\n100644 blob fbda2b3121972503edd5072a6f077c6f744abe77    web/backend/manage.py\n100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    web/main/__init__.py\n100644 blob 63356bd12bf3370b9b4e6411d6b24e1fe5681e99    web/main/asgi.py\n100644 blob 905914bb8d0aa8951948ba5f1a5167ea4627e35b    web/main/settings.py\n100644 blob e1637d6a6898f22e07fcfaee419c67565997e788    web/main/urls.py\n100644 blob e1c99c8fb064bb93d7710bdf80e80231fc95343a    web/main/wsgi.py\n
Run Code Online (Sandbox Code Playgroud)\n

因此,您在 GitHub 上看到这两组文件的原因是您最近的提交包含这两组文件。

\n
\n

请记住,我已将[web/main移至web/backup/main],因此web/main不应再存在,并且它在我的本地计算机中不存在

\n
\n

这里您需要小心,因为Git 不会使用工作树中的文件来构建新的提交。相反,Git 使用Git 索引中的文件。我相信我确切地知道发生了什么,但在我提出我认为发生的事情之前,让我们先描述一下 Git 的索引。

\n

指数

\n

Git 的索引对于 Git 来说非常重要,但它基本上是不可见的。如果您查看(非裸)存储库,您会看到一堆普通文件,如果仔细查看的话,还会看到\xe2\x80\x94a.git目录。该.git目录包含 Git 的数据库和辅助控制文件:

\n
$ ls .git\nbranches        HEAD            info            packed-refs\nconfig          hooks           logs            refs\ndescription     index           objects\n
Run Code Online (Sandbox Code Playgroud)\n

大数据库(所有 Git 对象)位于 中.git/objects,较小的数据库(例如分支和其他名称)位于.git/packed-refs.git/refs以及其他文件中。该文件.git/index是 Git 索引当前存储的位置,1但它是一个二进制文件:

\n
$ file .git/index\n.git/index: Git index, version 2, 13 entries\n
Run Code Online (Sandbox Code Playgroud)\n

该文件中的实际内容很混乱,如果您需要进行低级别的操作,但要描述 Git 的索引\xe2\x80,则应该使用管道命令git ls-files并对其进行操作git update-index\x94 也称为暂存区,有时也称为缓存\xe2\x80\x94 在尽可能少的单词中,我喜欢这句话: 索引保存您建议的下一次提交。

\n

当您使用或 签出提交时, Git 将从提交中填充此索引。实际内容只是索引条目,您可以使用 ; 查看它们。每个都有一个模式、一个哈希 ID(通常是 Blob 哈希 ID)、一个暂存编号(通常为零)和一个路径名,例如. 请注意,这些文件名包含嵌入的(正向)斜杠:此级别没有文件夹或目录,只有名称中包含斜杠的文件。2git checkoutgit switchgit ls-files --stageweb/main/__init__.py

\n

Git 还将在此结账期间填充您的工作树。这为您提供了可用的文件副本。索引中的 blob 对象是文件数据的只读、Git 化、压缩和重复数据删除副本。请注意,在git ls-tree -r上面的输出中,各种 blob 哈希 ID 重复:文件的数据在每个 blob 中仅存储一次,即使文件“位于”两次提交中也是如此。在第二次提交中,两个文件都有哈希 ID e69de29bb2d1d6434b8b29ae775ad8c2e48c5391。(这实际上是空文件的哈希 ID:每个Git存储库中的每个空文件都有这个特定的哈希 ID。)

\n

当您在工作树中工作时,您将更改某些文件的内容。您甚至可以重命名文件,或者调整树的位置。 这些都不会影响 Git 的索引,它保存文件的独立副本。但最终,您会希望将新内容放入新的提交中。现在你必须跑git add

\n

所做git add的就是读取工作树文件,对其进行压缩和去重,然后更新 Git 的索引。 如果新内容与某些现有 blob 匹配,则哈希 ID 匹配,并且 Git 仅重新使用现有 blob。如果新内容确实是新的,Git 会将其存储到对象数据库中,并将新的哈希 ID 放入索引中。无论哪种方式,更新的文件现在都已准备好进入新的提交。

\n

当您运行时git commit,Git 只是以当时的形式打包当时索引中的所有文件,以进行新的提交。因此,索引中的任何内容现在都在新的提交中。

\n
\n

1有一个环境变量 ,GIT_INDEX_FILE它给出了主要索引文件的路径名。该索引文件可以引用其他文件,并且许多细节没有以任何一种方式承诺,但GIT_INDEX_FILE如果您设置它,Git 管道命令将遵循,这允许您使用替代索引文件。例如,旧git stash脚本,当脚本存在时,就使用了这个。git stash

\n

2这就是 Git 无法存储空目录的原因。在索引中存储gitlink是可能的,并且 gitlink 的存在使 Git 创建一个空目录,因此这是一种作弊方法:gitlink 是子模块的一半,因此在索引中存储半子模块让你让 Git 创建一个空目录。但是您需要另一半来完成子模块,以使 Git 满意。另请参阅如何将空白目录添加到 Git 存储库?

\n
\n

删除或重命名文件

\n

如果您删除或重命名工作树中的文件,Git 的索引中还不会发生任何事情。您必须告诉 Git 更新其索引。

\n

执行此操作的命令包括git add像以前一样,但也git rm包括 和git mv您真正需要的唯一一个是git add因为git add可以从 Git 索引中删除文件。让我们Git 的索引中创建一个文件:

\n
$ git log --all --decorate --oneline --graph\n* fbef865 (HEAD -> master, origin/master, origin/HEAD) organizing the files in to backend file\n* 018aed1 first commit\n
Run Code Online (Sandbox Code Playgroud)\n

(又是那个空文件哈希 ID)。现在我们将从工作树中删除该文件,并查看它保留在 Git 的索引中,然后我们将运行git add并查看会发生什么:

\n
$ git ls-tree -r HEAD^\n100644 blob b7c617c5cf9a85407c312d9e32da17de165cf81c    .gitignore\n100644 blob 94ff3859fbf86be732f633254a110a860a8bbb65    requirements.txt\n100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    web/main/__init__.py\n100644 blob 63356bd12bf3370b9b4e6411d6b24e1fe5681e99    web/main/asgi.py\n100644 blob 905914bb8d0aa8951948ba5f1a5167ea4627e35b    web/main/settings.py\n100644 blob e1637d6a6898f22e07fcfaee419c67565997e788    web/main/urls.py\n100644 blob e1c99c8fb064bb93d7710bdf80e80231fc95343a    web/main/wsgi.py\n100644 blob fbda2b3121972503edd5072a6f077c6f744abe77    web/manage.py\n
Run Code Online (Sandbox Code Playgroud)\n

因此git add,将“暂存删除”:如果文件从工作树中消失,也会git add 从索引中删除该文件

\n

那么,为什么git rm存在git mv?它们的存在是为了方便:

\n
$ git ls-tree -r HEAD\n100644 blob b7c617c5cf9a85407c312d9e32da17de165cf81c    .gitignore\n100644 blob 94ff3859fbf86be732f633254a110a860a8bbb65    requirements.txt\n100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    web/backend/main/__init__.py\n100644 blob 63356bd12bf3370b9b4e6411d6b24e1fe5681e99    web/backend/main/asgi.py\n100644 blob 905914bb8d0aa8951948ba5f1a5167ea4627e35b    web/backend/main/settings.py\n100644 blob e1637d6a6898f22e07fcfaee419c67565997e788    web/backend/main/urls.py\n100644 blob e1c99c8fb064bb93d7710bdf80e80231fc95343a    web/backend/main/wsgi.py\n100644 blob fbda2b3121972503edd5072a6f077c6f744abe77    web/backend/manage.py\n100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    web/main/__init__.py\n100644 blob 63356bd12bf3370b9b4e6411d6b24e1fe5681e99    web/main/asgi.py\n100644 blob 905914bb8d0aa8951948ba5f1a5167ea4627e35b    web/main/settings.py\n100644 blob e1637d6a6898f22e07fcfaee419c67565997e788    web/main/urls.py\n100644 blob e1c99c8fb064bb93d7710bdf80e80231fc95343a    web/main/wsgi.py\n
Run Code Online (Sandbox Code Playgroud)\n

git rm命令将从索引和工作树中删除该文件(并且,正如您所看到的,在某些情况下会抱怨并且不执行任何操作,要求-f--force执行此操作)。或者:

\n
$ ls .git\nbranches        HEAD            info            packed-refs\nconfig          hooks           logs            refs\ndescription     index           objects\n
Run Code Online (Sandbox Code Playgroud)\n

如果文件已经从工作树中消失,则git rm行为很像git add,只需将其从索引中删除即可。最后,还有git rm --cached. 这就是旧名称“ cache”出现的地方。此变体从 Git 的索引/暂存区域中删除文件,而不触及您的工作树。

\n

同样,git mv也是为了方便。Git 不存储重命名:每次提交只有每个文件的完整快照。稍后,Git 将git diff猜测命名:如果 diff 的“左侧”的提交有一些带有某些内容的文件 X,并且没有文件 Y,并且 diff 的“右侧”的提交有某些文件 Y 的内容与 X 曾经具有的内容相同,但没有文件 X,那么为什么文件 X 可能被重命名为文件 Y。但是要实现这一目标,我们必须:

\n
    \n
  1. 将新命名的文件的内容哈希(和模式)复制到索引中的新名称;
  2. \n
  3. 从索引中删除旧名称文件的模式和哈希值;和
  4. \n
  5. 根据此操作系统的文件系统规则,重命名或以其他方式更新工作树中的文件。
  6. \n
\n

我们可以使用 一次性完成所有这三件事git mv。或者,我们可以使用操作系统的文件重命名器mvrename其他任何名称来重命名该文件,然后运行git add两次,或git rmgit add,或其他任何内容。但运行一个git mv命令会更方便一些。

\n

这就是 和 的用途git rmgit mv它们是实现我们想要的结果的更方便的方法,也就是说,操作Git 索引工作树中的某些文件。我们两者都不需要。我们可以使用常规操作系统命令进行文件操作,因为工作树中的文件常规操作系统文件。然后我们可以用 更新 Git 的索引,它可以做所有事情。git add

\n

我认为你做了什么

\n

现在你可能知道你做了什么,但我认为你做了什么(假设是类 Unix 系统的 shell 命令,可能有一些细微的差别):

\n
mkdir web/backend\nmv web/main web/backend/main\ngit add web/backend\n
Run Code Online (Sandbox Code Playgroud)\n

这告诉 Git 将所有重命名的文件以新名称复制到 Git 的索引中。但 Git 从未取出名称,因此这些索引条目及其mode 100644blob <whatever>、阶段零和路径名保留在 Git 的索引中。然后你跑了:

\n
git commit\n
Run Code Online (Sandbox Code Playgroud)\n

Git 打包了索引内容,并包含每个文件的副本。

\n

用于git status查看索引中感兴趣的部分

\n

您的工作树包含的文件少于索引,如果您运行git status,Git 将运行两个git diff操作:

\n
    \n
  • 我们会将HEAD提交与 Git 的索引进行比较。由于提交是根据Git 的索引HEAD进行的,因此这两个将完全匹配,并且没有什么可说的,因此不会从中打印任何内容。git diffgit status

    \n
  • \n
  • 第二个git diff --name-status操作将比较 Git 索引中的内容和工作树中的内容。由于索引保存了从工作树中神秘消失的文件,Git 会将这些文件称为D eleted”。如果使用git status --short它们,它们将在此处显示为状态字母“D”,如果没有--short它们,它们将被列为未暂存提交的更改,并且一堆文件被“删除”。

    \n
  • \n
\n

因此,该git status命令非常有用:

\n
    \n
  • 首先,它告诉您“位于”哪个分支,即,当您进行新提交时,哪个分支名称将被更新以保存该新提交的新哈希 ID。

    \n
  • \n
  • 然后,如果当前分支有一个上游集,它会告诉您当前分支与其上游的比较。(如果没有,则跳过这部分。)

    \n
  • \n
  • 现在它运行两个差异。对于与索引中的提交匹配的文件HEAD,它们不是很有趣,并且它对这些文件没有任何说明,但是对于已消失的文件、新的文件或不同的文件,它们很有趣:它告诉您这些文件,说它们是为 commit 准备的。对于第二个差异,对于索引和工作树中匹配的文件,这些文件很乏味,因此它什么也没说,但是当文件匹配时,它会告诉您有关更改或删除的文件。

    \n
  • \n
\n

这里有点奇怪。位于工作树中但不在Git 索引中的文件不会在上述部分中列出。相反,最后一组文件被分成自己的部分。根据定义,这些文件是\xe2\x80\x94\xe2\x80\x94您的未跟踪文件。当前存在于工作树中但不在 Git 索引中的任何文件都是未跟踪文件

\n

使用 Python 代码时,我们会得到字节编译的文件.pyc.pyo文件(取决于优化),其名称__pycache__是否会出现(取决于 Python 版本)。这些文件使您的工作树变得混乱,如果 Git 抱怨它们是未跟踪的文件,那可能会非常吵闹。4因此 Git 允许您git status关闭某些未跟踪的文件。这就是内容的一半。.gitignore

\n

这些未跟踪的文件也应该保持未跟踪状态。这就是另一半内容.gitignore:使用集体添加操作(例如 )git add .通常会将每个文件添加到 Git 的索引中,包括所有未跟踪的文件。但.pyc不应添加这些文件。通过列出*.pyc.gitignore你告诉git status他们闭嘴,而git add不是添加它们。

\n

不过,关于这一点,您还需要了解另一件事:一旦文件位于Git 的索引\xe2\x80\x94 中,无论它到达那里:通过git add或从某些现有提交中读取,都不会发生这种情况。没关系\xe2\x80\x94一旦它那里,该文件就不会被忽略,即使它列在.gitignore. 您必须删除它,例如,将其Git 的索引中git rm --cached删除,以便再次忽略它。从 Git 的索引中删除它意味着一旦您进行了它,它现在就不在您的下一次提交中,这意味着当有人有一天检查该提交时,它也不会出现在 Git 的索引中。这会产生后果,特别是当它已经在其他一些Git提交中时,检查提交意味着它将位于Git 的索引中。但等你把这个索引的其余部分都冷掉之后,再担心这些。

\n
\n

3既然你知道了,就不再神秘了。

\n

4通过通常总结__pycache__一些未跟踪文件的方式,您在这里只得到一行,这并不那么烦人。使用旧的 Py2k 方案,每个文件只有一行,这非常烦人。git status

\n