Git在推送时创建远程仓库。

31

我一直在试图解决这个问题,但是做起来很困难。我目前正在开发一个开源项目,需要让用户能够向远程存储库进行推送,即使该存储库不存在。我想避免手动登录到服务器并运行git initgit init --bare

由于明显的原因,当我尝试将我的本地存储库推送到在远程服务器上指向不存在存储库的路径时,就会收到以下错误:

fatal: '/var/repositories/myrepo' does not appear to be a git repository
fatal: The remote end hung up unexpectedly

但我想能够运行例如以下命令:

git push origin master
如果还不存在,我想在 /var/repositories 中创建 /myrepo。我应该如何完成这个任务?我猜它是一种全局的 git 配置设置,你可能需要在远程服务器上设置,或者在本地进行 "repository specific" 的 git 配置,但我无法弄清楚。
非常感谢任何帮助!
谢谢!
4个回答

28

目前没有办法使用git push在远程创建一个仓库。你能做的最好的事情就是编写一个单行脚本,类似于这样:

#!/bin/bash
ssh $1 "git init --bare $2" &&
git push ssh://$1/$2

希望您能够进行ssh登录;如果无法登录,则可以使用某种文件系统访问方式,通过手动创建相应的文件/目录结构来创建存储库(可能是在本地创建并复制)。您还可以在此过程中创建一个命名远程端点。
实际上,这是2010年Git调查中最受欢迎的功能之一-所以也许您会在接下来的一年内得到您的愿望!

感谢您的回复,很抱歉我回复晚了。您说的应该确实可行。是的,我有SSH的能力。我实际上使用GitHub上的PushAnd库解决了这个问题。我不完全确定为什么安装了这个库后它就可以工作,但它确实可以!我相信它会安装一个post-push钩子/脚本来运行一些命令来初始化存储库等。 - Michael van Rooijen
为了避免&&跨越两行,为什么不使用bash -e呢?此外,您还可以运行git init --bare /path/to/$2 - Eric
@Eric 这只是一个例子,但我通常更喜欢显式错误处理(不必担心 bash 对 -e 允许和不允许的事情),并且能够进行微调、复制粘贴到其他脚本中等等,而不必担心改变行为。 - Cascabel

10

您可以在远程上编写一个包装脚本,并在authorized_keys行之前添加command =“/path/to/wrapper”。

command="/usr/local/bin/gitaccess" ssh-rsa ...

在这个包装器中,您需要检查SSH_ORIGINAL_COMMAND。Git会发出以下命令:
git receive-pack '/path/provided' # when pushing
git upload-pack '/path/provided' # when pulling

如果 SSH_ORIGINAL_COMMAND 不为空并以其中之一开头,则检查路径,必要时创建存储库,在其中安装所需的任何配置,并执行命令。
如果 SSH_ORIGINAL_COMMAND 为空且您希望为用户提供 shell 访问权限,则调用 shell。
如果 SSH_ORIGINAL_COMMAND 不为空但不以 git 命令开头,如果您希望允许用户使用 shell,则只需执行提供的命令。
以下是一些 Ruby 代码示例。请注意,我没有测试它,有改进的余地(例如我们不应该硬编码 /bin/bash)。
#!/usr/bin/env ruby
orig_command = ENV['SSH_ORIGINAL_COMMAND']
if orig_command.nil?
    # If you want to preserve shell access
    exec '/bin/bash' # not tested, I hope it's interactive if executed this way
end

cmd_parts = orig_command.match /([a-z-]+) '([a-z0-9.\/]+)'/
if cmd_parts.nil?
    # If you want to preserve shell access
    exec '/bin/bash', '-c', orig_command
end

command = cmd_parts[1]
path = '/var/repositories/'+cmd_parts[2] # not secured (could contain '..')

if command == 'git-receive-pack' || command == 'git-upload-pack'
    if not File.directory?(path)
        `git init --bare #{path}` # not secured, I didn't escape path
        # Do any configuration here
    end
    exec 'git-shell', '-c', "#{command} '#{path}'"
end

# If you want to preserve shell access
exec '/bin/bash', '-c', orig_command

您可以在authorized_keys中传递参数给此脚本,以识别用户并选择他们是否应该拥有shell访问权限。我也使用这种方法来控制每个存储库的访问权限。如果您想将此参数带到git钩子中,只需创建一个环境变量即可。

这个回答应该被点赞。我没有尝试过,但是它很合理。 - septianw

4
  1. Checkout and track the branch from the remote:

    git checkout -t origin/funbranch
    
  2. Branch off of it:

    git checkout -b mybranch
    
  3. Push your new one up, it will create a new branch automatically on the remote:

    git push origin mybranch
    

应该显示"已创建新的远程分支origin/mybranch"

如果你仍然遇到"Remote end Hung up"的问题,这听起来像是一个安全问题。你是否正确安装了SSH密钥,并且在远程服务器上有写入权限?

大多数开源项目的工作方式是,你必须创建一个fork(本质上是克隆),用它来进行你的工作,因为大多数情况下你没有对repo的写入权限。当你有更改时,你会向存储库所有者发送一个pull请求,他/她将从你的fork中拉取他们想要的更改。


1
嗨。谢谢回复!虽然这对于大多数查看此问题的人来说是一个有效的答案,但它并不完全是我正在寻找的答案。我需要能够将我的本地 develop 分支推送到例如远程的 staging 分支。但我不想创建一个新分支来完成跟踪和所有操作。您是否知道可能还有其他方法可以实现此目标,而不会对我的本地存储库进行更改?如果问题有点不清楚,请原谅,我尝试做的可能不是很常见 :) - Michael van Rooijen
6
@Michael:你在这里的评论听起来完全不像你的问题。在这里,你正在谈论将一个本地分支推送到另一个不同的远程分支(该分支可能存在或不存在)。然而,你的问题是关于向一个尚未存在必须创建仓库的远程位置推送的。 - Cascabel

2

我知道这是一个老问题,但我在搜索其他内容时偶然发现了它。

我发现将代码推送到远程服务器并在其中创建存储库的最佳方法是使用gitolite。查看其安装指南,非常容易设置,如果更改某些配置设置,您可以让它使用您现有的/var/repositories存储库来存储repos。


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