32

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!

4 Answers 4

29

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!

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the response, and sorry for my late response. What you said should indeed work. Yes I have the ability to SSH. I actually solved this problem using the PushAnd library on GitHub. I am not entirely sure why it works with this library installed, but it does! I believe it installs a post-push hook/script that runs some command to initialize a repository and such.
To avoid &&over two lines, why not using bash -e? Also you can run git init --bare /path/to/$2.
@Eric It was just an example, but I usually prefer explicit error handling (don't have to worry about what bash does and doesn't allow with -e) and being able to tweak, copy-paste into other scripts, and so on without worrying about changing behavior.
11

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.

Comments

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 

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 Comments

Hi. Thanks for the reply! While this is a valid answer for probably most people viewing this question, it is not exactly the one I was looking for. I need to be able to push my local develop branch to for example a remote staging branch. But I do not wish to create a new branch just to accomplish the tracking and all. Do you know of any other way this might be accomplishable, without doing changes to my local repository? Sorry if the question is a bit unclear, what I'm trying to do is probably not very commonly done. :)
@Michael: Your comment here doesn't sound at all like your question. Here you're talking about pushing one local branch to a different remote branch (which may or may not exist). Your question, however, was about pushing to a remote where the repository doesn't yet exist, and must be created.
2

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.