Synchronize git repositories between deskop and laptop

I sometimes work on my laptop and sometimes on my desktop computer. This is a little demonstration showing how I synchronize the repository on the laptop with the repository on the desktop.

Let’s say I originally created my git repository on the laptop:

david@laptop$ mkdir ~/repo
david@laptop$ cd !$
david@laptop$ git init
david@laptop$ echo "hello world" > hello.txt
david@laptop$ git add hello.txt
david@laptop$ git commit -m 'Added hello world'

To continue to work on my desktop I’ll just clone the repository on my desktop:

david@desktop$ git clone ssh://192.168.0.42/home/david/repo
david@desktop$ cd repo

Let’s say I do some commits on the desktop now:

david@desktop$ for i in $(seq 5); do echo $i >> hello.txt; \
git commit -am "added $i"; done

Now I can simply push them back to the laptop:

david@desktop$ git push

Let’s go back to the laptop now:

david@laptop$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD ..." to unstage)
#
#	modified:   hello.txt
#

We notice it say’s the file has been modified. The reason is that the previous push from the laptop didn’t update the working directory, as a diff shows:

david@laptop$ git diff --cached
diff --git a/hello.txt b/hello.txt
index 1c8a100..3b18e51 100644
--- a/hello.txt
+++ b/hello.txt
@@ -1,6 +1 @@
 hello world
-1
-2
-3
-4
-5

So we’ll just checkout the version that has been pushed previously:

david@laptop$ git checkout -f HEAD

Let’s do some work on the laptop:

david@laptop$ git checkout -b dev
david@laptop$ echo "this is a new file" > new.txt
david@laptop$ git add new.txt
david@laptop$ git commit -m 'Added new file'
david@laptop$ git checkout master
david@laptop$ echo "6" >> hello.txt
david@laptop$ git commit -am 'Added 6'

Now to push those changes to the desktop we have to add the desktop as a remote.

david@laptop$ git remote add desktop ssh://192.168.0.12/home/david/repo
david@laptop$ git push desktop

This will push the latest changes in the master branch. To push the dev branch:

david@laptop$ git push desktop dev

Now let’s rebase dev onto master:

david@laptop$ git rebase master dev

With the rebase we went from:

hello -- added 1 -- added 3 -- added 4 -- added 5 -- added 6   <-- master
                                              \
                                              added new file  <-- dev

To:

hello -- added 1 -- added 3 -- added 4 -- added 5 -- added 6   <-- master
                                                         \
                                                         added new file  <-- dev

If we now try to push from the laptop to the desktop it will fail:

david@laptop$ git push desktop dev
david@192.168.0.12's password: 
To ssh://192.168.0.12/home/david/repo
 ! [rejected]        dev -> dev (non-fast forward)
error: failed to push some refs to 'ssh://192.168.0.12/home/david/repo'

The reason is that it is not a fast forward push, so what we have to do to push anyway is force the push:

david@laptop$ git push desktop dev -f

In summary, if you have done some changes which result in a non-fast forward push you can push those change anyway using the ‘-f’ switch.

WARNING: Non-fast forward pushes are disabled by default to prevent you from losing changes that are on the remote repository and that are not in your local repository. If you do accidentally lose some changes on the remote end, you can log on to the remote machine, use git reflog to find the commit that was the HEAD before the forced push was executed and establish that commit as the HEAD again using git reset –hard.

Now let’s see what happens if you try to do a non-fast forward pull. First we’ll have to do some changes that will result into a non-fast forward merge. For instance, let’s squash some commits using interactive rebase:

david@laptop$ git checkout master
david@laptop$ git log --pretty=oneline
3003e24db3933cdcf0b337efee105bb8d5fab162 Added 6
addeee530032c74354b119fd0afd5370ebcec744 added 5
94c3719981b10553b3dba4d34c56c4c383769bee added 4
8ed90f1eb2bafc66a238b2fff3a85e579f782b3c added 3
c499db8d72aab61eb275d38df72bcaddb3a5e263 added 2
5fd3b26ffcdd611e6f58e5ec4a7ed8ce34ffe546 added 1
0efc99fff3e258961c321b529930bc56c5d91c39 Added hello world
david@laptop$ git rebase -i master~6

Your editor will be opened with the following contents:

pick 5fd3b26 added 1
pick c499db8 added 2
pick 8ed90f1 added 3
pick 94c3719 added 4
pick addeee5 added 5
pick 3003e24 Added 6

# Rebase 0efc99f..3003e24 onto 0efc99f
#
# Commands:
#  pick = use commit
#  edit = use commit, but stop for amending
#  squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Now let’s squash some commits:

pick 5fd3b26 added 1
pick c499db8 added 2
pick 8ed90f1 added 3
squash 94c3719 added 4
squash addeee5 added 5
squash 3003e24 Added 6

# Rebase 0efc99f..3003e24 onto 0efc99f
#
# Commands:
#  pick = use commit
#  edit = use commit, but stop for amending
#  squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.

Save and quit and your editor will open again for a new commit message. Edit the new commit message, save and quit. Now the log should look similar to this:

david@laptop$ git log --pretty=oneline
857596e692c7fd98d019dae2630dc320f5c95177 added 3 added 4 added 5 Added 6
c499db8d72aab61eb275d38df72bcaddb3a5e263 added 2
5fd3b26ffcdd611e6f58e5ec4a7ed8ce34ffe546 added 1
0efc99fff3e258961c321b529930bc56c5d91c39 Added hello world

Now let’s go back to the desktop and see what happens if we do a pull now:

david@desktop$ git co -f master
david@desktop$ git pull
david@desktop$ git log --pretty=oneline
c4b80bae8d6a86373fac45e16c89608a6009e8fc Merge branch 'master' of ssh://192.168.0.42/home/david/repo
857596e692c7fd98d019dae2630dc320f5c95177 added 3 added 4 added 5 Added 6
3003e24db3933cdcf0b337efee105bb8d5fab162 Added 6
c499db8d72aab61eb275d38df72bcaddb3a5e263 added 2
addeee530032c74354b119fd0afd5370ebcec744 added 5
5fd3b26ffcdd611e6f58e5ec4a7ed8ce34ffe546 added 1
94c3719981b10553b3dba4d34c56c4c383769bee added 4
8ed90f1eb2bafc66a238b2fff3a85e579f782b3c added 3
0efc99fff3e258961c321b529930bc56c5d91c39 Added hello world

As you can see the pull doesn’t rewrite the history, i.e. the log looks different now on the laptop and on the desktop. I find that confusing, I’d prefer having the same commit history on all my machines. Let’s undo that last pull:

david@desktop$ git reset --hard HEAD^
david@desktop$ git log --pretty=oneline
3003e24db3933cdcf0b337efee105bb8d5fab162 Added 6
addeee530032c74354b119fd0afd5370ebcec744 added 5
94c3719981b10553b3dba4d34c56c4c383769bee added 4
8ed90f1eb2bafc66a238b2fff3a85e579f782b3c added 3
c499db8d72aab61eb275d38df72bcaddb3a5e263 added 2
5fd3b26ffcdd611e6f58e5ec4a7ed8ce34ffe546 added 1
0efc99fff3e258961c321b529930bc56c5d91c39 Added hello world

What I’d like to do is something equivalent to a push –force but with a pull. The solution is using the plus sign with pull:

WARNING: The same caution as to a push –force applies, you can loose changes that are on your local repository but not on the remote repository when using a pull with the plus sign. Although, like in the previous warning, you can probably use git reflog and git reset –hard to get those changes back.

david@desktop$ git pull origin +master:master

The first master references the remote master branch and the second references the local master branch. If we now look at the commit history we can see that it is the same as on the laptop:

david@desktop$ git log --pretty=oneline
857596e692c7fd98d019dae2630dc320f5c95177 added 3 added 4 added 5 Added 6
c499db8d72aab61eb275d38df72bcaddb3a5e263 added 2
5fd3b26ffcdd611e6f58e5ec4a7ed8ce34ffe546 added 1
0efc99fff3e258961c321b529930bc56c5d91c39 Added hello world

In summary, the equivalent pull operation to a “git push –force” is a pull using the plus sign.

Finally one more WARNING: do not force push to repositories from which other people have already pulled as that may cause problems for them. See problems with rewriting history in the git manual.

Advertisements

4 Responses to Synchronize git repositories between deskop and laptop

  1. Pieter says:

    What you are doing here is very dangerous and will result in loss of data if you haven’t committed some stuff on one side and then push to it.

    Why not use normal git practices? This seems much too complicated without any obvious benefit.. just use pull –rebase or push to non-checked out branches.

  2. davitenio says:

    As I understand it the problem with git push -f is not loosing uncommitted changes but loosing committed changes that are only on the end you push to but not on the end you push from. Uncommited changes on the remote end are not a problem because a git push -f will not change the working directory on the remote end.

    Again, what you might loose are committed changes which are only in the remote end and not in the local repository you are pushing from. Nevertheless, I don’t think it is that dangerous because you can recover the lost commits with the help of git reflog.

    Regarding your question, I mostly do use git push without force. But when you rewrite history (squashing, deleting previous commits…) or when you did some rebasing, a normal git push without force might fail.

    And regarding pull –rebase, I actually didn’t know about that option, but I just experimented with it a little bit and while it seems to work well to pull from a repository where some rebasing has been done I do get conflicts if the remote repository has deleted, squashed, edited… previous commits.

    So I use push -f and pull with a plus sign because I often rewrite history (which IS dangerous IF you already published that history, but I only force push and pull private branches).

  3. Great article.

    But my personal experience says to avoid the -f “force” option out, unless I don’t have any other option.

    For example, I recommend always to do your squashing before pushing to a remote server. If you forgot it, a few extra commits never hurt anyone (contrary to data loss).

  4. […] Synchronize git repositories between deskop and laptop […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: