我查看了你的第二个 Pastebin 链接,发现有很多混乱(这并不奇怪,因为 git 的术语似乎是故意混淆的ing).
我认为最好的办法是先退后一步,看看 git 术语,并使用 Pastebin 中的一些示例。
我们先从“远程”开始。
什么是遥控器?
A remote只是一个名字,比如origin
or staging
or upstream
,它为 git (和您)提供了对应 git 存储库的完整 URL 的短名称。但是 git 利用这个短名称为您提供“远程跟踪分支”,我们还无法描述它。首先我们需要谈谈“分支”。
什么是分支机构?
这是来自pastebin的一些内容:
1. The-Dorfchester:gordo.dev ajh$ git checkout -b trouble
2. M .idea/workspace.xml
3. M wp-content/themes/goTheme2015/blog-loadMore.php
4. M wp-content/themes/goTheme2015/home.php
5. M wp-content/themes/goTheme2015/stylesheets/layout.css
6. M wp-content/themes/goTheme2015/stylesheets/layout.scss
7. Switched to a new branch 'trouble'
8. The-Dorfchester:gordo.dev ajh$ git push trouble
9. warning: push.default is unset; ...
在第 1 行,您创建了一个新的分支名称,trouble
.
In git, the word "branch" has at least two distinct meanings.1 There are branch names, like trouble
, and then there are sequences in the graph of commits stored in the repository, which form actual branches. To distinguish these from branch names, let's call them "commit graph fragments". Since these are unwieldy terms, let's note that the commit graph is, technically speaking, a Directed Acyclic Graph or "DAG". A piece of this graph is therefore a "DAGlet".2
DAGlet 很重要,因为来自 git 的抱怨:
! [rejected] master -> master (non-fast-forward)
当您要求 git 进行推送时,会发生这种情况,该推送将“忘记”DAG 的一部分,即“丢失 DAGlet”。我们稍后再讨论这个问题。
分支和提交
我假设您对“提交”有一个合理的概念,在 git 中,它保存您工作的完整快照 - 更准确地说,是“索引”或“暂存区域”中的任何内容的副本 - 以及提交消息、您的姓名和电子邮件以及提交的时间。不过,他们还有一件非常重要的事情,我们将在下一节中介绍。
现在让我们看一下第 2-7 行。当你这样做时git checkout -b trouble
,git 指出它保留了一堆已修改但未提交的文件作为已修改但未提交的文件。
我知道你正在尝试做一个git push
, and git push
pushes commits,而不是文件。因此,如果您修改了但未提交的文件,这些未提交的更改cannot被推。
(你没有显示git status
在那里,但是git status
确实是查看已修改和未提交文件的情况的最佳命令。它告诉你你现在在哪个分支(这将是trouble
在这里,如果您的分支正在“跟踪”另一个分支,则您的分支领先和/或落后多远。你的git checkout -b
有效地运行git status
但对我们来说。)
这意味着在某些时候,您可能应该运行git commit
进行新的提交。当你do进行新的提交,它会去哪里?
提交添加到分支
每当你进行新的提交时,你的新提交都会有上面的“一件事”:它有一个家长 ID。对于大多数新提交,父 ID 是“当前提交”。这会将新的提交链接回之前的提交,我们可以将其绘制为一个小箭头:
A <- B <- C <- D
在这里,我们从最近(和当前)的提交开始,我称之为D
. D
存储其父提交的 IDC
; C
存储其父级的 IDB
; and B
存储其父级的 IDA
.
我们刚刚绘制了一个 DAGlet:我们选择一个起始提交(在本例中,D
),并且该提交为我们提供了一系列较旧的提交。如果我们进行新的提交——我们称其为E
——它将指向D
:
A <- B <- C <- D <- E
我们有一个更长的 DAGlet。
分支名称从何而来?
分店name让我们找到一个特定的提交。这几乎就是分支名称的全部内容:它是指向提交的指针。如果您在分行master
当你做出一个新的提交时,git 首先找到当前的提交——它有一个又大又丑的 40 个字符的 SHA-1“真实姓名”,有时你会看到这个的缩写版本,比如b66c9f0
— 并从暂存区域、您的姓名和电子邮件、您的提交消息以及此父 ID 进行新提交。然后——这是棘手的部分——git 写了new将 SHA-1 提交到分支名称中。
这样,枝条本身就长大了,枝条也生长了。name指向分支末尾的新“提示提交”。如果它过去结束于D
,你现在有D <- E
与名字master
指向E
代替D
。犯罪E
现在是“tip-most”,并且您的分支(名称)指向E
,整个链条开始于E
形成分支(DAGlet)。这是之前和之后:
[before]
A <- B <- C <- D <-- master
[after]
A <- B <- C <- D <- E <-- master
为什么这一切都很重要?
当你去git push
一些提交,你让你的 git 将提交交给另一个 git(监听远程存储的 URL 的 git),然后要求该 git 设置its分支到您正在推送的最尖端的提交。
不过,假设您要求 git 推送您的master
(犯罪E
)到他们身边并让他们master
指向E
也。但是——无论出于什么原因——他们的master
现在存在,但指向不同的提交:
A <- B <- C <- D <- F <-- master (in their repo)
\
E <-- (your proposed replacement)
如果有礼物套装master
在他们的仓库中指向提交E
,您将让他们使用您拥有的相同 DAGlet:E
指向D
, 指向C
, 等等。犯罪F
——他们拥有而你没有的承诺——将会丢失。
这是什么non-fast-forward
means.这意味着他们的分支名称指向您的提交don't包含在您要推送的任何 DAGlet 中。该承诺指向一些父母,又指向更多父母,依此类推。最终(甚至是立即)这些历史提交会合并在一起,但至少有一个提交是他们拥有而您没有的,如果他们将分支名称指向您的 DAGlet(其副本),那么这些提交就会丢失。
迄今为止的回顾:
git push
:这需要一个遥控器,即一个短名称,例如origin
or staging
提供用于推送的 URL。这是后面的第一个词push
. Any 额外的单词是“refspecs”,我们还没有定义,但现在我们只说它们是由分支名称组成的(这基本上是正确的)。你给 git 一个分支名称,例如master
or trouble
它尝试使用远程上的另一个分支名称将您在该名称下的提交推送到远程。 (Which远程分支名称?好吧,我们稍后会看到其他内容。)
您推送的 DAGlet 应该简单地extend他们的分支,向其添加新的提交。从技术上讲,您要求他们将分支设置为的提交应该是他们已经拥有的提示提交(这是“无实际推送”的情况),或者最终应该指向该提交。您可以添加一个或多个提交,但在序列中的某个位置,您的新提交之一必须指向其现有的提示提交。
或者,您可以推送他们根本没有的分支名称。如果您在远程创建该名称,则不会丢失任何提交。
侧边栏:push.default
让我们考虑第 9 行,warning: push.default is unset
。该警告一直运行到第 28 行。每次运行时都会出现该警告git push
使用遥控器,但没有额外的 refspec 参数。为了让 git 关闭,我建议设置push.default
, 到任一simple
or upstream
.
您可以对每个存储库执行一次此操作,或者在您的个人全局配置中进行设置(通常在其中设置用户名和电子邮件):
$ git config --global push.default simple
例如。
如果你使用simple
,git 将推送您当前的分支(例如,trouble
) 到远程,要求远程更新its trouble
。也就是说,这些推送按分支工作names and simple
要求这里的两个不同的 git(你的,在你的系统上,他们的,在远程系统上)使用相同的分支名称。
这回答了“你的 git 要求他们的 git 更新哪个分支名称”的问题:如果你正在推送分支trouble
,你的 git 会要求他们的 git 更新他们名为trouble
。如果你的 git 正在推动你master
,它会要求他们的 git 更新他们的master
。如果您没有命名分支,您将推送的分支就是您当前的分支,并且没有诸如在其中创建分支之类的棘手问题your拼写的存储库Raymond-Luxury-Yacht,但在遥控器上拼写为 Throatwobbler-Mangrove http://www.imdb.com/character/ch0027524/.
这非常简单,这就是为什么它被称为simple
。还有其他四个选项,但我将在本文中将其保留。
这里出了什么问题?
考虑第 35-37 行:
35. The-Dorfchester:gordo.dev ajh$ git push trouble trouble
36. fatal: 'trouble' does not appear to be a git repository
37. fatal: Could not read from remote repository.
The git push
命令将远程作为其后的第一个单词push
。这个单词trouble
,被视为遥控器,不起作用(您没有名为trouble
). The push
代码充满了历史包袱,因此它尝试使用trouble
之后作为 URL,但这也不起作用。
(我要跳过git show-branch
输出因为某些东西损坏了它,删除了前导空白,使其难以阅读。)
git checkout
,以及这里出了什么问题
Git's checkout
命令(在我看来)不必要地复杂,因为它有太多的操作模式。如果 git 使用单独的命令来“切换到不同的分支”与“从某个分支中检查特定文件,而不更改当前分支”,那就不会那么混乱了。但这些都归结为一git checkout
命令,让我们看一下第 75-79 行:
75. The-Dorfchester:gordo.dev ajh$ git checkout staging master
76. error: pathspec 'staging' did not match any file(s) known to git.
77. error: pathspec 'master' did not match any file(s) known to git.
78. The-Dorfchester:gordo.dev ajh$ git checkout staging
79. error: pathspec 'staging' did not match any file(s) known to git.
更常见的结账形式是git checkout branch-name
,但在这种情况下,您调用的是不同的git checkout
,即git checkout [ branch-name ] [ -- ] path1 path2 ... pathN
,但你可以省略--
。自从staging
不是有效的分支名称,它被解释为path
反而。 (这并不重要master
is有效的分支名称,因为staging
处于唯一的论证位置git checkout
允许使用分支名称。)
在第 80 行,你得到了一个不同的错误:
80. The-Dorfchester:gordo.dev ajh$ git checkout master
81. error: Your local changes to the following files would be overwritten by checkout:
82. .idea/workspace.xml
83. wp-content/themes/goTheme2015/blog-loadMore.php
84. wp-content/themes/goTheme2015/home.php
85. wp-content/themes/goTheme2015/stylesheets/layout.css
86. wp-content/themes/goTheme2015/stylesheets/layout.scss
87. Please, commit your changes or stash them before you can switch branches.
您目前(仍在)在分支上trouble
并且你要求 git 移动到分支master
。要从一个分支移动到另一个分支,git 必须替换工作树中某些文件的内容。哪些文件?
答案有点复杂,但在这种情况下,(至少)某些文件会出现错误,例如.idea/workspace.xml
(1) 存储在最近的提交中current分支; (2) 位于new分支的提示也提交; (3) 该文件的内容new分支是不同的来自当前提交中该文件的内容。
如果工作树中的文件与当前提交中的文件匹配,git 会放心地擦除工作树版本并将其替换为切换到分支(master
,在本例中)提示提交版本。但是你的工作树中的这些文件don't匹配当前提交。我们在原著中看到了git checkout -b
当它运行时git status
.
因此,git 拒绝更改分支,要求您提交更改的文件,或者使用git stash
提交它们(区别在于git stash
承诺他们no分支,而不是当前分支)。
好的,所以你终于提交了它们
现在我们进入第 89-91 行:
89. The-Dorfchester:gordo.dev ajh$ git commit -am "idk"
90. [trouble 820decb] idk
91. 5 files changed, 39 insertions(+), 93 deletions(-)
这在当前分支上进行了新的提交trouble
。然后,它移动分支以指向新提交,其 40 个字符的 SHA-1 开头为820decb
.
现在我们到达第 92 行:
92. The-Dorfchester:gordo.dev ajh$ git push master
这要求你的 git 推送到名为master
。没有,并且您会得到与之前相同的错误。所有这些也吐出了巨大的烦人的“设置你的push.default
" 消息,这让我们到达第 119 行,以及第 125 行:
119. The-Dorfchester:gordo.dev ajh$ git pull master
...
125. The-Dorfchester:gordo.dev ajh$ git push --set-upstream master trouble
这两个都有和以前一样的问题:这个词master
位于远程名称的插槽中,但不是有效的远程。
这将我们带到第 131 行:
131. The-Dorfchester:gordo.dev ajh$ git push --set-upstream staging trouble
终于有一个 git 喜欢的命令了! :-) 这次staging
位于遥控器插槽中,并且trouble
位于“refspec”的位置,并且这两者都有效。 (这--set-upstream
tells git push
一旦推送成功,它应该记录staging
's trouble
作为您当地的“上游分支”staging
。有关这一切含义的更多信息,请参阅这个答案 https://stackoverflow.com/a/33509159/1256452.)
让我们跳过一个错误的检查并继续执行第 154 行(及其随之而来的成功消息):
154. The-Dorfchester:gordo.dev ajh$ git checkout master
155. Switched to branch 'master'
156. Your branch is behind 'staging/master' by 16 commits, and can be fast-forwarded.
157. (use "git pull" to update your local branch)
这个有效。并不是因为成功git push
不过:这次成功了,因为你终于承诺了trouble
分支。这使得修改后的文件安全地存储在存储库中,因为新提交的 40 个字符的“真实名称”SHA-1 ID 开头为820decb
.
一旦它们被提交,那么你的工作树是干净的,git 就可以了git checkout master
。你也推了你的trouble
到名为“staging”的遥控器,为其命名trouble
也有,但这对于git checkout master
step.
第 155 行确认成功。第 156 行是以下输出git status
,告诉你你的master
位于其上游(你的 git 称之为staging/master
)比其上游提交了 16 次(他们有 16 次提交是您没有的),并且并不“领先”其上游。再次,请参阅我的其他答案(上面已链接)以了解更多信息。
然后你跑了git pull
,这实际上只是git fetch
其次是git merge
(它的目的是作为这两个步骤的方便包装,但事实证明第二个是wrong对于大多数人来说,这是一个步骤,所以它是可配置的)。这git merge
进行了“快进”合并,这意味着您没有新的提交,而他们有新的提交,因此您的 git 能够“将分支向前滑动”到新的分支提示,将他们的新 DAGlet 添加到您现有的 DAG没有任何大惊小怪。
这让我们一直到第 388 行(及其响应行):
388. The-Dorfchester:gordo.dev ajh$ git commit -am "pull from staging/master to master, idk"
389. On branch master
390. Your branch is up-to-date with 'staging/master'.
391. nothing to commit, working directory clean
This git commit
命令发现没有任何可提交的内容,并且没有进行任何新的提交。你的分行master
没有添加新的提交,并且分支提示与以前相同。
392. The-Dorfchester:gordo.dev ajh$ git push master
这与之前的错误相同:git push
首先想要一个遥控器的名称。喷出的大水大约push.default
让我们一路:
419. The-Dorfchester:gordo.dev ajh$ git push staging master
这是正确的,但我们刚刚看到您的git commit
早些时候没有添加新的提交。所以你的 git 会调用 gitstaging
并发现没有什么可做的:
420. Everything up-to-date
它什么也没做(并且成功了)。
(顺便说一下,关于push.default
此处省略,因为您给出了git push
both一个遥控器and参考规格。这push.default
设置是推送应该做的,如果你don't给出一个参考规范,即,如果你只给它一个遥控器,或者什么都不给它。如果你给git push
什么都没有,它会根据当前分支的上游设置确定要使用的遥控器。)
1How many meanings depends on how you count some finer divisions. See this question https://stackoverflow.com/q/25068543/1256452 for more.
2Be aware that DAGlet is my own invention, so if you start using it, you may have to define it for your audience.