AGWA / git-crypt

Transparent file encryption in git
https://www.agwa.name/projects/git-crypt/
GNU General Public License v3.0
8.18k stars 475 forks source link

Doesn't work with sparse checkouts. #155

Open cunneen opened 6 years ago

cunneen commented 6 years ago

Background

I have a big monorepo with a lot of files, some of which are git-crypted. In a CI server I need to check out a submodule (using a git sparse checkout) and decrypt the included subset of the git-crypted files. Git-crypt won't allow this.

Reproduction

(using bash)

mkdir -p git-crypt-sparse-demo/repo1/foo \
    git-crypt-sparse-demo/repo1/bar \
    git-crypt-sparse-demo/repo2

# set up a test repository (repo1) with 2 encrypted files
cd git-crypt-sparse-demo/repo1
git init
git-crypt init
# NOTE: You'll need to replace me@example.com below with a real gpg user from your keychain
git-crypt add-gpg-user me@example.com
echo "foo" >> foo/foo.txt && echo "bar" >> bar/bar.txt
echo -e "/foo/foo.txt filter=git-crypt diff=git-crypt\n/bar/bar.txt filter=git-crypt diff=git-crypt" >> .gitattributes
git add .
git commit -m "foo and bar"
git-crypt lock

# set up a sparse checkout of repo1, in the repo2 folder, that omits everything under /bar
cd ../repo2
git init
git config core.sparseCheckout true
git remote add origin "file://`pwd`/../repo1"
echo "/foo/" >> .git/info/sparse-checkout
echo "/.gitattributes" >> .git/info/sparse-checkout
echo "/.git-crypt" >> .git/info/sparse-checkout
git fetch --depth 1 origin
git checkout master
# now we've got foo/ checked out, try to unlock foo/foo.txt
git-crypt unlock

Expected Behaviour

foo/foo.txt is decrypted

Actual Behaviour

foo/foo.txt is still encrypted, and the following error message is displayed:

error: pathspec 'bar/bar.txt' did not match any file(s) known to git.
Error: 'git checkout' failed
git-crypt has been set up but existing encrypted files have not been decrypted
cunneen commented 6 years ago

There's a workaround, but it's a bit inelegant. It involves adding all of the encrypted files to the sparse checkout, just so we can decrypt the ones we want.

mkdir -p git-crypt-sparse-demo/repo1/foo \
    git-crypt-sparse-demo/repo1/bar \
    git-crypt-sparse-demo/repo1/baz \
    git-crypt-sparse-demo/repo2 

# set up a test repository with 2 encrypted files ("foo" and "bar") and one unencrypted ("baz")
cd git-crypt-sparse-demo/repo1
git init
git-crypt init
# NOTE: You'll need to replace me@example.com below with a real gpg user from your keychain
git-crypt add-gpg-user me@example.com
echo "foo" >> foo/foo.txt && echo "bar" >> bar/bar.txt && echo "baz" >> baz/baz.txt
echo -e "/foo/foo.txt filter=git-crypt diff=git-crypt\n/bar/bar.txt filter=git-crypt diff=git-crypt" >> .gitattributes
git add .
git commit -m "foo, bar and baz"
git-crypt lock

# set up a sparse checkout of repo1, in the repo2 folder
cd ../repo2
git init
git config core.sparseCheckout true
git remote add origin "file://`pwd`/../repo1"
echo "/foo/" >> .git/info/sparse-checkout
echo "/.git-crypt" >> .git/info/sparse-checkout
git fetch --depth 1 origin
git checkout master

# Get a list of encrypted files for the whole repo, add them to the sparse checkout
git-crypt status -e | tr -s " " | cut -f3 -d" " >> .git/info/sparse-checkout

# Checkout again to get the other encrypted files
git checkout master

# now we can unlock
git-crypt unlock

Note that this will leave an unencrypted copy of bar/bar.txt in the repo, which is not exactly what I want.

cunneen commented 6 years ago

Another, more elegant (but still imperfect), workaround is to use multiple keys, with a different key being used to encrypt the files that will be in the sparse checkout.