How to push to a non-bare Git repository?
Asked Answered
M

5

175

I usually work on a remote server via ssh (screen and vim), where I have a Git repository. Sometimes I'm not online, so I have a separate repository (cloned from my remote) on my laptop.

However, I can't pull from this repository on remote side because I'm usually behind a firewall or I don't have a public IP.

I've read that I should push just to a bare repository. How should I then push my changes to my remote repository?

Macedo answered 19/11, 2009 at 16:2 Comment(3)
related: #12266229Inez
have 2 remote repos, a bare and a normal, and use hooks. seems like a hassle, but according to git ready and the official git wiki, you should only push to a bare repo. this is probably why most git repo hosts (e.g. GitHub, Bitbucket) include post-receive hooks, so you can POST to a URL on your server which runs a script that executes for example git pull github master.Patagium
Possible duplicate of What is this Git warning message when pushing changes to a remote repository?Uthrop
H
156

receive.denyCurrentBranch updateInstead

This options was added in Git 2.3, and it makes the server update its working tree if it is clean.

So if you ensure that you always commit before you pull locally, and keep a clean working tree on the server (which you should do to avoid having merge conflicts), then this option is a good solution.

Sample usage:

git init server
cd server
touch a
git add .
git commit -m 0
git config --local receive.denyCurrentBranch updateInstead

cd ..
git clone server local
cd local
touch b
git add .
git commit -m 1
git push origin master:master

cd ../server
ls

Output:

a
b
Heidiheidie answered 7/2, 2015 at 10:58 Comment(6)
is it possible to push without creating a bare repo like heroku doesCorrianne
@ANinJa I don't understand, isn't that exactly what my example does?Heidiheidie
is --local optional?Albatross
@Yukulélé --local affects only current directory, --global affects all git repos with ~/.gitconfig, see man git-config.Heidiheidie
thank @CiroSantilli新疆改造中心六四事件法轮功 after reading the doc I confirm that it is not necessary "(you can say --local but that is the default)"Albatross
I can see the reason for "keeping a clean working tree on the server", but cannot see that for "always committing before you pull locally". What's the rationale behind that?Pharyngitis
I
159

Best Option

Probably the cleanest, least confusing, and safest way to push into your non-bare remote repository, is to push to dedicated branches in the remote that represent your laptop branches.

Let's look at the simplest case, and assume you have just one branch in each repo: master. When you push to the remote repo from your laptop, instead of pushing master -> master, push master -> laptop-master (or a similar name). This way the push doesn't affect the currently checked-out master branch in the remote repo. To do this from the laptop, the command is pretty simple:

git push origin master:laptop-master

This means that the local master branch will be pushed to the branch named "laptop-master" in the remote repository. In your remote repo, you'll have a new branch named "laptop-master" that you can then merge into your remote master when you are ready.

Alternate Option

It's also possible to just push master -> master, but pushing to the currently checked-out branch of a non-bare repo is generally not recommended, because it can be confusing if you don't understand what is going on. This is because pushing to a checked-out branch doesn't update the work tree, so checking git status in the checked-out branch that was pushed into will show exactly the opposite differences as what was most recently pushed. It would get especially confusing if the work tree was dirty before the push was done, which is a big reason why this is not recommended.

If you want to try just pushing master -> master, then the command is just:

git push origin

But when you go back to the remote repo, you'll most likely want to do a git reset --hard HEAD to get the work tree in sync with the content that was pushed. This can be dangerous, because if there are any uncommitted changes in the remote work tree that you wanted to keep it will wipe them out. Be sure you know what the consequences of this are before you try it, or at least make a backup first!

EDIT Since Git 2.3, you can use "push-to-deploy" git push: https://github.com/blog/1957-git-2-3-has-been-released. But pushing to a separate branch and then merging is usually better since it does an actual merge (hence works with uncommitted changes just like merge does).

Iceberg answered 19/11, 2009 at 16:52 Comment(5)
Is it possible to automate the branching after pushing the laptop-master?Canaanite
@rdoubleui: Did you mean "automate the merging "? If so, no, it's not possible to automate the merge because merges are not guaranteed to be possible without human intervention. There may be conflicts that need to be sorted out.Iceberg
in later versions(?), git config receive.denyCurrentBranch ignore has to be done before pushing to non-bare reposInez
Great answer @DanMoulding, thanks. @rdoubleui: you could always save a command line like the following as a bash function: git push origin master:laptop-master && ssh user@remotemachine 'cd repos_path && git merge laptop-master'Selfrealization
@Canaanite to automate the merging, you can use gitolite and set it up (a bit complicated) to make the non-bare commit everything dirty and push it onto the gitolite (bare) version using the pre-git trigger. After that if the merging can't be resolved in the non-bare, it will reject your push, so you know you have to pull first, resolve the merges and push again. I haven't yet set this up, but I'm in the process and think it can work.Teetotalism
H
156

receive.denyCurrentBranch updateInstead

This options was added in Git 2.3, and it makes the server update its working tree if it is clean.

So if you ensure that you always commit before you pull locally, and keep a clean working tree on the server (which you should do to avoid having merge conflicts), then this option is a good solution.

Sample usage:

git init server
cd server
touch a
git add .
git commit -m 0
git config --local receive.denyCurrentBranch updateInstead

cd ..
git clone server local
cd local
touch b
git add .
git commit -m 1
git push origin master:master

cd ../server
ls

Output:

a
b
Heidiheidie answered 7/2, 2015 at 10:58 Comment(6)
is it possible to push without creating a bare repo like heroku doesCorrianne
@ANinJa I don't understand, isn't that exactly what my example does?Heidiheidie
is --local optional?Albatross
@Yukulélé --local affects only current directory, --global affects all git repos with ~/.gitconfig, see man git-config.Heidiheidie
thank @CiroSantilli新疆改造中心六四事件法轮功 after reading the doc I confirm that it is not necessary "(you can say --local but that is the default)"Albatross
I can see the reason for "keeping a clean working tree on the server", but cannot see that for "always committing before you pull locally". What's the rationale behind that?Pharyngitis
N
18

I would suggest to have a bare-repository and a local working (non-bare) repos in your server. You could push changes from laptop to server bare repo and then pull from that bare repo to server working repo. The reason I say this is because you might have many complete/incomplete branches in server which you will want to replicate on the laptop.

This way you don't have to worry about the state of the branch checked out on server working repo while pushing changes to server.

Nicolanicolai answered 7/3, 2013 at 16:34 Comment(1)
But if I only have a laptop and a server, why would I have 3 repos? It isn't aesthetic. I find it's better to disable changes in the server. For example by only allowing git users to write the repo.Yelmene
K
5

Another option is to setup a reverse ssh tunnel so that you can pull instead of push.

# start the tunnel from the natted box you wish to pull from (local)
$ ssh -R 1234:localhost:22 user@remote

# on the other box (remote)
$ git remote add other-side ssh://user@localhost:1234/the/repo
$ git pull other-side

And if you want the tunnel to run in the background

$ ssh -fNnR 1234:localhost:22 user@remote
Kamin answered 23/5, 2015 at 16:58 Comment(0)
C
1

You can do:

$git config --bool core.bare true

this can be done in bare or central repository so that it will accept any files that are pushed from non bare repositories. If you do this in non bare repository then we cannot push any files from non bare to bare repository.

If you are practicing GIT by creating central and non bare repo in PC it might not show the pushed files in some PC's but it has been pushed. you can check it by running.

$git log in central repo.

Other than if you push to GitHub it will show the files there.

Coarctate answered 20/1, 2018 at 17:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.