Skip to content
This repository was archived by the owner on May 7, 2024. It is now read-only.

Commit 2a50730

Browse files
committed
Fix merge of encrypted files with conflicts
Fix transcrypt's handling of merges where encrypted files have conflicting changes, a situation which would lead to Git producing "merged" files with conflict markers around partially- or fully- encrypted content that cannot be sensibly merged by a person. The root problem is that git does not run the smudge/textconv filter on all BASE, LOCAL, REMOTE conflicting version files before attempting a three-way merge. This change adds: - a merge driver script to pre-decrypt conflicting BASE, LOCAL, and REMOTE file versions then run git's internal `merge-file` command to merge the decrypted versions - git repo settings to configure the merge driver - recommendation to add the extra "merge=crypt" setting to .gitattribute definitions.
1 parent beabd6b commit 2a50730

File tree

1 file changed

+20
-6
lines changed

1 file changed

+20
-6
lines changed

transcrypt

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,18 @@ save_helper_scripts() {
316316
fi
317317
EOF
318318

319+
cat <<-'EOF' >"${GIT_DIR}/crypt/merge"
320+
#!/usr/bin/env bash
321+
# Decrypt BASE, LOCAL, and REMOTE versions of file being merged
322+
echo "$(./.git/crypt/textconv $1)" > $1
323+
echo "$(./.git/crypt/textconv $2)" > $2
324+
echo "$(./.git/crypt/textconv $3)" > $3
325+
# Use git's internal merge-file to merge the decrypted files
326+
git merge-file --marker-size=$4 -L local -L base -L remote $2 $1 $3
327+
EOF
328+
319329
# make scripts executable
320-
for script in {clean,smudge,textconv}; do
330+
for script in {clean,smudge,textconv,merge}; do
321331
chmod 0755 "${GIT_DIR}/crypt/${script}"
322332
done
323333
}
@@ -338,15 +348,18 @@ save_configuration() {
338348
git config filter.crypt.clean '"$(git rev-parse --git-common-dir)"/crypt/clean %f'
339349
git config filter.crypt.smudge '"$(git rev-parse --git-common-dir)"/crypt/smudge'
340350
git config diff.crypt.textconv '"$(git rev-parse --git-common-dir)"/crypt/textconv'
351+
git config merge.crypt.driver '"$(git rev-parse --git-common-dir)"/crypt/merge %O %A %B %L %P'
341352
else
342353
git config filter.crypt.clean '"$(git rev-parse --git-dir)"/crypt/clean %f'
343354
git config filter.crypt.smudge '"$(git rev-parse --git-dir)"/crypt/smudge'
344355
git config diff.crypt.textconv '"$(git rev-parse --git-dir)"/crypt/textconv'
356+
git config merge.crypt.driver '"$(git rev-parse --git-dir)"/crypt/merge %O %A %B %L %P'
345357
fi
346358
git config filter.crypt.required 'true'
347359
git config diff.crypt.cachetextconv 'true'
348360
git config diff.crypt.binary 'true'
349361
git config merge.renormalize 'true'
362+
git config merge.crypt.name 'Merge transcrypt secret files'
350363

351364
# add a git alias for listing encrypted files
352365
git config alias.ls-crypt "!git ls-files | git check-attr --stdin filter | awk 'BEGIN { FS = \":\" }; /crypt$/{ print \$1 }'"
@@ -374,6 +387,7 @@ clean_gitconfig() {
374387
git config --remove-section transcrypt 2>/dev/null || true
375388
git config --remove-section filter.crypt 2>/dev/null || true
376389
git config --remove-section diff.crypt 2>/dev/null || true
390+
git config --remove-section merge.crypt 2>/dev/null || true
377391
git config --unset merge.renormalize
378392

379393
# remove the merge section if it's now empty
@@ -451,7 +465,7 @@ uninstall_transcrypt() {
451465
clean_gitconfig
452466

453467
# remove helper scripts
454-
for script in {clean,smudge,textconv}; do
468+
for script in {clean,smudge,textconv,merge}; do
455469
[[ ! -f "${GIT_DIR}/crypt/${script}" ]] || rm "${GIT_DIR}/crypt/${script}"
456470
done
457471
[[ ! -d "${GIT_DIR}/crypt" ]] || rmdir "${GIT_DIR}/crypt"
@@ -475,10 +489,10 @@ uninstall_transcrypt() {
475489
# remove any defined crypt patterns in gitattributes
476490
case $OSTYPE in
477491
darwin*)
478-
/usr/bin/sed -i '' '/filter=crypt diff=crypt[ \t]*$/d' "$GIT_ATTRIBUTES"
492+
/usr/bin/sed -i '' '/filter=crypt diff=crypt merge=crypt[ \t]*$/d' "$GIT_ATTRIBUTES"
479493
;;
480494
linux*)
481-
sed -i '/filter=crypt diff=crypt[ \t]*$/d' "$GIT_ATTRIBUTES"
495+
sed -i '/filter=crypt diff=crypt merge=crypt[ \t]*$/d' "$GIT_ATTRIBUTES"
482496
;;
483497
esac
484498

@@ -665,7 +679,7 @@ help() {
665679
a file in your repository, the file will be transparently encrypted
666680
once you stage and commit it:
667681
668-
$ echo 'sensitive_file filter=crypt diff=crypt' >> .gitattributes
682+
$ echo 'sensitive_file filter=crypt diff=crypt merge=crypt' >> .gitattributes
669683
$ git add .gitattributes sensitive_file
670684
$ git commit -m 'Add encrypted version of a sensitive file'
671685
@@ -851,7 +865,7 @@ fi
851865
# ensure the git attributes file exists
852866
if [[ ! -f $GIT_ATTRIBUTES ]]; then
853867
mkdir -p "${GIT_ATTRIBUTES%/*}"
854-
printf '#pattern filter=crypt diff=crypt\n' >"$GIT_ATTRIBUTES"
868+
printf '#pattern filter=crypt diff=crypt merge=crypt\n' >"$GIT_ATTRIBUTES"
855869
fi
856870

857871
printf 'The repository has been successfully configured by transcrypt.\n'

0 commit comments

Comments
 (0)