1

foreword ;-) : Most people (including me) work with git in a client-server mindset I think („there's this remote repo on the dedicated server, from which you pull and push and not touch otherwise...“) I recall however, that git is basically absolute peer-to-peer, there is not truly a central server.

So, here's my question:

Are there any problems to have one git repo my machine (~/frank/repo), and one on my “household” fileshare (smb://myshare/somewhere) connecting them mutually to each other as remote repo and work on either one?

  • The benefit being, that at home several PCs in the LAN could directly work on that fileshare server and/while when I am on the road, I will work on my laptop.
  • Doing the pulling/rebasing next time I hit home. So yeah, neither side can push, only commit while away, but not a problem for me.
  • I simply want to avoid a third git repository (the classic „server“).
  • both btw being “non-bare” repos (as in working repos), not running a git server/daemon on any port. Truly just a fileshare...

purpose:

I am not doing source code work in this scenario, only „document shuffling“, so now branching or major merge conflicts to be expected. But I do want to have some basic versioning and security against accidental deletion, and a bit of journaling info (who added when what), thus rsync, xcopy, will not suit my needs...


I have some past experience with a local bare repo I used to add as second remote (besides github), to push against when trying out things. No server/daemon/thread involved back then, too. The only thing I am essentially about to change is to use a „non-bare“ this time and also work in there,to)

Frank N
  • 9,625
  • 4
  • 80
  • 110
  • This can work; I've done it. But I use ssh URLs to connect one machine to another, not a SMB file system. SMB requires extensions to support POSIX semantics (see https://www.samba.org/samba/CIFS_POSIX_extensions.html) and Git somewhat depends on POSIX semantics. Fortunately it seems most systems have these extensions by default (see link). – torek Aug 14 '22 at 07:06

2 Answers2

1

AFAIK, you can't push to a checked-out branch of a non-bare repository. But if you use different branches on each side, and sync them when needed, it can be possible I think.

  • 3
    "*…you can't push to a checked-out branch of a non-bare repository…*" You can but you have to prepare the receiving repository: [`git config --local receive.denyCurrentBranch updateInstead`](https://stackoverflow.com/a/28383598/7976758) and [`push-to-checkout` hook](https://git-scm.com/docs/githooks#_push_to_checkout). – phd Aug 13 '22 at 12:55
1

I, well, looked rather thoroughly into the issue. @phd and @Özgür-murat-sağdıçoğlu gave great hints,.receive.denyCurrentBranch is the key!

A good recipe to build up a „tandem repo“ (just two wheels, both usable :) and sync the two without manual intervention.

(Note: None of this is suitable for any source code work, just for keeping a mostly growing tree of e.g. scanned documents in exactly two places...)

creation

A) "Local" on Laptop ( "server" this is pushed to, itself cannot push (no reamote))

mkcd /home/frank/docs
git init --initial-branch=master --shared=all

# the most important thing! ↓↓↓
git config --local receive.denyCurrentBranch updateInstead

# (you could also take just any other file)
code -r .gitignore
git add .gitignore
git commit -m 0


B) "Share"

cd /share/docs
git clone /home/frank/docs .
# ↑ doing it like this and in this order saves the add remote, etc connecting pains

syncing

cd $localDir
git add . && git commit -m ""
(no pushing possible here)

cd $shareDir
git add . && git commit -m ""
git pull --rebase -X theirs
git push

(yes, you could wash away changes on local by this, but they are preserved with the first commit hence restorable, if need be.)

The whole sync thing also goes nicely into a git alias like git sync.


Adding some sanity checks and smoke testing, this is my git alias for a fully unattend git sync:

# backup of final stable version, including prependd commit comment
# usage:
#       git sync
#       git sync Some stuff going on here
#       git sync 'This+that with special chars like plus'
#
# must be called from within repo, but doesn't matter which side
[alias]
  share = "!                                                                             \
    fail() {                                                                             \
      echo \"\\e[91;48;5;52mfail: $1 ✗✗✗\\e[0m – exiting.\"; exit;                       \
    };                                                                                   \
    addAndCommit() {                                                                     \
      git add .;                                                                         \
      git status;                                                                        \
      [ $? -eq 0 ] || fail 'git status not 0';                                           \
      message=\"$1: $(git status --porcelain | wc -l) Files: \";                         \
      message=\"$message$(git status --porcelain |  sed -re 's/^\\s?(M|A)\\s\\s?/\\1 /'  \
      | awk '{ printf(\"%s, \", $0) }'| cut -c -120)\";                                  \
      git commit -m \"$message\";                                                        \
    };                                                                                   \
                                                                                         \
                                                                                         \
    f() {                                                                                \
    _local='/home/frank/docs';                                                           \
    _share='/share/docs';                                                                \
    local message='';                                                                    \
                                                                                         \
    [ -z \"${1}\" ] || message=\"$1: \";                                                 \
    echo \"\ndir and sanity checks -----------------------------\";                      \
                                                                                         \
    [ -f $_share/.gitalias ] || fail 'smoke test failed: $_share/.gitalias';             \
    [ -f $_share/docsnotes.md ] || fail 'smoke test failed: $_share/docsnotes.md';       \
    [ -f $_local/.gitalias ] || fail 'smoke test failed: $_local/.gitalias';             \
    [ -f $_local/docsnotes.md ] || fail 'smoke test failed local : $_local/docsnotes.md';\
                                                                                         \
    echo \"\nLOCAL part $(pwd) ---------------------------------\";                      \
    echo \"${message}LOCAL changes\" ;                                                   \
    cd $_local;                                                                          \
    addAndCommit \"${message}LOCAL changes\";                                            \
                                                                                         \
    echo \"\nSHARE part $(pwd) ---------------------------------\";                      \
    cd $_share;                                                                          \
    addAndCommit \"${message}SHARE changes\";                                            \
    git pull --rebase -X theirs --autostash;                                             \
    git push;                                                                            \
    echo \"\n\\e[92mdone\\e[0m\";                                                        \
  }; f \"$*\""
Frank N
  • 9,625
  • 4
  • 80
  • 110