如何推送到非裸的Git仓库?

170

我通常通过ssh(使用screen和vim)在远程服务器上工作,我在那里有一个Git仓库。有时我不在线,所以我的笔记本电脑上有一个单独的仓库(从我的远程仓库克隆而来)。

但是,由于我通常在防火墙后面或没有公共IP,所以我无法从这个本地仓库拉取到远程端。

我读过应该只推送到裸仓库。那么我应该如何将我的更改推送到我的远程仓库呢?


相关:https://dev59.com/a2ct5IYBdhLWcg3wNqwg - prusswan
3
有两个远程代码库,一个是裸库(bare repo),另一个是普通库。使用钩子(hooks)似乎很麻烦,但据Git Ready官方Git Wiki称,你应该只推送到裸库(bare repo)。这可能是大多数Git仓库主机(如GitHub、Bitbucket)包含后置接收钩子的原因,这样您可以将POST发送到服务器上的URL,运行执行例如git pull github master的脚本。 - Jake Berger
5个回答

155

最佳方案

推送到非裸库的远程仓库,最清晰、最不会混淆和最安全的方法是将其推送到代表您笔记本电脑分支的远程仓库中的专用分支上。

让我们看看最简单的情况,假设您每个仓库只有一个主分支:master。当您从笔记本电脑向远程仓库推送时,不要将 master -> master 推送,而是将 master -> laptop-master(或类似名称)推送。这样推送不会影响远程仓库中当前选定的主分支。从笔记本电脑执行此操作的命令非常简单:

git push origin master:laptop-master

这意味着本地的主分支将被推送到远程仓库中名为 "laptop-master" 的分支。在远程仓库中,你将有一个新的名为 "laptop-master" 的分支,当你准备好时,可以将其合并到你的远程主分支中。

另一种选择

也可以只将主分支(master)推送到主分支(master),但是在非裸露(bare)仓库的当前检出(checkout)分支进行推送通常是不建议的,因为如果您不了解发生了什么,它可能会让人感到困惑。这是因为向已检出的分支推送不会更新工作树(work tree),因此在被推送的已检出分支中检查 git status 将显示与最近推送的相反的差异。如果在推送之前工作树处于脏状态,那么这种情况就会变得特别混乱,这也是为什么不建议这样做的一个很大的原因。

如果你想尝试只推送主分支(master)到主分支(master),那么命令就是:

git push origin

但是当你回到远程仓库时,你最有可能想要执行git reset --hard HEAD以将工作区与已推送的内容同步。这可能是危险的,因为如果在远程工作树中有任何未提交的更改,并且你希望保留它们,它将抹掉它们。在尝试之前,请确保你知道其后果,或者至少先备份!

编辑 自Git 2.3以来,你可以使用“push-to-deploy” git push:https://github.com/blog/1957-git-2-3-has-been-released。但是,将推送到单独的分支,然后合并通常更好,因为它执行实际的合并(因此与merge一样适用于未提交的更改)。


1
在推送laptop-master后自动分支是否可行? - rdoubleui
3
@rdoubleui: 你是指“自动合并”吗?如果是,那不行,因为合并可能需要人工干预才能实现。可能会出现需要解决的冲突。 - Dan Moulding
7
在后续版本中,若要向非裸仓库推送代码,需要先运行git config receive.denyCurrentBranch ignore。请注意保持翻译内容的准确性和原意不变,并简明易懂。 - prusswan
3
谢谢你的夸奖,@DanMoulding。@rdoubleui:你可以将以下命令行保存为Bash函数:git push origin master:laptop-master && ssh user@remotemachine 'cd repos_path && git merge laptop-master' - Rich
@rdoubleui 自动合并,您可以使用gitolite并设置它(有点复杂)使非裸提交所有内容,并使用pre-git触发器将其推送到gitolite(裸)版本。之后,如果无法在非裸中解决合并,则会拒绝您的推送,因此您知道必须先拉取,解决合并并再次推送。我还没有设置这个,但我正在进行中,并认为它可以工作。 - user1115652

153

receive.denyCurrentBranch updateInstead

这个选项 在Git 2.3中添加,如果服务器的工作树是干净的,它会使服务器更新其工作树。

因此,如果您确保在本地拉取之前始终进行提交,并在服务器上保持干净的工作树(为避免合并冲突而应该这样做),那么此选项是一个很好的解决方案。

使用示例:

git init server
cd server
touch a
git add .
git commit -m 0
git config --local receive.denyCurrentBranch updateInstead

cd ..
git clone server local
cd local
touch b
git add .
git commit -m 1
git push origin master:master

cd ../server
ls

输出:

a
b

是否可能像Heroku一样不创建裸库而进行推送? - Rishabh Agrawal
2
@ANinJa 我不明白,这不正是我示例所做的吗? - Ciro Santilli OurBigBook.com
--local 是可选的吗? - Yukulélé
@Yukulélé --local 只影响当前目录,--global 影响所有带有 ~/.gitconfig 的 git 仓库,请参阅 man git-config - Ciro Santilli OurBigBook.com
我能理解“在服务器上保持一个干净的工作树”的原因,但是不明白“在本地拉取代码之前总是提交代码”的理由。这背后的逻辑是什么? - Nandin Borjigin

18

我建议在您的服务器上拥有一个裸仓库和一个本地工作(非裸)仓库。您可以将更改从笔记本电脑推送到服务器裸仓库,然后从该裸仓库拉取到服务器工作仓库。我这么说的原因是,您可能在服务器上有许多完整/不完整的分支,您希望在笔记本电脑上复制这些分支。

这样,您就不必担心在将更改推送到服务器时检出的分支状态会影响服务器工作仓库。


1
但是如果我只有一台笔记本电脑和一台服务器,为什么我需要三个仓库呢?这并不美观。我认为最好禁止在服务器上进行更改。例如,只允许git用户编写repo。 - TZubiri

5
另一种选择是设置反向ssh隧道,这样您就可以拉取而不是推送。
# start the tunnel from the natted box you wish to pull from (local)
$ ssh -R 1234:localhost:22 user@remote

# on the other box (remote)
$ git remote add other-side ssh://user@localhost:1234/the/repo
$ git pull other-side

如果您希望隧道在后台运行

$ ssh -fNnR 1234:localhost:22 user@remote

1
你可以执行以下操作: $git config --bool core.bare true 这可以在裸库或中央仓库中完成,以便接受从非裸库推送的任何文件。 如果在非裸库中执行此操作,则无法将任何文件从非裸库推送到裸库。
如果您通过在PC上创建中央和非裸库来练习GIT,它可能不会在某些PC上显示已推送的文件,但它已经被推送了。您可以通过在中央仓库中运行$git log来检查它。
除此之外,如果您推送到GitHub,则会在那里显示文件。

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