Using git as a central repository

I have set up git for my own use--so I can access a project from 'anywhere', and keep version safety if I happen to be working on part X over here, and part Y over here, and can merge if necessary.

However, only one of my development machines has a static IP. My brain is stuck in CVS mode, so I tried to set up git to have that machine be the 'central' server, which all others pull from.

This sort of works. I have a bunch of machines A-C which do a git-pull from the 'master' M. They do git pushes to send the data back.

The problem comes if I do development on the master. First, I can't figure out how to get the central repo to provide the latest version without doing

git reset --hard HEAD

which seems a bit excessive. And if I do development on the central machine before resetting, I'm not sure how to merge it with changes that have already been pushed up.

Something about my mental model is way off. Help?


Solution 1:

You want your central repository to be bare. Say the machine it lives on is named static:

$ ssh static git init --bare /git/myproject.git

This bare repository is a central rendezvous point: it's for pushing to and pulling from, not development.

Do your development on clones of the central repository:

$ cd ~/src
$ git clone static:/git/myproject.git

Even if you're on static, work in a clone:

$ git clone /git/myproject.git

Although you're the only one working on this repository, get in the habit of doing your work on what the git documentation calls topic branches. An immediate benefit of this is that it keeps a clean master, that is you can always pull from your central master branch into your current local repository's master with no merging.

For example:

$ git checkout -b fix-bug-in-foo
$ hack
$ git add file.c file.h
$ git commit -m "Fix ..."

That may not seem like a big deal, but it gives you the freedom to leave the project as represented on that branch in a partially cooked state, or if your cool idea turns out to be a flop, you can easily throw away that branch without breaking anything else in your project that's already working on other branches. Infinite free mulligans!

Maybe you go home that night and added a new feature. The next morning, you

$ git checkout master
$ git pull

to update your local master to reflect what's in the central repository.

But now say you've fixed the foo bug and are ready to include it in your master branch. First you want to integrate it with the changes from last night:

$ git checkout fix-bug-in-foo
$ git rebase master

The rebase command makes your repository look as though you fixed the foo bug on top of last night's new feature. (This is sort of like svn update, but more flexible and powerful.)

Now to get it into your central master:

$ git checkout master
$ git merge fix-bug-in-foo
$ git push origin master

We've been treating master as special, but that's only conventional. You can share work on different branches of different repositories through the git repository on static just as easily.

Solution 2:

If you have a central server with a central git repository, that repository should be a bare repository. Bare repositories don't have working copies of the files in them. Consequently, if you are working on that central machine, you don't work with the central repository directly, but with a local clone.

Solution 3:

This answer is similar to gbacon's answer, but takes an approach that you already have a local repo setup, and want to create a remote master that is treated as a central repo. It's just adding details from a different approach.

I use git to store my dot-config files. I push and pull from what I consider a 'central repo'. It's quite convenient to reset all my dot files through multiple computers.

$ ssh example.com
$ mkdir dotconf.git && cd dotconf.git
$ git init --bare
$ exit

This created an empty bare repo on the repo site.

Now if I already have an existing repo locally, I can push that to the remote site.

$ cd ~/src/dotconf

chdir into the local directory.

$ git remote add origin ssh://example.com/~/dotconf.git

Add the remote repo as the origin, so push/pull will act upon that repo.

$ git push origin master

Push my master to the origin (as previously labelled via the git remote). Now that remote repo is treated as my 'central repo'. All my git push/pull will interact with the origin.

If I go to another host, I can easily pull via clone that repo to a new location.

$ git clone ssh://example.com/~/dotconf.git

If I want to do development ON the remote server, I clone first, then push/pull back into the bare repo.

$ cd ~/src
$ git clone ~/dotconf.git
$ cd ~/src/dotconf
  * do coding *
$ git push
  * check in from another location *
$ git pull

You'll likely have to set your git config --add branch.master.remote origin so git pull doesn't complain that you're not being specific enough. The other alternative is to set your master branch to --track the remote origin. Useful if you have multiple branches.