开发者

git: Simple solution for pushing between working copies

What I want to do: On my (ssh remotely accessible) university machine I work on a project which I have put under gi开发者_运维问答t source control (git init, then git commit -a after every change, all works fine). Now I want to want to work on that project on my private machine at home. Should be easy, since git is a distributed vcs, right?

I read the git tutorial, which suggests to do a git pull at university to get the changes done at home. That won't work, since my machine at home is not remotely accessible. So I thought that I'd do a git push at home. That works, but it's complicated (requires git reset at university afterwards, etc.), since non-bare repositories are not designed for pushing.

Question 1: Is there an easier way than adding a additional bare repository to my setup (which would mean that I had: (1) the "main" bare repository, (2) the university working copy, (3) the home working copy)?

<Rant>If I really need that setup, I could have stayed with SVN.</Rant>

Question 2: If that setup is really needed, how do I create that bare repository (git clone --bare, I guess) and make it the "main" repository, i.e., tell the working copies that git push is supposed to go there.

PS: I know that there's a post-receive hook floating around that allows you to push into non-bare repositories. I tried it, but it didn't work well since the git version on the university machine is quite old (1.5.5.6) and misses some commands used by the hook. Updating is not an option, and I'd prefer a solution without third-party scripts anyway.


You really shouldn't push to the checked out branch as it effectively pulls the rug from under the remote working copy. It's then difficult to work out if the working tree is modified because the branch head has moved or if there were also local changes which would be lost by a reset --hard.

The simplest thing to do is to push to a different branch. You can then merge this into the working copy's checkout out branch (or rebase the local branch onto it) when you have access to the remote machine and need to work on it.

From home:

git push origin HEAD:from-home

From 'work':

git merge from-home

You can set up your config to default to a particular push refspec.

e.g.

git config remote.origin.push +master:from-home

A bare repository is often more natural. You can either clone it from an existing repository or, what I usually do, initialize a new repository and push the master branch that I want to it from an existing repository.

Better still, if you're going to use working copies at each location, is to use this trick to directly modify the remote's remotes, rather than a speically renamed branch.

So, on origin, create a remote called 'home' -- you obviously can't fetch from it because of your network configuration. That doesn't matter.

On home, tell it, "When I push to origin, have it update the origin's remote named home:

git config remote.origin.push +master:home/master

Now, things get really slick. From home, run git push origin, and go to origin, and run git status or git branch -a -v -- What you will see is something like: "master is behind home/master by 3 commits and can be fast forwarded."

In other words, using home to push a change to origin's remote named home, is functionally the same as using origin to pull from home.

The one downside here is that you'll need to continually do new git config settings as you create additional branches on home. That's the overhead you pay for your network setup. Thankfully, it's simple and only happens once per branch create.


  1. This question sort of sums up my experience with trying to sync two working copies. Eventually I figured it was more natural and simple to have a bare repository. It's not a "main" repository in the SVN sense - you can have one at the uni and one at home, for example, and push-pull between those.

  2. Somebody will probably post the proper way to set the bare repository as your push target, but without looking at the docs, I would simply delete the working copy and clone it from the bare repository again.


I solved this problem by using git-bundle. On my laptop, I do git pull, to get the upstream changes. This works over ssh, as expected. Then, I do git bundle to put my local changes into a bundle, copy the bundle file to the remote server, and then ssh to the server and do git pull from the bundle file.

From the documentation:

Some workflows require that one or more branches of development on one machine be replicated on another machine, but the two machines cannot be directly connected, and therefore the interactive Git protocols (git, ssh, http) cannot be used. This command provides support for git fetch and git pull to operate by packaging objects and references in an archive at the originating machine, then importing those into another repository using git fetch and git pull after moving the archive by some means (e.g., by sneakernet). As no direct connection between the repositories exists, the user must specify a basis for the bundle that is held by the destination repository: the bundle assumes that all objects in the basis are already in the destination repository.

My repository is small enough that I can add the entire git tree to the bundle by using --all. However, if your repository is bigger, you may want to add only recent changes to the bundle (e.g. with --since=10.days master to get the last 10 days). The docs have lots more examples.

Here's my code. In this case, the laptop and the server both have the repository in the same place: ~/src/ (which is a non-bare repository, i.e. a working copy). The ~/tmp directory exists on both the server and the laptop.

TMPDIR=~/tmp
cd ~/src
git pull $server:~/src/
git bundle create $TMPDIR/bundle --all
rsync $TMPDIR/bundle $server:$TMPDIR
ssh $server "cd ~/src;git pull $TMPDIR/bundle"
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜