Serving Mercurial over SSH


I’ve been deploying our big Django application to all of our servers using a pretty slick setup with Fabric and rsync.

This worked fine when I was the only developer, working on my local machine and pushing to a Mercurial repository on one of our internal Ubuntu boxes. Since it was all local, I just used the server’s Apache setup and mod_wsgi and didn’t worry about security too much. The Linux box is completely firewalled off from the Intertubes.

However, as we get more people working on the code, and as we deploy to more servers, having the ability to update to and from our Mercurial repository is becoming more important. Just ask Jeff(rey), whose template changes I clobbered this afternoon.

Since the shared hosting on which I wanted to set up my Mercurial repositories doesn’t have mod_wsgi, nor is it really safe to put a ‘foreign’ module like that into a cPanel setup, I had to find another way to serve mercurial securely.

Also, since we’re finding that Apache and its threading model are consuming waaaaaaaayyyy too much memory under load, we’re moving toward lighter weight, single purpose servers for everything anyway.

So…I found mercurial-server.

It gives secure, tightly controlled Mercurial access over a simple SSH connection.

After I found the `.deb` file and was able to use `dpkg` to actually install it, things went pretty smoothly.

Setup instructions are pretty straightforward with the only part that confused me a little was extracting the key from SSH Agent with `ssh-add -L`. I wasn’t using SSH Agent so the directions didn’t work but once I figured out that al I needed in-hand was the public key, I was on my way.

The repositories are kept squirreled away in kind of an odd location that’s not mentioned anywhere, but that doesn’t seem to be much of an issue as long as that directory tree (`/var/lib/mercurial-server/repos/`) is getting backed up, I’m fine with wherever it wants to put it. The reason for that location, far as I can figure, is that the /var/ tree is supposed to be for things:

/var/ Variable files—files whose content is expected to continually change during normal operation of the system—such as logs, spool files, and temporary e-mail files. Sometimes a separate partition.

At any rate, it took a couple of hours to get set up right and, while speeds don’t seem to be quite as good as they were running under Apigche, it’ll work just fine for my automated setup.

Now, to figure out how to get hooks to do all the necessary pushing and pulling for me…

To see how to get this running on CentOS, see my next blog post. What a PITA!

How to Work On Multiple Twisted Branches at the Same Time


I’m very interested in the new HTTP/1.1 functionality that’s pending review in Twisted in two different branches.

They’re all about new Twisted Web Client functionality and HTTP/1.1, and are documented in TwistedWebClient.

Don’t download them, but they are:




The main Twisted version control repository is currently Subversion which isn’t particularly good at merging things or, at least, I’ve never been very happy with the way it works.

So, I asked on the #twisted IRC channel how one would go about working on those two branches simultaneously.

The following conversation ensued:

ssteinerX: Does anyone know how to checkout twisted-branch-expressive-http-client-886-4 and twisted-branch-high-level-web-client-3987 in such a way as they can be used together?
ssteinerX: 3987 depends on the stuff in 886-4 but they’re in completely separate branches
ssteinerX: I forget (thankfully) how to even use svn other than a simple checkout…
ivan: I use git and merge everything into my personal branch
ssteinerX: ivan: have you merged those two particular branches successfully?
ivan: yes
ssteinerX: Is it something you could possibly post (or have posted) to github?
ivan: my git svn mirror of unmodified Twisted code is at
ivan: git svn fetch, make a branch, merge 886-4, merge 3987

So…taking that information, here’s how to make a local branch that contains everything from those two branches together:

First, get whatever is the current “Twisted-svn-git-to…” archive from

I use wget like:

	# wget

Unarchive that, then change to the directory and update it with:

	# git svn fetch

That will pull the latest changes from the svn server right into your git repository.

Then, make a branch of your own to work on. I called mine 886-4+3987 since that’s what it is…

	# git branch 886-4+3987
	# git checkout 886-4+3987
	# git merge expressive-http-client-886-4
  	# git merge high-level-web-client-3987

And there you have it. Everything in both branches, working in one checkout.

You can make a new virtualenv, install this branch to it with the normal `python develop` and go about your business with your new Twisted!

Converting from Git to Mercurial

So, as per my previous post, we’re going to be using Mercurial going forward.

I have several projects in Git already and was doing a conversion to Mercurial.

I did the simple:

	# rm -rf .git 
	# mv .gitignore .hgignore
	# hg init
	# hg status

Unfortunately, this gave me:

	abort: /super-secret/.hgignore: invalid pattern (relre): *~


Turns out, that for Mercurial, you have tell it the format of your .hgignore file.


syntax: glob

Fixed it right up and I’m off and running.

Into the breach!

The WSSW Stack

Choosing The Stack

Ok, so I’ve been plooking around with various web frameworks, even languages, for a couple of years now.

Now, while starting WebSauce Software for real, it’s time to choose a standard toolset. This is what we are going to use to produce our software until further notice.

Unless there’s a compelling reason to change, this is what we’re using.

If something great comes along to replace a component then fine, but it’ll have to be pretty damn good for us to switch.

If it’s great, we’ll switch.

Adapt or die!

First a little history.

We got into the web business about 7 years ago after 25 years of general purpose contract programming which overlapped with about 10 years of software publishing.

I started consulting in about 1982, started publishing software in about 1986, stopped publishing software in 1994, and retired from the software business, sort of, in 1995, had my first son in 2002, and went back to work in 2004-ish.

I did some consulting between 1995 and 2004, but only a handful of really complex, challenging jobs. I was not making a living, I was just taking on work I liked and wanted to do.

When I went back to work, I didn’t know exactly what type of work would be coming up and I wasn’t too worried about it. I’ve always managed to keep busy.

Unfortunately, I had been out of the loop for almost 10 years so most of my old consulting contract clients were gone, companies changed hands, engineers at those companies moved around to parts unknown. In short, I didn’t really have any contacts any more.

So, I rented an office and hung my shingle out to see what would happen.

People kept asking me if we did websites.

So, I said we did.

Now, it’s not that we hadn’t done websites before that for ourselves or for customers, but we weren’t in the business of making websites for other people.

So, now we were, and we did.

Lots of them.

We grew, hired people, had clients, had a stream of new clients, a few big clients, I wrote some nice tools for in-house use that made us more efficient than other companies, we learned the web development business and everything was hunky-dunky.


I hate making new websites for people who don’t already have them.

They have unrealistic expectations of what the site can do for them and especially, how much it should cost. At least people with existing sites have an idea what things cost, and know what the site is doing or not doing for them.

Improving an existing site is way better, for us. Less friction, better
results all’round.

What I do like…

Fixing existing sites

Fixing up an existing site is a blast. We get to leverage all of our cool tools and, because of those tools, we’re very efficient at it. Because of our efficiency, clients get a better deal that they did from their prior company which makes us look good and, since almost everything is automated, we make good profit margins.

Best part? I get paid to spend time ploinking on the tools we use to do customer jobs more efficiently which is the most fun for me.

Doing SEO

Getting sites to rank well in the Search Engines, making sure that their customers can do useful things with their website, and generally helping our customers serve their customers better.

My software engineering background has allowed me to write some tools that do things in this area that nobody else has. We’ll be publishing some of them soon. We’ll let you know ;-).

Writing web applications

Things that are kind of like desktop applications but run in a browser and do things that are appropriately web based. We’ve done integration, custom database editing applications for real estate brokers, inventory control and management against existing, legacy databases that just need a new view to be more useful than they already are, all kinds of stuff. Love it.

How I’ve Written All This Stuff

I’ve written utilities for doing the repetitive parts of SEO and also written web applications for various purposes for clients and for in-house needs.

I was always hunting for the best development toolset both for client applications and for our own internal tools.

I’ve gone through a lot of tools.

So I tried…in no particular order

and God knows how many other frameworks, version control systems, WSGI components, templating languages, and chunks and parts of various solutions.

So…I’ve finally settled

So, after all that trial and error, here’s my toolset.

This is what I’m using from now on unless there’s a compelling reason to use something else. Most of the bigger tools (Django, for example) have or are developing plug-in parts for things like the templating system so these choices are not as rigid as having this list might imply.

Linux Distribution: Ubuntu

I’ve used just about every Linux distribution at one time or another, we host lots of sites on the Centos series, I think one of our in-house boxes is Suse. Then I started using Ubuntu since it seemed to be the one most of the documentation for the tools I was using was written for. I figured there must be some reason for that since it was just too pervasive to be a coincidence. Not a coincidence. It just works better. All of our cloud servers are now fired up with Ubuntu 9.04 server configuration and I run Kubuntu (I absolutely hate Gnome, love KDE). I’m envious of the MacOS-X Aqua theme, only for Gnome so far, but it’s not enough of a reason to switch to Gnome.

Ubuntu has been rock solid, and apt-get blows away any other system package tool I’ve used (yum, nasty RPMs, etc.).

Language: Python

The language I always come back to. I’ve tried other languages. Seems like I’ve tried every other language at one time or another. Last time I counted it was, like, 40 or something including dialects of Basic, Pascal, C, C++, Delphi, various Assembly languages, Perl, Ruby, Awk, SmallTalk, Lisp, Sed, Haskell, and many, many others I can’t even remember. I don’t remember who said it but Python really is executable pseudo code

VCS: Mercurial (hg)

Up until a few months ago, we were Subversion users. I feel dirty even saying it, now. We used Perforce for one job but I hated it the whole time. I always found Subversion annoying; especially trying to merge branches.

The centralized repository always gave me an uncomfortable feeling I never identified until I started using Git on an Open Source project I was working on.

The first time I did a merge, I was hooked. It was painless and it wasn’t a trivial merge either. I had to manually resolve one conflict out of 30 or so changes. It took five minutes. It would have taken all day in Subversion and I would have been swearing the whole time. I was leaning toward Git, not having used any of the other likely suspects much until this announcement.

Then, there’s Google’s support which double sealed the deal.

Since the main Python repository is going to be Mercurial, and since that will likely drive adoption on other projects that have yet to move out of Subversion, and since Mercurial is written in Python, it would be silly to use anything else since there’s really little obviously superior about any other DVCS.

Mercurial is also sure to get lots of loving attention and will pass Git in short order in any area where it’s currently lagging. Fortunately, Git, Mercurial, and Bazaar are similar enough that it’ll be easy enough to switch around when needed.

WebSauce’s projects will all be DVCS’d in Mercurial and I’ll document the setup as soon as I get around to it. The setup, that is…

Desktop App Development: Cocoa/Objective-C

I tried writing my first OS X Application for publication using Python and PyObjc. I had a working prototype but, even with expert help, couldn’t get it to run anywhere but my development system. Next app is pure Objective-C and, if I need Python for something, I’ll run it as an external process and work on getting the results back some way other than being running inside the main application space.

Web Framework: Django

I may not like some of the parts of the Django stack so much but it all hangs together well and, if I get sufficiently dissatisfied with any particular part, I’m sure there will be a way to “fix” it on my own checkout and submit a patch. I’m pretty sure most of the Django pieces are pluggable to some extent and, where they’re not, it would be good of me to help make them so. That’s what Open Source is all about, right?

Web ToolKit: Twisted

Twisted does so many things, and our applications need so many of them, that it’d be silly not to use the grandfather of all things Python and Web.

Sure, it’s a little hard to wrap your head around in the beginning, and there are parts that are dark, deep, and mysterious, but I’ve been hanging around on the mailing list and IRC channel and I’m confident that if I run into a problem, and do my research before asking for help, that I’ll be able to get any problem solved in relatively short order.

Because so much of the rest of our apps require Twisted services, we’re going to run our Django app using Twisted’s WSGI unless we run into problems, Then we’ll fall back to eiter CherryPy’s WSGI, Apache’s mod_python, Apache’s mod_wsgi. Whatever, not a big deal.

Documentation Language: Restructured Text

The documentation format of Python that can be easily converted to everything else.

It’s human-readable in source form, intuitive, and is everywhere in all the tools I use.

No brainer.

Other Tools

The stack really isn’t worth anything unless you can deploy it.

For that, I’m relying on several other Python based tools:


I’m only using the directory template creation of Paste. Paste is for the most part, overgrown and under-focused but the directory templating works well enough for now.

virtualenv, virtualenvwrapper

These allow me to set up an isolated Python environment in which to run my applications. Keeps all the cruft out of the system and gives me an attainable target to deploy.


Allows creation of a completely self-contained app. Virtualenv’s great for development, but this wraps it all up in a one-stop-shopping bundle.


Makes deployment as simple as writing a Python script that does what you want to distribute an application to wherever you want to deploy it.


The documentation tool used on the Python project itself. You can set up a documentation structure in one command, write your docs in reStructuredText, and have it in html, latex, and several other formats in a flash.


Not really part of the deployment stack but from having worked on several open source projects on github with git, I think it’s about the best there is right now. I’m still interested in looking at BitBucket and I’m contributing to a few projects there as well but Github seems to be more mature and has a much more informative and useful interface. LaunchPad is very ambitious, and seems well thought out and pretty all-encompasing. Unfortunately, the only backend it supports is Bazaar. Yuck.


We’ve been using Basecamp for a while now for project management. It’s not perfect but it is the best shared system we’ve found. We’ve tried Google Docs and got addicted to shared documents but the rest of the system doesn’t provide any project management functionality so things tended to get lost in there since there was no way to indicate what was to be done next. Basecamp also has shared documents (Writeboards) and also ToDo Lists and Milestones which make it possible to keep a project moving.


We’re currently using FogBugz to track our bugs in the OS X product that we’re untangling the Python code from and it really is a great bug tracking system.

We’ve been focused mostly in BaseCamp so it will be interesting to see how well they integrate or whether we move to another system for this functionality. An obvious choice would be Trac and, with the buildout script, maybe it won’t be so abominable to install.

For now, that’s it.

I’ll be updating this as I update the toolset but this is it, for now…

Wicked Easy Github Forking

How to fork a github project is described
nicely in the href="">documentation.

Once you’ve read that and understood it, here’s a quick checklist for cloning
a project without all the explanation; just step-by-step.

  1. Login to github
  2. Go to the project you want to fork
  3. Press the “Fork” button; github will make your copy and go to your fork’s home page
  4. Copy the “Your Clone URL” using the handy button at the end of the
  5. Open a Terminal and change to the directory that you want to use as
    the parent of your work (I use ~/work) and clone it with:

  6. # git clone [[paste Your Clone URL here]]
    # cd [[the new clone directory]]

  7. Go back to your browser and click the link next to “Fork of” just
    below your clone’s project name. When you’re back at your parent project’s
    page, click the clipboard icon next to the “Public Clone URL.”
  8. Go back to your Terminal window and type:

    # git remote add upstream [[parent project's Public Clone URL]]

    This gives you a branch in your repository that points to the parent
    project and a shortcut way of getting there i.e. upstream is
    just shorthand for the parent project’s URL.

  9. Get the ‘upstream’ repository, i.e. make a local copy of our parent
    repository with:

  10. # git fetch upstream

  11. Make some changes, the push your repository back up to github:

  12. # git push origin master

  13. Whenever you want to merge the upstream changes into your
    local branch, just run:

  14. # git pull upstream master

    And that will pull the upstream branch down and merge
    those changes with your master branch in one shot.
    Remember to push your branch right after that with:

    # git push

    And you will easily stay in sync with your parent’s repository.

    It is a good idea to do the sequence above i.e. updating from the
    parent branch, then push your changes just before you issue a pull
    request to the owner of your parent project so you can resolve any
    conflicts with your changes instead of having the project owner have
    to do that.

Adding a New Repository to Git/Gitosis

It’s been a while since I used my own git server. I’m pulling together a bunch of management scripts into a toolbox to take with me from server to server and wanted to make a new repository.

Except I had no idea what to do to make a new one and I don’t seem to have written it down last time since it seemed so obvious at the time.

So, here’s the scoop…

To create new repositories:

  • Add the project to the gitosis configuration
  • Make them writable for the user you want to push as
  • Push up the gitosis-admin configuration
  • Create a new home for the project
  • Initialize it as a git repository
  • Copy all your project files into the new repository directory
  • Add them all to the git repository
  • Commit everything
  • Push to the server

For example: let’s assume your username is ‘jdoe’ and you want to create a repository ‘myproject’.

In your clone of your server’s gitosis-admin, edit ‘gitosis.conf’ and add::

  [group exampleproject]
  members = jdoe
  writable = myproject

Commit that change and push the changed gitosis configuration.

    # git commit -a -m "Added new group 'exampleproject' and project 'myproject'"
    # git push

Then create the local repository:

    # mkdir myproject
    # cd mypyroject
    # git init
    # ...copy all your project files in...
    # git commit -a -m "First commit"

Set it up to push to the remote:

    # git remote add myserver

Do some work, add, commit everything then push it up:

    # git commit -a -m "Putting away before first push to server"
    # git push myserver master:refs/heads/master

That’s it. You now have a new project up on your git server and every time you do a git push it’ll go up.

Moving a Gitosis Setup

We’ve done a server shuffle here and now that the dust has settled a bit, I’ve got to get out Gitosis repository back online.

To make things more interesting, I also changed the name of the user under which the whole setup is running. Long story short; create a user in cPanel to be your Gitosis user rather than creating just a unix user with useradd so that the user can be moved etc. using the handy cPanel tools. My original user almost got stranded on the old server since he didn’t show up in any of the cPanel tools I was using to move hosting accounts.

So, on the new server (as per the old post :

	# cd /usr/local/src
	# git clone git://
	# cd gitosis
	# python install
	Traceback (most recent call last):
	  File "", line 2, in 
	    from setuptools import setup, find_packages
	ImportError: No module named setuptools

I downloaded from the Peak website and rather than try to remember anything about how to set it up, I just took the two lines at the top of and stuck’em into the gitosis per the instructions at the top of and reran

When I moved servers, I created a cPanel user with a new name, git_new, different from the one on the old server and copied all the Gitosis stuff into their home directory.

The trick is to get the gitosis setup to recognize everything in its new home and getting at it from my local machine. I’m not comfortable manually twiddling any of the files in the git setup since I’m not 100% sure what’s what so I’ll start by trying to checkout the Gitosis configuration to my local work area.

Since I’ve already created the user in cPanel, I’ll have to figure out how to make sure they’re not able to login by password but for now, I’m just going to get gitosis installed and initialized.

Before I do anything crazy, I’m going to make a quick copy of my original repositories. They’re in /home/git_new/repositories so I just quickly make a backup with:

	# cp -r /home/git_new/repositories /home/git_new/repositories-2009-03-06.bak

I always put the date in backups so that I know when I was messing with something. File dates are unreliable since file changes change dates and it’s not easy to see when something was created. This keeps things straight with very little effort.

I copied my key from my main machine up to the /tmp directory on the new server and ran:

	# sudo -H -u git gitosis-init < /tmp/

I got an error doing this because the /home/git_new/gitosis directory wasn't owned by the git_new user but after fixing that up with:

	cd /home/git_new
	chown -R git_new:git_new gitosis repositories

everything went fine.

Now rerunning the gitosis-init works just fine printing out the "Reinitializing..." message twice as documented.

Unfortunately, the original instructions used the --disabled-password switch on adduser which can't be used here since the user was created through cPanel and neither cPanel's Password Modification or the command line passwd utility can set a disabled password.

After poking around for a while, I finally just dumped the contents of /etc/shadow and saw that, for all the usually disabled accounts, the shadow password was set to !!. I manually edited /etc/shadow, poked in that password, and off we went.

This leaves the user unable to login via password, but they can still login using an ssh key which is exactly what we want. According to shadow(5),
If the password field contains some string that is not valid result of
crypt(3), for instance ! or *, the user will not be able to use a unix
password to log in, subject to pam(7). There are some intricacies and stupidity in the exact configuration and who can login which way but we don't need to go there for this purpose.

NOTE: You can also globally disable password authentication in the ssh server but that's not what we needed here either.

The next step, following my own instructions, is to attempt to clone the configuration repository with:

	# git clone

resulting in:

	fatal: protocol error: bad line length character


Googling around lead to the Git FAQ entry on that error.

Following the instructions there, I just tried ssh'ing in as the user to execute a simple command with:

	# ssh echo testing commands
	Shell access is not enabled on your account!

I had forgotten to enable shell access on the account (it's off by default in my cPanel setup since it's a security risk and so few hosting clients actually need it). If I had gotten stucker I would have used ssh's -v parameter to get verbose output to see where things were falling down.

I went into cPanel and enabled shell access for the account and voilà, out came my stuff!

Since I left my repositories in place in the /home/git_new/repositories directory I was hoping to just be able to check them out normally.

I always create a test repository, available to everyone who has any privileges on any repository, so that we can quickly establish connectivity to the server before messing around with specific privileges on any particular project.

So, the quickest test for a new repository is to just try:

	# git clone

And, since it hadn't clobbered any of my original settings when I reinitialized gitosis, I'm right back in business!

NOTE: Just for fun, I tried to use some of my checkouts from the old repositories, just to see how hard it would be. A simple git pull or git push didn't work and failed in a strange and not worth repeating sort of way.

Just edit the checkout's .git/config and change the [remote "origin"] section's url variable to the current user name and repository hostname and everything seems to work perfectly.

How to do a Full Git Repository Integrity Check

One of my acid tests in the s3fs extravaganza of the last few posts was to initialize and fill a Git repository on the mapped drive.

I wanted to see whether the whole repository was completely Kosher after various operations.

To do so, use:

    # git fsck-objects --full

Git Subprojects

Subprojects with Git

I’m working on a fairly complex piece of OS X software right now; lots of moving parts.

I also have several elves working on these various parts. The parts are very well isolated from the main project in a plug-in-y sort of way.

The main project has some kinky dependencies that I really don’t want to have to help someone set up and there’s no real reason to. In the final build, it’s all there.

I’m paying them to help with the parts they’re good at, not dick around with installation and configuration and such.

This also means that each independent piece has to have its own completely separate test application/suite that demonstrates the component’s functionality while remaining completely uncoupled from the main app.

All is as it should be.

Except for the version control part.

Each component has its own Git repository, and every developer working on the component can check out the whole component’s repository. No problem.

Except that I’ve also got to be able to check out their work into my build tree to make the whole product.

Git Subprojects to the Rescue

I just picked up The Pragmatic Programmers’ Pragmatic Version Control Using Git. Haven’t had much time to get into it yet but I did jump on the chance to celebrate their 5th anniversary with a 30% off coupon. Congratulations to them, they really do publish some fine books and they deserve all their success.

Chapter 8, “Organizing Your Repository” has a section on using Git’s submodules feature to track external dependencies. These will be familiar to Subversion users as svn:externals.

Creating a Submodule

To illustrate, let’s just create a whole new structure starting with the root of the project.

	# cd ~
	# mkdir projectRoot
	# cd projectRoot
	# git init

Pretty straightforward. Make a subdirectory, initialize the git repository in it.

I’m going to follow the book here since there’s already a public repository out there and it’s not like a million people are going to read this drivel and rush out and knock github over.

	# git submodule add \
			git:// \

This creates the submodule hocus under projectRoot. What is not particularly clear is that this initializes the submodule to track the current HEAD of the external repository. It stores the revision number of the head in a configuration file called .gitmodules which you’ll add and track with the repository.

Unlike subversion, the submodule is bound to a particular revision within the external repository and will not follow the repository when modifications are made to it

Running the git submodule command will give a hint of that but it’s not that obvious, especially if you’re used to Subversion’s behaviour.

	# git submodule
	-20cc9ddc65b5f3ea3b871480c1e6d8085db48457 hocus

This shows that there’s a single submodule and will show a ‘-’ sign next to it in the listing since it’s not been initialized.

To get it fired up:

	# git submodule init hocus

That will register the hocus submodule.

To actually pull the contents of hocus, at the revision at which you ran submodule add, into the hocus subdirectory:

	# git submodule update hocus

It is possible to change the commit to which the submodule is bound but that’s for another day (or buy the book!).

There’s a really good (and fast!) overview of this process and the intricacies of checking out a new copy of a project with submodules at the Rubaidh Ltd. blog.

Gitosis — Creating projects and allowing users to access them

Creating Projects, Giving Users Access

NOTE: I apologize for late posting of this — got hung up in my drafts folder and I got busy with other things.

Where we’re at:

Now that we’ve got Gitosis and some users set up, it’s time to add some actual repositories.

Create a new git repository (skip this if you’ve already got one)

  • Create a home for it
        # mkdir foo; cd foo
  • Turn it into a repository

    # git init

  • Add something to it and commit it so we can prove it’s working later:

    # cat gt; README.TXT
    README.TXT — a file with no purpose other than to prove it can be cloned
    # git add README.TXT
    # git commit -a -m “Added README.TXT”

Adding the repository to gitosis-conf and push it to the server

  • Set up access in the gitosis-conf

    First, set up a new group, add users (include yourself!), and indicate what they can write to:

        [group myteam]
        members = jdoe you@yourmachine.local
        writable = foo
  • Push those configuration changes to the server so you’ll have permission on the server to push up the new repository:

        # git commit -m "Added myteam, foo project, gave access to jdoe"
        # git push
  • Change to the directory containing your new repository, and flag the local repository as originating on the server running gitosis in the repository foo.git:
        # cd em>whereverYourRepositoryIs/em>
        # git remote add origin git@YOUR_SERVER_HOSTNAME:foo.git
  • Our local copy has committed changes, push them up to the remote repository:
        # git push origin master:refs/heads/master
  • To test your setup, go to a brand new directory, and attempt to clone your new repository:
        # cd ~
        # git clone git@YOUR_SERVER_HOSTNAME:foo.git
        # cd foo

    You should now have a clone of your repository in ~/foo!

Just for fun, you could run:

	# cd ..
	# diff whereverYourRepositoryIs foo

No output means they’re identical, which they should be except for the notice from diff about the .git directory that exists in both repositories:

	Common subdirectories: whereverYourRepositoryIs/.git and foo/.git