我如何让两个git仓库保持同步?

6
这是一个场景。我有两台电脑,"桌面电脑"和"笔记本电脑"。
在桌面电脑上,我执行以下操作:
mkdir git_test
cd git_test
git init
dd if=/dev/urandom of=test.img bs=1k count=1000
git add test.img
git commit -m "Added first image"

然后在笔记本电脑上执行:
git clone [USER]@desktop:/home/[USER]/git_test    
cd git_test
dd if=/dev/urandom of=test2.img bs=1k count=1000
git add test2.img
git commit -m "Added second image"

我希望桌面上的git仓库和笔记本电脑上的git仓库看起来相同。在笔记本电脑上,我执行以下操作:

git push origin master

但是我会得到以下错误信息:

remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: error: is denied, because it will make the index and work tree inconsistent
remote: error: with what you pushed, and will require 'git reset --hard' to match
remote: error: the work tree to HEAD.
remote: error: 
remote: error: You can set 'receive.denyCurrentBranch' configuration variable to
remote: error: 'ignore' or 'warn' in the remote repository to allow pushing into
remote: error: its current branch; however, this is not recommended unless you
remote: error: arranged to update its work tree to match what you pushed in some
remote: error: other way.
remote: error: 
remote: error: To squelch this message and still keep the default behaviour, set
remote: error: 'receive.denyCurrentBranch' configuration variable to 'refuse'.

如何让这两个仓库保持同步?
错误消息似乎表明git具有我所需的功能,但出于某些原因,它似乎是一种更高级的工作流。我曾听人说过,不能向当前所在的分支推送,有人可以阐述一下这个可能的解决方案吗?如果以这种方式解决问题,即我可以在我的笔记本电脑上使用某个分支,在我的台式机上使用不同的分支,然后以某种方式将它们同步起来,使它们相同,那就没问题了。
请注意以下几点:
1. 我不想使用像GitHub这样的集中式仓库,有几个原因。第一是安全性。第二是简单性。第三是由于某些情况(我在这里不会详细说明),我不能指望通过某个远程服务器连接到互联网,我只有通过局域网连接的两台机器。最后,我想学习如何按照我在此处请求的方式进行操作,以获得特定的知识。 2. 我也不想使用一个裸仓库,因为裸仓库的根目录中有很多垃圾。这很难看,很乱。我从svn转换到git的整个原因是,git看起来是一个更干净、更分散的解决方案。另外,我有一些非常不懂技术的人会在台式机的根目录下操作,他们将对垃圾感到非常困惑。我认为git的美丽之处在于,所有东西都隐藏在.git文件夹中。 编辑:显然我没有清楚地表达这个观点。 想象一下我有一个“文档”文件夹,其中包含以下子文件夹:mydata1和mydata2。 这不是一个刻意的例子,这正是我试图解决的问题。 mydata1应该只包含“test.img”这个文件,但现在它却有了branches,config,description,HEAD,hooks,info,objects和refs这样的文件。
我希望能够只需进入mydata1并开始编辑文件,而不是必须通过分支等等才能完成工作。每个子文件夹都有这种目录结构就无法工作。 请不要告诉我要按照这种方式处理事情。请直接回答问题。谢谢。
以上两点是我在此发布此问题的全部原因。如果你有这个问题的答案,请回复。 :) 谢谢!!

我所指的“cruft”是以下内容:“branches config description HEAD hooks info objects refs”,我只想让/home/[User]/git_repo仅包含我正在使用的文件。另外:.git文件夹是隐藏的。 - cat pants
4个回答

12

做到这一点的一种方法是拉取而不是推送。在设置好两台机器后,在第一台机器上执行以下操作:

git remote add origin [USER]@laptop:/home/[USER]/git_test
git pull # Complains about untracked branch while adding the branch you want to track
git branch --set-upstream master origin/master
git pull

现在你可以从任何一台机器上拉取提交记录。如果你想要推送,我不确定如何在分散式设置中进行。


拉取对于我的工作流程绝对有效,只要我不必处理裸仓库或集中式仓库。感谢您的澄清。我将测试这个工作流程并回报。谢谢! - cat pants
1
如果机器即使稍微物理上分开,也有一个不太推送的可能性:为类似于alias git_push="ssh remotemachine 'cd git_test; git pull'"的东西创建一个bash别名。 - cjc343
这绝对是正确的方式。考虑一下如果这两个仓库属于你和另一个人;你不会希望那个人把他的东西倒入你的仓库,他也不会希望你把你的东西倒入他的仓库。将新工作拉取到不同的分支中可能会有很大的不同,而不是直接推送它。 - David Culp
好的,我已经尝试过了,在机器“A”上运行这些命令。第二个命令git branch --set-upstream master origin/master失败了,显示fatal: Not a valid object name: 'origin/master' - cat pants
啊,在此之前只需运行 git pull 命令即可。它会提示你没有指定分支,但也会将 origin/master 添加到你的引用中。 - cjc343
这个完美地运行了,非常感谢!我真的很感激你解决了我提出的两个问题。干杯! :) - cat pants

2
我最近遇到了一个问题,我的服务器和桌面电脑不同步。在网上搜索了一段时间后,我没有找到与我确切情况相关的答案。所以我联系了Rackspace支持团队,他们能够为我提供比我在网上找到的更好的答案。
这个帖子是最接近我的问题的,所以我想在这里分享一下我如何解决这个问题,以防将来有人遇到相同的问题。
以下是出错的原因:
我试图安装Magento的Sweet Tooth插件,并按照我们所有人都要经历的常规步骤安装Magento扩展。这意味着将一堆文件放在我的App、JS和Skin目录中。
在app/design/adminhtml目录中有一个“base”文件夹。如果你知道Magento的任何事情,那么你就知道他们有无数关于不要随意更改基础文件的警告。简而言之——不要这样做。
我认为既然我的本地repo没有“base”目录,我把它放在我的本地机器上就没问题了。不幸的是,当我执行添加操作时,它添加了一个只包含Sweet Tooth文件的“base”目录。当我将其放在服务器上时,它清除了我的整个基础目录,Magento就崩溃了。
幸运的是,我使用了以下命令,回到了之前的服务器状态:
git reset --hard HEAD@{1}
这个命令将你带回到上一个提交,并立即使一切恢复正常。当然,现在我的服务器和桌面电脑不同步了,因为我的服务器现在比本地机器晚一个提交。
更加糟糕的是,我犯了一个非常愚蠢的错误,从我的计算机中删除了扩展文件,并在我的桌面上提交了另一个提交,所以服务器和桌面电脑已经落后两个提交,git 现在在它的缓存中有这些文件。
以下是我如何解决这个问题:
显然,这让你陷入了一个非常烦人的境地,因为在我的服务器上进行git pull操作会每次都清除我的“base”目录。而且由于它比本地机器多两个提交,我不能只回滚一个提交。
我联系了Rackspace,他们在我的心目中现在是神,找到了两种不同的解决方法,取决于你想从服务器端还是桌面端解决这个问题。
事实证明,你真的无法从服务器端解决这个问题,因为文件现在不再在桌面上了。虽然他们建议执行git commit -a,然后从服务器推送,再从桌面拉取,但这行不通,因为文件不再在桌面上了。
所以你必须从桌面端修复它,并且需要将那些该死的文件从缓存中取出。

对于目录使用 git rm -r --cached /folder/directory

以及

对于文件使用 git rm --cached /folder/file

现在这些内容已经不在缓存中,我可以提交并让git知道这些文件已经被删除。

接下来进行了一次推送到主分支。然后只需要从服务器上拉取一下,就完成了!

** 结论和教训 **

这里学到的教训非常重要,我写这篇文章是为了帮助其他程序员解决类似的问题,同时也提醒自己永远不要再犯同样的错误。

  1. 如果你的本地仓库中没有一个与服务器上相同的文件夹,请不要将其添加到本地仓库中,因为这会清空服务器上的目录。

  2. 如果你不小心弄错了,一定要使用git删除文件,而不是从本地文件系统中删除它们。

  3. 有时候 git rest --hard HEAD@{1} 可能是你最好的朋友。

我希望这能帮助其他人解决类似的问题。我花费了数小时阅读在线文档,但却找不到解决这里遇到的复杂性问题的任何信息,因此如果我能帮助其他人节省一些时间并减少一些灰发,那么我的噩梦将为世界做出了一些好事!


1
我可能不明白为什么您不想使用裸仓库,我相信只使用拉取的解决方案应该可以正常工作,但我也相信在使用裸仓库时,您希望隐藏的“杂物”仍然可以被隐藏。
在您的桌面上,如果您:
mkdir git_test
git init --bare git_test

然后:

git clone bare_repo_directory new_location

您可以从克隆库正常地推送和拉取到裸仓库(默认情况下将其引用为 origin),而无需处理它包含的额外内容;克隆库只会有 .git 目录。
要推送,您需要运行:
git push origin master (assuming you're working out of master)

针对您的笔记本电脑:

git clone [USER]@desktop:path_to_bare

克隆副本中的工作应该是正常的。您将从原始库(即裸库)推送和拉取,但不需要直接在其中工作。


1

推送不起作用的原因正是您提到的。默认情况下,如果该存储库(接收推送的远程)的当前工作区指向相同的分支,则无法将其推送到一个分支。因此,如果您的远程已经检出 master,则无法将其推送到 master

两个快速解决方案:

如果您在 repo 中已经有任何提交,请直接checkout一个哈希值,这会使您处于无头状态,这意味着您不在任何分支上:

> git checkout <any-hash>
Note: checking out '<any-hash>'.

You are in 'detached HEAD' state...

如果您想要将内容推送到 master,另一种使用全新存储库的方法就是将初始分支更改为其他名称而非 master

> git symbolic-ref HEAD refs/heads/non-existent

这将使你处于类似于在新存储库中的master的状态,在一个没有提交的分支中。只是你不会获取主分支,因此它将被允许接收推送。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接