Search code examples
githardlink

Does git use hardlinks for a remote on the same disk?


I sometimes work with two sibling directories both containing the same repository. Using

git remote add sibling ../the-other-directory

I take over changes simply via

git fetch sibling
git cherry-pick sibling/a-branch

This is pretty practical, however I have a concern regarding hardlinks:

If the repositories share files, then crashing one of them could crash the other as well. A broken repository is something I've already experienced and I'm sure that saving a few megabytes and a few milliseconds is just not worth it.

  • Does git use hardlinks when fetching from a repository on the same partition?
  • If so, is there a way of preventing this (like --no-hardlinks for git-clone)?

An additional advantage of not using hardlinks is having an additional backup which gets updated often without any effort.


Solution

  • Testing with the following script suggests that pseudo-cloning via git init + git remote add + git fetch doesn't create hardlinks to the source repository:

    hardlinktest:

    #!/usr/bin/env bash
    tmpdir="$(mktemp -d)"
    trap "rm -rf $tmpdir" EXIT
    cd "$tmpdir"
    set -x
    git clone https://github.com/dictcp/awesome-git testrepo
    git clone testrepo testrepo.localclone
    mkdir testrepo.pseudoclone
    cd testrepo.pseudoclone
    git init
    git remote add sibling ../testrepo
    git fetch sibling
    cd ..
    ls -1 -i testrepo*/.git/objects/a0
    

    Relevant part of the output:

    $ ls -1 -i testrepo*/.git/objects/a0
    testrepo/.git/objects/a0:
    417590 cdfa472f2bf8212a02a3edeb941868d651749d
    
    testrepo.localclone/.git/objects/a0:
    417590 cdfa472f2bf8212a02a3edeb941868d651749d
    
    testrepo.pseudoclone/.git/objects/a0:
    537341 cdfa472f2bf8212a02a3edeb941868d651749d
    

    This means that the file testrepo.localclone/.git/objects/a0/cdfa472f2bf8212a02a3edeb941868d651749d is a hardlink to testrepo/.git/objects/a0/cdfa472f2bf8212a02a3edeb941868d651749d - their inode values are the same (417590 for my test run, but your mileage will of course vary). The inode value (537341) of the corresponding file in the testrepo.pseudoclone repository tells us that it is an independent copy.

    Full output:

    $ ./hardlinktest 
    + git clone https://github.com/dictcp/awesome-git testrepo
    Cloning into 'testrepo'...
    remote: Counting objects: 58, done.
    remote: Total 58 (delta 0), reused 0 (delta 0), pack-reused 58
    Unpacking objects: 100% (58/58), done.
    Checking connectivity... done.
    + git clone testrepo testrepo.localclone
    Cloning into 'testrepo.localclone'...
    done.
    + mkdir testrepo.pseudoclone
    + cd testrepo.pseudoclone
    + git init
    Initialized empty Git repository in /tmp/tmp.ZWoH0OTA1P/testrepo.pseudoclone/.git/
    + git remote add sibling ../testrepo
    + git fetch sibling
    remote: Counting objects: 58, done.
    remote: Compressing objects: 100% (40/40), done.
    remote: Total 58 (delta 17), reused 0 (delta 0)
    Unpacking objects: 100% (58/58), done.
    From ../testrepo
     * [new branch]      master     -> sibling/master
    + cd ..
    + ls -1 -i testrepo/.git/objects/a0 testrepo.localclone/.git/objects/a0 testrepo.pseudoclone/.git/objects/a0
    testrepo/.git/objects/a0:
    417590 cdfa472f2bf8212a02a3edeb941868d651749d
    
    testrepo.localclone/.git/objects/a0:
    417590 cdfa472f2bf8212a02a3edeb941868d651749d
    
    testrepo.pseudoclone/.git/objects/a0:
    537341 cdfa472f2bf8212a02a3edeb941868d651749d
    + rm -rf /tmp/tmp.ZWoH0OTA1P