所以我在理解Git时遇到了一些麻烦。我在Windows上使用它,带有远程(GitLab安装)repo。我有一个主分支的本地副本,我非常努力地经常使用git pull
来保持我的本地副本是最新的。
所以这是发布时间(当然......当发生这种情况时不是吗?)并且我想检查我已经改变的ONE文件。我键入git pull
但收到错误:
error: Your local changes to the following files would be overwritten by merge:
<path>/filename.py
Please, commit your changes or stash them before you can merge.
Aborting
我使用git diff
,当然,另一位开发人员在我试图提交的同一地点修正了一个拼写错误。快速git status
显示:
Your branch is behind 'origin/master' by 4 commits, and can be fast-forwarded
所以,不知所措,我将更改提交到我的本地仓库。
git add <filename>
git commit -m "reworking to ignore logs"
现在,我可以git pull
成功。
git pull origin master
...
Merge made by the 'recursive' strategy
file1
file2...
....
<filename>
5 files changed, 1 insertion, 63 deletions...
现在,我可以将我的更改推送到远程仓库
git push
但是:这是我的问题。在远程服务器上,它显示了我的提交以及我提交后由本地pull
引起的文件更改。也就是说,它基本上表明远程文件已更改,但实际上已更改为远程存储库中的 ALREADY 。
我的老板惊慌失措地问我为什么要更改所有这些(其他四个)文件,我无法回答。我们最终检查了两个提交(当前和我之前的更改)并对它们进行区分,以证明只有我的一个文件的更改才有所不同,然后我们才松一口气。
所以我的问题是:我错过了什么?为什么Git认为我想看到我更改了文件以匹配遥控器中的远程HEAD?我见过this question about git squash
ing,并意识到有一个git rebase
也可以帮助我。有人可以帮我理解为什么Git会这样做吗?我觉得好像我并没有完全接受“分布式”范式的转变。
答案 0 :(得分:1)
显然,你和你的老板都错过了同样的事情,你的老板也会感到恐慌。 : - )
这是你所做的,以图形形式绘制为提交。最初你在你的回购中有这个:
... - F - G <-- HEAD=master, origin/master
其中G
是当时最新版本。但是在一个文件中存在拼写错误,因此您在工作树中编辑了该文件,从而解决了问题。 (我将在下面方便地命名文件typo
。)
$ vim typo
...
你还没有提交,但你确实改变了它。然后你做了:
$ git pull
给你的错误信息。
现在,在幕后,git pull
只是git fetch
后跟git merge
。那么让我们来看看git fetch
做了些什么。似乎其他开发者做了四次提交;让我们将它们绘制到commit-tree的图形表示中:
H - I - J - K <-- origin/master
/
... - F - G <-- HEAD=master
git merge
失败,因为其他人也修改了文件typo
。
所以,现在你做了:
$ git add typo
$ git commit -m "reworking to ignore logs"
这会在您当前的分支上添加一个新的提交(无论HEAD
指向哪个),这显然是master
。这给出了一个如下所示的提交树:
H - I - J - K <-- origin/master
/
... - F - G ------------- L <-- HEAD=master
现在您再次运行git pull
,这只是fetch
,然后是merge
。此次提取无法获取(提交H
到K
,标签origin/master
已全部处理完毕)。合并然后将提交K
(origin/master
)合并到当前分支master
,创建新的合并提交M
:
H - I - J - K <-- origin/master
/ \
... - F - G ------------- L - M <-- HEAD=master
如果您现在git push
结果,则提交M
,将您的更改L
合并为合并的第一个父级,并将其提交K
合并为第二个父母。
这是生成M
(正如评论中提到的hobbs)所发生的事情的实际历史,并且完全正常。但是,你(和你的恐慌老板)可能更愿意构建一个不同的历史记录,假装你在之后注意到typo
中的问题,你选择了提交H
到{{1 }}。要做到这一点,您需要“修改”您的提交(K
,以上),使其位于L
之上。
您可以使用origin/master
执行此操作(与我在键入此长文件时出现的hobbs' answer一样),或者在执行git pull --rebase
后使用普通的git rebase
执行此操作。在这种情况下,您要做的是对提交git fetch
中的更改进行复制(人们通常拼写为“commit L
”):
L'
此时,您不再需要提交 H - I - J - K - L'
/
... - F - G ------------- L
- 因为git的“reflogs”,它会在回购中停留90天左右,但它会从正常{{1}消失例如,输出 - 所以你只需要稍微重新绘制一下这个图形,将该行留给L
,然后像这样贴上标签:
git log
这正是命令序列中的L
:
H - I - J - K <-- origin/master
/ \
... - F - G L' <-- HEAD=master
会做的。 (或者,再次,您可以提交,然后使用git rebase
,这只会让git使用$ git add typo
$ git commit -m "reworking to ignore logs"
$ git rebase
- 然后 - git pull --rebase
序列,而不是fetch
- 然后 - {{ 1}}。如果没有东西可以获取,就像在这种情况下那样,就像运行rebase
一样。)
答案 1 :(得分:0)
当您git pull
在具有本地更改的分支上,并且还有上游更改时,pull会将上游更改合并到您的本地更改中,从而创建合并提交。当您推送时,此合并提交将被发送到服务器并成为您看到的历史记录的一部分。
可能有所帮助的两件事:
git pull --rebase
来提取上游更改并在其上重新定位您未经删除的本地工作。这导致了更清晰的历史,除了反历史重写纯粹主义者之外,通常被认为是一种改进。