所以我使用 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 文件main
web/
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
文件夹已经消失,即使它没有出现在我的本地计算机中
先感谢您
克隆您的存储库(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
:
$ 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
:
$ 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\n请记住,我已将[
\nweb/main
移至web/backup/main
],因此web/main
不应再存在,并且它在我的本地计算机中不存在
这里您需要小心,因为Git 不会使用工作树中的文件来构建新的提交。相反,Git 使用Git 索引中的文件。我相信我确切地知道发生了什么,但在我提出我认为发生的事情之前,让我们先描述一下 Git 的索引。
\nGit 的索引对于 Git 来说非常重要,但它基本上是不可见的。如果您查看(非裸)存储库,您会看到一堆普通文件,如果仔细查看的话,还会看到\xe2\x80\x94a.git
目录。该.git
目录包含 Git 的数据库和辅助控制文件:
$ 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但它是一个二进制文件:
$ 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 在尽可能少的单词中,我喜欢这句话: 索引保存您建议的下一次提交。
当您使用或 签出提交时, Git 将从提交中填充此索引。实际内容只是索引条目,您可以使用 ; 查看它们。每个都有一个模式、一个哈希 ID(通常是 Blob 哈希 ID)、一个暂存编号(通常为零)和一个路径名,例如. 请注意,这些文件名包含嵌入的(正向)斜杠:此级别没有文件夹或目录,只有名称中包含斜杠的文件。2git checkout
git switch
git ls-files --stage
web/main/__init__.py
Git 还将在此结账期间填充您的工作树。这为您提供了可用的文件副本。索引中的 blob 对象是文件数据的只读、Git 化、压缩和重复数据删除副本。请注意,在git ls-tree -r
上面的输出中,各种 blob 哈希 ID 重复:文件的数据在每个 blob 中仅存储一次,即使文件“位于”两次提交中也是如此。在第二次提交中,两个文件都有哈希 ID e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
。(这实际上是空文件的哈希 ID:每个Git存储库中的每个空文件都有这个特定的哈希 ID。)
当您在工作树中工作时,您将更改某些文件的内容。您甚至可以重命名文件,或者调整树的位置。 这些都不会影响 Git 的索引,它保存文件的独立副本。但最终,您会希望将新内容放入新的提交中。现在你必须跑git add
。
所做git add
的就是读取工作树文件,对其进行压缩和去重,然后更新 Git 的索引。 如果新内容与某些现有 blob 匹配,则哈希 ID 匹配,并且 Git 仅重新使用现有 blob。如果新内容确实是新的,Git 会将其存储到对象数据库中,并将新的哈希 ID 放入索引中。无论哪种方式,更新的文件现在都已准备好进入新的提交。
当您运行时git commit
,Git 只是以当时的形式打包当时索引中的所有文件,以进行新的提交。因此,索引中的任何内容现在都在新的提交中。
1有一个环境变量 ,GIT_INDEX_FILE
它给出了主要索引文件的路径名。该索引文件可以引用其他文件,并且许多细节没有以任何一种方式承诺,但GIT_INDEX_FILE
如果您设置它,Git 管道命令将遵循,这允许您使用替代索引文件。例如,旧git stash
脚本,当脚本存在时,就使用了这个。git stash
2这就是 Git 无法存储空目录的原因。在索引中存储gitlink是可能的,并且 gitlink 的存在使 Git 创建一个空目录,因此这是一种作弊方法:gitlink 是子模块的一半,因此在索引中存储半子模块让你让 Git 创建一个空目录。但是您需要另一半来完成子模块,以使 Git 满意。另请参阅如何将空白目录添加到 Git 存储库?
\n如果您删除或重命名工作树中的文件,Git 的索引中还不会发生任何事情。您必须告诉 Git 更新其索引。
\n执行此操作的命令包括git add
像以前一样,但也git rm
包括 和git mv
。 您真正需要的唯一一个是git add
因为git add
可以从 Git 索引中删除文件。让我们在Git 的索引中创建一个文件:
$ 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
并查看会发生什么:
$ 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
从索引中删除该文件。
那么,为什么git rm
存在git mv
?它们的存在是为了方便:
$ 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
执行此操作)。或者:
$ 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 的索引/暂存区域中删除文件,而不触及您的工作树。
同样,git mv
也是为了方便。Git 不存储重命名:每次提交只有每个文件的完整快照。稍后,Git 将git diff
猜测重命名:如果 diff 的“左侧”的提交有一些带有某些内容的文件 X,并且没有文件 Y,并且 diff 的“右侧”的提交有某些文件 Y 的内容与 X 曾经具有的内容相同,但没有文件 X,那么为什么文件 X 可能被重命名为文件 Y。但是要实现这一目标,我们必须:
我们可以使用 一次性完成所有这三件事git mv
。或者,我们可以使用操作系统的文件重命名器mv
或rename
其他任何名称来重命名该文件,然后运行git add
两次,或git rm
和git add
,或其他任何内容。但运行一个git mv
命令会更方便一些。
这就是 和 的用途git rm
:git mv
它们是实现我们想要的结果的更方便的方法,也就是说,操作Git 索引和工作树中的某些文件。我们两者都不需要。我们可以使用常规操作系统命令进行文件操作,因为工作树中的文件是常规操作系统文件。然后我们可以用 更新 Git 的索引,它可以做所有事情。git add
现在你可能知道你做了什么,但我认为你做了什么(假设是类 Unix 系统的 shell 命令,可能有一些细微的差别):
\nmkdir web/backend\nmv web/main web/backend/main\ngit add web/backend\n
Run Code Online (Sandbox Code Playgroud)\n这告诉 Git 将所有重命名的文件以新名称复制到 Git 的索引中。但 Git 从未取出旧名称,因此这些索引条目及其mode 100644
和blob <whatever>
、阶段零和路径名保留在 Git 的索引中。然后你跑了:
git commit\n
Run Code Online (Sandbox Code Playgroud)\nGit 打包了索引内容,并包含每个文件的副本。
\ngit status
查看索引中感兴趣的部分您的工作树包含的文件少于索引,如果您运行git status
,Git 将运行两个git diff
操作:
我们会将HEAD
提交与 Git 的索引进行比较。由于提交是根据Git 的索引HEAD
进行的,因此这两个将完全匹配,并且没有什么可说的,因此不会从中打印任何内容。git diff
git status
第二个git diff --name-status
操作将比较 Git 索引中的内容和工作树中的内容。由于索引保存了从工作树中神秘消失的文件,Git 会将这些文件称为“D
eleted”。如果使用git status --short
它们,它们将在此处显示为状态字母“D”,如果没有--short
它们,它们将被列为未暂存提交的更改,并且一堆文件被“删除”。
因此,该git status
命令非常有用:
首先,它告诉您“位于”哪个分支,即,当您进行新提交时,哪个分支名称将被更新以保存该新提交的新哈希 ID。
\n然后,如果当前分支有一个上游集,它会告诉您当前分支与其上游的比较。(如果没有,则跳过这部分。)
\n现在它运行两个差异。对于与索引中的提交匹配的文件HEAD
,它们不是很有趣,并且它对这些文件没有任何说明,但是对于已消失的文件、新的文件或不同的文件,它们很有趣:它告诉您这些文件,说它们是为 commit 准备的。对于第二个差异,对于索引和工作树中匹配的文件,这些文件很乏味,因此它什么也没说,但是当文件不匹配时,它会告诉您有关更改或删除的文件。
这里有点奇怪。位于工作树中但不在Git 索引中的文件不会在上述部分中列出。相反,最后一组文件被分成自己的部分。根据定义,这些文件是\xe2\x80\x94\xe2\x80\x94您的未跟踪文件。当前存在于工作树中但不在 Git 索引中的任何文件都是未跟踪文件。
\n使用 Python 代码时,我们会得到字节编译的文件.pyc
或.pyo
文件(取决于优化),其名称__pycache__
是否会出现(取决于 Python 版本)。这些文件使您的工作树变得混乱,如果 Git 抱怨它们是未跟踪的文件,那可能会非常吵闹。4因此, Git 允许您git status
关闭某些未跟踪的文件。这就是内容的一半。.gitignore
这些未跟踪的文件也应该保持未跟踪状态。这就是另一半内容.gitignore
:使用集体添加操作(例如 )git add .
通常会将每个文件添加到 Git 的索引中,包括所有未跟踪的文件。但.pyc
不应添加这些文件。通过列出*.pyc
,.gitignore
你告诉git status
他们闭嘴,而git add
不是添加它们。
不过,关于这一点,您还需要了解另一件事:一旦文件位于Git 的索引\xe2\x80\x94 中,无论它到达那里:通过git add
或从某些现有提交中读取,都不会发生这种情况。没关系\xe2\x80\x94一旦它在那里,该文件就不会被忽略,即使它列在.gitignore
. 您必须删除它,例如,将其从Git 的索引中git rm --cached
删除,以便再次忽略它。从 Git 的索引中删除它意味着一旦您进行了它,它现在就不在您的下一次提交中,这意味着当有人有一天检查该提交时,它也不会出现在 Git 的索引中。这会产生后果,特别是当它已经在其他一些Git提交中时,检查该提交意味着它将位于Git 的索引中。但等你把这个索引的其余部分都冷掉之后,再担心这些。
3既然你知道了,就不再神秘了。
\n4通过通常总结__pycache__
一些未跟踪文件的方式,您在这里只得到一行,这并不那么烦人。使用旧的 Py2k 方案,每个文件只有一行,这非常烦人。git status