Git create remote repository on push

2020-02-20 07:36发布

问题:

I have been trying to figure this one out but I am having a hard time doing so. I am currently working on an open source project that requires me to allow a user to push to remote repository without it already existing there. I want to avoid manually logging in to a server and running git init or git init --bare.

For obvious reasons, I get the following error when trying to push my local repository to a path that doesn't point to an existing repository on the remote server:

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

But I would like to be able to run for example the following command:

git push origin master

And have that create /myrepo in /var/repositories if it does not yet exist. How would I be able to accomplish this? I would assume it is some kind of (global) git config setting you would probably set on the remote server, or otherwise a (repository specific) git config locally, but I couldn't figure it out.

Any help would be much appreciated!

Thanks!

回答1:

  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
    

It should say "created new remote branch origin/mybranch"

If you are still getting "Remote end Hung up", it sounds like a security thing. Do you have SSH keys installed correctly, and do you have write permissions on the remote server?

The way most open source projects work, you have to create a fork (a clone inherently) that you use to do your work because most of the time you don't have write permissions to the repo. When you have changes, you will then send a pull request to the repository owner, and he/she will pull from your fork the changes that they want.



回答2:

There is currently no way to use git push to create a repository on the remote. The best you can do is write a one-liner script something like this:

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

Hopefully you can ssh in; if you can't, you could use some kind of filesystem access to just manually create the repo by dumping in the appropriate file/directory structure (maybe create it locally and copy). You might also create a named remote along the way.

This was actually one of the most requested features on the 2010 Git Survey - so maybe you'll get your wish sometime in the next year!



回答3:

You can write a wrapper script on the remote, and prepend command="/path/to/wrapper" to the authorized_keys' lines.

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

In this wrapper you would check SSH_ORIGINAL_COMMAND. Git issues these commands:

git receive-pack '/path/provided' # when pushing
git upload-pack '/path/provided' # when pulling

If SSH_ORIGINAL_COMMAND is not empty and starts with one of these, you check the path, create the repository if necessary, install any configuration you need in it, and execute the command.

If SSH_ORIGINAL_COMMAND is empty and you want to provide users with shell access, you invoke a shell.

If SSH_ORIGINAL_COMMAND is not empty but doesn't start with a git command, if you want to allow users to have shell access, you just execute the command provided.

Here's a bit of Ruby code to demonstrate. Note that I didn't test it and there's room for improvement (for example we should not hardcode /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

You can also pass an argument to this script in the authorized_keys to identify users and choose whether they should have shell access. I also do this to control access on a per-repository basis. If you want to carry this argument to the git hooks, just create an environment variable.



回答4:

I know this is an old question, but I stumbled upon this while googling for something else.

The best way I've found to be able to push to a remote server and have it create a repository if it doesn't exists is to use gitolite. Look at the install guides for it, it's very easy to setup, and if you change some config settings you can have it use your exiting /var/repositories to store the repos.