Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Let the user merge branches. #141

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,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.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -143,8 +143,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.
Expand Down
45 changes: 45 additions & 0 deletions commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,22 @@ 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",
"A merge driver used to merge git-crypted files");
git_config(std::string("merge.git-crypt-") + key_name + ".driver",
"bash ./.git-crypt/merge-tool.sh %O %A %B");
git_config(std::string("merge.git-crypt-") + key_name + ".recursive",
"binary");
} 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",
"A merge driver used to merge git-crypted files");
git_config("merge.git-crypt.driver",
"bash ./.git-crypt/merge-tool.sh %O %A %B");
git_config("merge.git-crypt.recursive", "binary");
}
}

Expand All @@ -181,6 +192,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_has_config("merge." + attribute_name(key_name) + ".recursive")) {
git_deconfig("merge." + attribute_name(key_name));
}
}

static bool git_checkout (const std::vector<std::string>& paths)
Expand Down Expand Up @@ -1256,6 +1273,34 @@ int add_gpg_user (int argc, const char** argv)
new_files.push_back(state_gitattributes_path);
}

// Add the merge-tool script to the repo state directory to provide "git merge" command
const std::string state_mergetool_path(state_path + "/merge-tool.sh");
if (access(state_mergetool_path.c_str(), F_OK) != 0) {
std::ofstream state_mergetool_file(state_mergetool_path.c_str());
// |--------------------------------------------------------------------------------| 80 chars
state_mergetool_file << "#!/usr/bin/env bash\n";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use /bin/sh instead of /usr/bin/env bash for better compatibility – not every system ships bash by default. /bin/sh exists on every *nix system, it’s defined by POSIX, and this script is compatible with POSIX shell.

state_mergetool_file << "ancestor_decrypted=$(mktemp)\n";
state_mergetool_file << "current_decrypted=$(mktemp)\n";
state_mergetool_file << "other_decrypted=$(mktemp)\n";
state_mergetool_file << "echo \"# Git crypt driver called #\"\n";
state_mergetool_file << "cat $1 | git-crypt smudge > \"${ancestor_decrypted}\"\n";
state_mergetool_file << "cat $2 | git-crypt smudge > \"${current_decrypted}\"\n";
state_mergetool_file << "cat $3 | git-crypt smudge > \"${other_decrypted}\"\n";
state_mergetool_file << "if [ \"$(git config --get merge.conflictstyle)\" == \"diff3\" ]; then diff=\"--diff3\"\n";
state_mergetool_file << "else diff=\"--no-diff3\"; fi\n";
state_mergetool_file << "git merge-file -L \"current branch\" -L \"ancestor branch\" -L \"other branch\" \"${current_decrypted}\" \"$diff\" \"${ancestor_decrypted}\" \"${other_decrypted}\"\n";
state_mergetool_file << "exit_code=$?\n";
state_mergetool_file << "cat \"${current_decrypted}\" | git-crypt clean > $2\n";
state_mergetool_file << "rm \"${other_decrypted}\" \"${ancestor_decrypted}\" \"${current_decrypted}\"\n";
state_mergetool_file << "exit $exit_code\n";
state_mergetool_file.close();
if (!state_mergetool_file) {
std::clog << "Error: unable to write " << state_mergetool_path << std::endl;
return 1;
}
new_files.push_back(state_mergetool_path);
}

// add/commit the new files
if (!new_files.empty()) {
// git add NEW_FILE ...
Expand Down