aboutsummaryrefslogtreecommitdiffstats
path: root/community/git-crypt
diff options
context:
space:
mode:
authorJakub Jirutka <jakub@jirutka.cz>2019-07-28 21:39:47 +0200
committerJakub Jirutka <jakub@jirutka.cz>2019-07-28 21:39:54 +0200
commitf2518caccfe3208aa2b5212ea338ee43f044bdf3 (patch)
tree9335862fa0a93a815fa3b1dcc391e059da21e77b /community/git-crypt
parent73f168230426b9c6466d5aef3ed4bb50c5b3d043 (diff)
downloadaports-f2518caccfe3208aa2b5212ea338ee43f044bdf3.tar.bz2
aports-f2518caccfe3208aa2b5212ea338ee43f044bdf3.tar.xz
community/git-crypt: move from testing
Diffstat (limited to 'community/git-crypt')
-rw-r--r--community/git-crypt/0001-add-merge-driver.patch459
-rw-r--r--community/git-crypt/0002-keep-empty-files-unencrypted.patch44
-rw-r--r--community/git-crypt/APKBUILD34
3 files changed, 537 insertions, 0 deletions
diff --git a/community/git-crypt/0001-add-merge-driver.patch b/community/git-crypt/0001-add-merge-driver.patch
new file mode 100644
index 0000000000..771cc56e33
--- /dev/null
+++ b/community/git-crypt/0001-add-merge-driver.patch
@@ -0,0 +1,459 @@
+From b6f4f33ba969cfd44cdd9a5df2048ad5b6330fb0 Mon Sep 17 00:00:00 2001
+From: Jakub Jirutka <jakub@jirutka.cz>
+Date: Sun, 28 Jul 2019 17:14:09 +0200
+Subject: [PATCH 1/2] Change clean() and smudge() functions to accept in/out
+ stream
+
+This is a preparation for the merge command.
+
+I've extracted this change from #107.
+
+Co-Authored-By: Shlomo Shachar <shlomo.shachar@binatix.com>
+Patch-Source: https://github.com/AGWA/git-crypt/pull/180
+---
+ commands.cpp | 38 +++++++++++++++++++-------------------
+ commands.hpp | 5 +++--
+ 2 files changed, 22 insertions(+), 21 deletions(-)
+
+diff --git a/commands.cpp b/commands.cpp
+index d25c4cc..93a840e 100644
+--- a/commands.cpp
++++ b/commands.cpp
+@@ -690,8 +690,8 @@ static int parse_plumbing_options (const char** key_name, const char** key_file,
+ return parse_options(options, argc, argv);
+ }
+
+-// Encrypt contents of stdin and write to stdout
+-int clean (int argc, const char** argv)
++// Encrypt contents of &in and write to &out
++int clean (int argc, const char** argv, std::istream& in, std::ostream& out)
+ {
+ const char* key_name = 0;
+ const char* key_path = 0;
+@@ -724,10 +724,10 @@ int clean (int argc, const char** argv)
+
+ char buffer[1024];
+
+- while (std::cin && file_size < Aes_ctr_encryptor::MAX_CRYPT_BYTES) {
+- std::cin.read(buffer, sizeof(buffer));
++ while (in && file_size < Aes_ctr_encryptor::MAX_CRYPT_BYTES) {
++ in.read(buffer, sizeof(buffer));
+
+- const size_t bytes_read = std::cin.gcount();
++ const size_t bytes_read = in.gcount();
+
+ hmac.add(reinterpret_cast<unsigned char*>(buffer), bytes_read);
+ file_size += bytes_read;
+@@ -775,8 +775,8 @@ int clean (int argc, const char** argv)
+ hmac.get(digest);
+
+ // Write a header that...
+- std::cout.write("\0GITCRYPT\0", 10); // ...identifies this as an encrypted file
+- std::cout.write(reinterpret_cast<char*>(digest), Aes_ctr_encryptor::NONCE_LEN); // ...includes the nonce
++ out.write("\0GITCRYPT\0", 10); // ...identifies this as an encrypted file
++ out.write(reinterpret_cast<char*>(digest), Aes_ctr_encryptor::NONCE_LEN); // ...includes the nonce
+
+ // Now encrypt the file and write to stdout
+ Aes_ctr_encryptor aes(key->aes_key, digest);
+@@ -787,7 +787,7 @@ int clean (int argc, const char** argv)
+ while (file_data_len > 0) {
+ const size_t buffer_len = std::min(sizeof(buffer), file_data_len);
+ aes.process(file_data, reinterpret_cast<unsigned char*>(buffer), buffer_len);
+- std::cout.write(buffer, buffer_len);
++ out.write(buffer, buffer_len);
+ file_data += buffer_len;
+ file_data_len -= buffer_len;
+ }
+@@ -803,14 +803,14 @@ int clean (int argc, const char** argv)
+ aes.process(reinterpret_cast<unsigned char*>(buffer),
+ reinterpret_cast<unsigned char*>(buffer),
+ buffer_len);
+- std::cout.write(buffer, buffer_len);
++ out.write(buffer, buffer_len);
+ }
+ }
+
+ return 0;
+ }
+
+-static int decrypt_file_to_stdout (const Key_file& key_file, const unsigned char* header, std::istream& in)
++static int decrypt_file_to_stream (const Key_file& key_file, const unsigned char* header, std::istream& in, std::ostream& out = std::cout)
+ {
+ const unsigned char* nonce = header + 10;
+ uint32_t key_version = 0; // TODO: get the version from the file header
+@@ -828,7 +828,7 @@ static int decrypt_file_to_stdout (const Key_file& key_file, const unsigned char
+ in.read(reinterpret_cast<char*>(buffer), sizeof(buffer));
+ aes.process(buffer, buffer, in.gcount());
+ hmac.add(buffer, in.gcount());
+- std::cout.write(reinterpret_cast<char*>(buffer), in.gcount());
++ out.write(reinterpret_cast<char*>(buffer), in.gcount());
+ }
+
+ unsigned char digest[Hmac_sha1_state::LEN];
+@@ -844,8 +844,8 @@ static int decrypt_file_to_stdout (const Key_file& key_file, const unsigned char
+ return 0;
+ }
+
+-// Decrypt contents of stdin and write to stdout
+-int smudge (int argc, const char** argv)
++// Decrypt contents of &in and write to &out
++int smudge (int argc, const char** argv, std::istream& in, std::ostream& out)
+ {
+ const char* key_name = 0;
+ const char* key_path = 0;
+@@ -864,8 +864,8 @@ int smudge (int argc, const char** argv)
+
+ // Read the header to get the nonce and make sure it's actually encrypted
+ unsigned char header[10 + Aes_ctr_decryptor::NONCE_LEN];
+- std::cin.read(reinterpret_cast<char*>(header), sizeof(header));
+- if (std::cin.gcount() != sizeof(header) || std::memcmp(header, "\0GITCRYPT\0", 10) != 0) {
++ in.read(reinterpret_cast<char*>(header), sizeof(header));
++ if (in.gcount() != sizeof(header) || std::memcmp(header, "\0GITCRYPT\0", 10) != 0) {
+ // File not encrypted - just copy it out to stdout
+ std::clog << "git-crypt: Warning: file not encrypted" << std::endl;
+ std::clog << "git-crypt: Run 'git-crypt status' to make sure all files are properly encrypted." << std::endl;
+@@ -873,12 +873,12 @@ int smudge (int argc, const char** argv)
+ std::clog << "git-crypt: this file may be unencrypted in the repository's history. If this" << std::endl;
+ std::clog << "git-crypt: file contains sensitive information, you can use 'git filter-branch'" << std::endl;
+ std::clog << "git-crypt: to remove its old versions from the history." << std::endl;
+- std::cout.write(reinterpret_cast<char*>(header), std::cin.gcount()); // include the bytes which we already read
+- std::cout << std::cin.rdbuf();
++ out.write(reinterpret_cast<char*>(header), in.gcount()); // include the bytes which we already read
++ out << in.rdbuf();
+ return 0;
+ }
+
+- return decrypt_file_to_stdout(key_file, header, std::cin);
++ return decrypt_file_to_stream(key_file, header, in, out);
+ }
+
+ int diff (int argc, const char** argv)
+@@ -920,7 +920,7 @@ int diff (int argc, const char** argv)
+ }
+
+ // Go ahead and decrypt it
+- return decrypt_file_to_stdout(key_file, header, in);
++ return decrypt_file_to_stream(key_file, header, in);
+ }
+
+ void help_init (std::ostream& out)
+diff --git a/commands.hpp b/commands.hpp
+index f441e93..bf4632c 100644
+--- a/commands.hpp
++++ b/commands.hpp
+@@ -33,6 +33,7 @@
+
+ #include <string>
+ #include <iosfwd>
++#include <iostream>
+
+ struct Error {
+ std::string message;
+@@ -41,8 +42,8 @@ struct Error {
+ };
+
+ // Plumbing commands:
+-int clean (int argc, const char** argv);
+-int smudge (int argc, const char** argv);
++int clean (int argc, const char** argv, std::istream& in = std::cin, std::ostream& out = std::cout);
++int smudge (int argc, const char** argv, std::istream& in = std::cin, std::ostream& out = std::cout);
+ int diff (int argc, const char** argv);
+ // Public commands:
+ int init (int argc, const char** argv);
+
+From b2efa705c4bd8c5d5590816226aace123bf19265 Mon Sep 17 00:00:00 2001
+From: Jakub Jirutka <jakub@jirutka.cz>
+Date: Sun, 28 Jul 2019 19:18:39 +0200
+Subject: [PATCH 2/2] Add git-crypt merge driver to support secret files
+ merging
+
+This commit is based on #107.
+
+Co-Authored-By: Shlomo Shachar <shlomo.shachar@binatix.com>
+Patch-Source: https://github.com/AGWA/git-crypt/pull/180
+---
+ README | 8 ++--
+ README.md | 8 ++--
+ commands.cpp | 111 +++++++++++++++++++++++++++++++++++++++++++
+ commands.hpp | 1 +
+ doc/multiple_keys.md | 2 +-
+ git-crypt.cpp | 4 ++
+ man/git-crypt.xml | 8 ++--
+ 7 files changed, 129 insertions(+), 13 deletions(-)
+
+diff --git a/README b/README
+index 232947f..2810a26 100644
+--- a/README
++++ b/README
+@@ -28,8 +28,8 @@ Configure a repository to use git-crypt:
+
+ Specify files to encrypt by creating a .gitattributes file:
+
+- secretfile filter=git-crypt diff=git-crypt
+- *.key filter=git-crypt diff=git-crypt
++ secretfile filter=git-crypt diff=git-crypt merge=git-crypt
++ *.key filter=git-crypt diff=git-crypt merge=git-crypt
+
+ Like a .gitignore file, it can match wildcards and should be checked into
+ the repository. See below for more information about .gitattributes.
+@@ -151,8 +151,8 @@ Also note that the pattern `dir/*` does not match files under
+ sub-directories of dir/. To encrypt an entire sub-tree dir/, place the
+ following in dir/.gitattributes:
+
+- * filter=git-crypt diff=git-crypt
+- .gitattributes !filter !diff
++ * filter=git-crypt diff=git-crypt merge=git-crypt
++ .gitattributes !filter !diff !merge
+
+ The second pattern is essential for ensuring that .gitattributes itself
+ is not encrypted.
+diff --git a/README.md b/README.md
+index d24517a..d008aab 100644
+--- a/README.md
++++ b/README.md
+@@ -29,8 +29,8 @@ Configure a repository to use git-crypt:
+
+ Specify files to encrypt by creating a .gitattributes file:
+
+- secretfile filter=git-crypt diff=git-crypt
+- *.key filter=git-crypt diff=git-crypt
++ secretfile filter=git-crypt diff=git-crypt merge=git-crypt
++ *.key filter=git-crypt diff=git-crypt merge=git-crypt
+
+ Like a .gitignore file, it can match wildcards and should be checked into
+ the repository. See below for more information about .gitattributes.
+@@ -153,8 +153,8 @@ Also note that the pattern `dir/*` does not match files under
+ sub-directories of dir/. To encrypt an entire sub-tree dir/, place the
+ following in dir/.gitattributes:
+
+- * filter=git-crypt diff=git-crypt
+- .gitattributes !filter !diff
++ * filter=git-crypt diff=git-crypt merge=git-crypt
++ .gitattributes !filter !diff !merge
+
+ The second pattern is essential for ensuring that .gitattributes itself
+ is not encrypted.
+diff --git a/commands.cpp b/commands.cpp
+index 93a840e..415b36a 100644
+--- a/commands.cpp
++++ b/commands.cpp
+@@ -160,11 +160,16 @@ static void configure_git_filters (const char* key_name)
+ git_config(std::string("filter.git-crypt-") + key_name + ".required", "true");
+ git_config(std::string("diff.git-crypt-") + key_name + ".textconv",
+ escaped_git_crypt_path + " diff --key-name=" + key_name);
++ git_config(std::string("merge.git-crypt-") + key_name + ".name", "git-crypt merge driver");
++ git_config(std::string("merge.git-crypt-") + key_name + ".driver",
++ escaped_git_crypt_path + " merge --key-name=" + key_name + " %A %O %B %L");
+ } else {
+ git_config("filter.git-crypt.smudge", escaped_git_crypt_path + " smudge");
+ git_config("filter.git-crypt.clean", escaped_git_crypt_path + " clean");
+ git_config("filter.git-crypt.required", "true");
+ git_config("diff.git-crypt.textconv", escaped_git_crypt_path + " diff");
++ git_config("merge.git-crypt.name", "git-crypt merge driver");
++ git_config("merge.git-crypt.driver", escaped_git_crypt_path + " merge %A %O %B %L");
+ }
+ }
+
+@@ -181,6 +186,12 @@ static void deconfigure_git_filters (const char* key_name)
+ if (git_has_config("diff." + attribute_name(key_name) + ".textconv")) {
+ git_deconfig("diff." + attribute_name(key_name));
+ }
++
++ if (git_has_config("merge." + attribute_name(key_name) + ".name") ||
++ git_has_config("merge." + attribute_name(key_name) + ".driver")) {
++
++ git_deconfig("merge." + attribute_name(key_name));
++ }
+ }
+
+ static bool git_checkout (const std::vector<std::string>& paths)
+@@ -923,6 +934,106 @@ int diff (int argc, const char** argv)
+ return decrypt_file_to_stream(key_file, header, in);
+ }
+
++int merge (int argc, const char** argv)
++{
++ const char* key_name = 0; // unused but needed
++ const char* key_path = 0; // unused but needed
++ const char* current_path = 0; // %A
++ const char* base_path = 0; // %O
++ const char* other_path = 0; // %B
++ const char* marker_size = 0; // %L
++
++ int argi = parse_plumbing_options(&key_name, &key_path, argc, argv);
++ if (argc - argi == 4) {
++ current_path = argv[argi];
++ base_path = argv[argi + 1];
++ other_path = argv[argi + 2];
++ marker_size = argv[argi + 3];
++ } else {
++ std::clog << "Usage: git-crypt merge [--key-name=NAME] [--key-file=PATH] CURRENT BASE OTHER MARKER_SIZE" << std::endl;
++ return 2;
++ }
++
++ // Run smudge on input files
++ std::vector<std::string> smudge_files;
++ smudge_files.push_back(current_path);
++ smudge_files.push_back(base_path);
++ smudge_files.push_back(other_path);
++
++ for (std::vector<std::string>::const_iterator file(smudge_files.begin()); file != smudge_files.end(); ++file) {
++ std::ifstream in(*file, std::ifstream::binary);
++ if (!in) {
++ std::clog << "git-crypt: " << *file << ": unable to open for reading" << std::endl;
++ return 1;
++ }
++ in.exceptions(std::ifstream::badbit);
++
++ std::ofstream out(*file + ".tmp", std::ofstream::binary | std::ofstream::trunc);
++ if (!out) {
++ std::clog << "git-crypt: " << *file << ".tmp: unable to open for writing" << std::endl;
++ return 1;
++ }
++ out.exceptions(std::ifstream::badbit);
++
++ if (smudge(argi, argv, in, out) != 0) {
++ std::clog << "Error: failed to smudge " << *file << ": unable to merge file" << std::endl;
++ return 1;
++ }
++ in.close();
++ out.close();
++ }
++
++ // git merge-file --marker-size <marker_size> <current_path> <base_path> <other_path>
++ std::vector<std::string> command;
++ command.push_back("git");
++ command.push_back("merge-file");
++ command.push_back("-L");
++ command.push_back("ours");
++ command.push_back("-L");
++ command.push_back("base");
++ command.push_back("-L");
++ command.push_back("theirs");
++ command.push_back("--marker-size");
++ command.push_back(marker_size);
++ command.push_back(std::string(current_path) + ".tmp");
++ command.push_back(std::string(base_path) + ".tmp");
++ command.push_back(std::string(other_path) + ".tmp");
++ int ret = exit_status(exec_command(command));
++
++ // Run clean on output file
++ // We have to clean (encrypt) the output file because git runs smudge filter on it
++ // afterwards which would complain about the file not being encrypted.
++ {
++ std::ifstream in(std::string(current_path) + ".tmp", std::ifstream::binary);
++ if (!in) {
++ std::clog << "git-crypt: " << current_path << ".tmp: unable to open for reading" << std::endl;
++ return 1;
++ }
++ in.exceptions(std::ifstream::badbit);
++
++ std::ofstream out(current_path, std::ofstream::binary | std::ofstream::trunc);
++ if (!out) {
++ std::clog << "git-crypt: " << current_path << ": unable to open for writing" << std::endl;
++ return 1;
++ }
++ out.exceptions(std::ifstream::badbit);
++
++ if (clean(argi, argv, in, out) != 0) {
++ std::clog << "Error: failed to clean " << current_path << ": unable to merge file" << std::endl;
++ return 1;
++ }
++ in.close();
++ out.close();
++ }
++
++ // Clean-up temporary files
++ for (std::vector<std::string>::const_iterator file(smudge_files.begin()); file != smudge_files.end(); ++file) {
++ remove_file(*file + ".tmp");
++ }
++
++ return ret;
++}
++
+ void help_init (std::ostream& out)
+ {
+ // |--------------------------------------------------------------------------------| 80 chars
+diff --git a/commands.hpp b/commands.hpp
+index bf4632c..51f4aea 100644
+--- a/commands.hpp
++++ b/commands.hpp
+@@ -45,6 +45,7 @@ struct Error {
+ int clean (int argc, const char** argv, std::istream& in = std::cin, std::ostream& out = std::cout);
+ int smudge (int argc, const char** argv, std::istream& in = std::cin, std::ostream& out = std::cout);
+ int diff (int argc, const char** argv);
++int merge (int argc, const char** argv);
+ // Public commands:
+ int init (int argc, const char** argv);
+ int unlock (int argc, const char** argv);
+diff --git a/doc/multiple_keys.md b/doc/multiple_keys.md
+index 6d7fc69..66b462a 100644
+--- a/doc/multiple_keys.md
++++ b/doc/multiple_keys.md
+@@ -11,7 +11,7 @@ option to `git-crypt init` as follows:
+ To encrypt a file with an alternative key, use the `git-crypt-KEYNAME`
+ filter in `.gitattributes` as follows:
+
+- secretfile filter=git-crypt-KEYNAME diff=git-crypt-KEYNAME
++ secretfile filter=git-crypt-KEYNAME diff=git-crypt-KEYNAME merge=git-crypt-KEYNAME
+
+ To export an alternative key or share it with a GPG user, pass the `-k
+ KEYNAME` option to `git-crypt export-key` or `git-crypt add-gpg-user`
+diff --git a/git-crypt.cpp b/git-crypt.cpp
+index 9505834..d8c2072 100644
+--- a/git-crypt.cpp
++++ b/git-crypt.cpp
+@@ -73,6 +73,7 @@ static void print_usage (std::ostream& out)
+ out << " clean [LEGACY-KEYFILE]" << std::endl;
+ out << " smudge [LEGACY-KEYFILE]" << std::endl;
+ out << " diff [LEGACY-KEYFILE] FILE" << std::endl;
++ out << " merge" << std::endl;
+ */
+ out << std::endl;
+ out << "See 'git-crypt help COMMAND' for more information on a specific command." << std::endl;
+@@ -231,6 +232,9 @@ try {
+ if (std::strcmp(command, "diff") == 0) {
+ return diff(argc, argv);
+ }
++ if (std::strcmp(command, "merge") == 0) {
++ return merge(argc, argv);
++ }
+ } catch (const Option_error& e) {
+ std::clog << "git-crypt: Error: " << e.option_name << ": " << e.message << std::endl;
+ help_for_command(command, std::clog);
+diff --git a/man/git-crypt.xml b/man/git-crypt.xml
+index 96f53d7..21e1359 100644
+--- a/man/git-crypt.xml
++++ b/man/git-crypt.xml
+@@ -310,11 +310,11 @@
+ <para>
+ Then, you specify the files to encrypt by creating a
+ <citerefentry><refentrytitle>gitattributes</refentrytitle><manvolnum>5</manvolnum></citerefentry> file.
+- Each file which you want to encrypt should be assigned the "<literal>filter=git-crypt diff=git-crypt</literal>"
++ Each file which you want to encrypt should be assigned the "<literal>filter=git-crypt diff=git-crypt merge=git-crypt</literal>"
+ attributes. For example:
+ </para>
+
+- <screen>secretfile filter=git-crypt diff=git-crypt&#10;*.key filter=git-crypt diff=git-crypt</screen>
++ <screen>secretfile filter=git-crypt diff=git-crypt merge=git-crypt&#10;*.key filter=git-crypt diff=git-crypt merge=git-crypt</screen>
+
+ <para>
+ Like a <filename>.gitignore</filename> file, <filename>.gitattributes</filename> files can match wildcards and
+@@ -383,7 +383,7 @@
+ following in <filename>dir/.gitattributes</filename>:
+ </para>
+
+- <screen>* filter=git-crypt diff=git-crypt&#10;.gitattributes !filter !diff</screen>
++ <screen>* filter=git-crypt diff=git-crypt merge=git-crypt&#10;.gitattributes !filter !diff !merge</screen>
+
+ <para>
+ The second pattern is essential for ensuring that <filename>.gitattributes</filename> itself
+@@ -414,7 +414,7 @@
+ filter in <filename>.gitattributes</filename> as follows:
+ </para>
+
+- <screen><replaceable>secretfile</replaceable> filter=git-crypt-<replaceable>KEYNAME</replaceable> diff=git-crypt-<replaceable>KEYNAME</replaceable></screen>
++ <screen><replaceable>secretfile</replaceable> filter=git-crypt-<replaceable>KEYNAME</replaceable> diff=git-crypt-<replaceable>KEYNAME</replaceable> merge=git-crypt-<replaceable>KEYNAME</replaceable></screen>
+
+ <para>
+ To export an alternative key or share it with a GPG user, pass the
diff --git a/community/git-crypt/0002-keep-empty-files-unencrypted.patch b/community/git-crypt/0002-keep-empty-files-unencrypted.patch
new file mode 100644
index 0000000000..d0e490bfd8
--- /dev/null
+++ b/community/git-crypt/0002-keep-empty-files-unencrypted.patch
@@ -0,0 +1,44 @@
+From 62c372581b3342d6540e5c11aaea3247ee9f852c Mon Sep 17 00:00:00 2001
+From: Hugo Peixoto <hugo.peixoto@gmail.com>
+Date: Mon, 29 Oct 2018 19:40:18 +0000
+Subject: [PATCH] Keep empty files unencrypted
+
+To work around the issue that git considers the working directory
+dirty when empty files are encrypted, these are kept untouched when
+cleaning/smudging.
+
+Security wise, this is not an issue, as you can check if an encrypted
+file is empty due to the deterministic encryption properties.
+
+Patch-Source: https://github.com/AGWA/git-crypt/issues/53
+---
+ commands.cpp | 9 +++++++++
+ 1 file changed, 9 insertions(+)
+
+diff --git a/commands.cpp b/commands.cpp
+index 5ac0b47..a0a8d6e 100644
+--- a/commands.cpp
++++ b/commands.cpp
+@@ -748,6 +748,10 @@ int clean (int argc, const char** argv)
+ return 1;
+ }
+
++ if (file_size == 0) {
++ return 0;
++ }
++
+ // We use an HMAC of the file as the encryption nonce (IV) for CTR mode.
+ // By using a hash of the file we ensure that the encryption is
+ // deterministic so git doesn't think the file has changed when it really
+@@ -865,6 +869,11 @@ int smudge (int argc, const char** argv)
+ // Read the header to get the nonce and make sure it's actually encrypted
+ unsigned char header[10 + Aes_ctr_decryptor::NONCE_LEN];
+ in.read(reinterpret_cast<char*>(header), sizeof(header));
++
++ if (in.gcount() == 0) {
++ return 0;
++ }
++
+ if (in.gcount() != sizeof(header) || std::memcmp(header, "\0GITCRYPT\0", 10) != 0) {
+ // File not encrypted - just copy it out to stdout
+ std::clog << "git-crypt: Warning: file not encrypted" << std::endl;
diff --git a/community/git-crypt/APKBUILD b/community/git-crypt/APKBUILD
new file mode 100644
index 0000000000..037fdc3799
--- /dev/null
+++ b/community/git-crypt/APKBUILD
@@ -0,0 +1,34 @@
+# Contributor: Laurent Arnoud <laurent@spkdev.net>
+# Contributor: Jakub Jirutka <jakub@jirutka.cz>
+# Maintainer: Laurent Arnoud <laurent@spkdev.net>
+pkgname=git-crypt
+pkgver=0.6.0
+pkgrel=1
+pkgdesc="Transparent file encryption in git"
+url="https://www.agwa.name/projects/git-crypt"
+arch="all"
+license="GPL-3.0-or-later"
+depends="git"
+options="!check" # No test suite
+makedepends="docbook-xml docbook-xsl openssl-dev"
+subpackages="$pkgname-doc"
+source="$pkgname-$pkgver.tar.gz::https://github.com/AGWA/$pkgname/archive/$pkgver.tar.gz
+ 0001-add-merge-driver.patch
+ 0002-keep-empty-files-unencrypted.patch
+ "
+builddir="$srcdir/$pkgname-$pkgver"
+
+build() {
+ cd "$builddir"
+ make
+}
+
+package() {
+ cd "$builddir"
+ make PREFIX="/usr" DESTDIR="$pkgdir" ENABLE_MAN=yes install
+ install -Dm644 COPYING "$pkgdir"/usr/share/licenses/$pkgname/COPYING
+}
+
+sha512sums="06fd9f6dbdc5c9fa9196f3e81a87cd3688089623b2658daf9c98809d5ca14df0b7ca69fdfe8279abf575957c366f2f93bd5a6885092eb533bd0d1ed9fe9dfac5 git-crypt-0.6.0.tar.gz
+90da8f4c6f084ec35a753969908eb1b1c4e2ff3bc6b0eb22aa3d4b88c712c2f9db09d7ad404ec2fb0e6addeb6f4c724b7008ed2687ad3892ea4bf16f4685c975 0001-add-merge-driver.patch
+136b5ad3a197f462878210462ce4d4bac6978d9ef67f5a4f49acedd58c48491c245f8649885bcc2bed43d642f7e5652d32acf12861b28582b6658482168f7088 0002-keep-empty-files-unencrypted.patch"