@@ -169,11 +169,16 @@ static void configure_git_filters (const char* key_name)
169
169
git_config (std::string (" filter.git-crypt-" ) + key_name + " .required" , " true" );
170
170
git_config (std::string (" diff.git-crypt-" ) + key_name + " .textconv" ,
171
171
escaped_git_crypt_path + " diff --key-name=" + key_name);
172
+ git_config (std::string (" merge.git-crypt-" ) + key_name + " .name" , " git-crypt merge driver" );
173
+ git_config (std::string (" merge.git-crypt-" ) + key_name + " .driver" ,
174
+ escaped_git_crypt_path + " merge --key-name=" + key_name + " %A %O %B %L" );
172
175
} else {
173
176
git_config (" filter.git-crypt.smudge" , escaped_git_crypt_path + " smudge" );
174
177
git_config (" filter.git-crypt.clean" , escaped_git_crypt_path + " clean" );
175
178
git_config (" filter.git-crypt.required" , " true" );
176
179
git_config (" diff.git-crypt.textconv" , escaped_git_crypt_path + " diff" );
180
+ git_config (" merge.git-crypt.name" , " git-crypt merge driver" );
181
+ git_config (" merge.git-crypt.driver" , escaped_git_crypt_path + " merge %A %O %B %L" );
177
182
}
178
183
}
179
184
@@ -190,6 +195,12 @@ static void deconfigure_git_filters (const char* key_name)
190
195
if (git_has_config (" diff." + attribute_name (key_name) + " .textconv" )) {
191
196
git_deconfig (" diff." + attribute_name (key_name));
192
197
}
198
+
199
+ if (git_has_config (" merge." + attribute_name (key_name) + " .name" ) ||
200
+ git_has_config (" merge." + attribute_name (key_name) + " .driver" )) {
201
+
202
+ git_deconfig (" merge." + attribute_name (key_name));
203
+ }
193
204
}
194
205
195
206
static bool git_checkout_batch (std::vector<std::string>::const_iterator paths_begin, std::vector<std::string>::const_iterator paths_end)
@@ -717,8 +728,8 @@ static int parse_plumbing_options (const char** key_name, const char** key_file,
717
728
return parse_options (options, argc, argv);
718
729
}
719
730
720
- // Encrypt contents of stdin and write to stdout
721
- int clean (int argc, const char ** argv)
731
+ // Encrypt contents of &in and write to &out
732
+ int clean (int argc, const char ** argv, std::istream& in, std::ostream& out )
722
733
{
723
734
const char * key_name = 0 ;
724
735
const char * key_path = 0 ;
@@ -751,10 +762,10 @@ int clean (int argc, const char** argv)
751
762
752
763
char buffer[1024 ];
753
764
754
- while (std::cin && file_size < Aes_ctr_encryptor::MAX_CRYPT_BYTES) {
755
- std::cin .read (buffer, sizeof (buffer));
765
+ while (in && file_size < Aes_ctr_encryptor::MAX_CRYPT_BYTES) {
766
+ in .read (buffer, sizeof (buffer));
756
767
757
- const size_t bytes_read = std::cin .gcount ();
768
+ const size_t bytes_read = in .gcount ();
758
769
759
770
hmac.add (reinterpret_cast <unsigned char *>(buffer), bytes_read);
760
771
file_size += bytes_read;
@@ -802,8 +813,8 @@ int clean (int argc, const char** argv)
802
813
hmac.get (digest);
803
814
804
815
// Write a header that...
805
- std::cout .write (" \0 GITCRYPT\0 " , 10 ); // ...identifies this as an encrypted file
806
- std::cout .write (reinterpret_cast <char *>(digest), Aes_ctr_encryptor::NONCE_LEN); // ...includes the nonce
816
+ out .write (" \0 GITCRYPT\0 " , 10 ); // ...identifies this as an encrypted file
817
+ out .write (reinterpret_cast <char *>(digest), Aes_ctr_encryptor::NONCE_LEN); // ...includes the nonce
807
818
808
819
// Now encrypt the file and write to stdout
809
820
Aes_ctr_encryptor aes (key->aes_key , digest);
@@ -814,7 +825,7 @@ int clean (int argc, const char** argv)
814
825
while (file_data_len > 0 ) {
815
826
const size_t buffer_len = std::min (sizeof (buffer), file_data_len);
816
827
aes.process (file_data, reinterpret_cast <unsigned char *>(buffer), buffer_len);
817
- std::cout .write (buffer, buffer_len);
828
+ out .write (buffer, buffer_len);
818
829
file_data += buffer_len;
819
830
file_data_len -= buffer_len;
820
831
}
@@ -830,14 +841,14 @@ int clean (int argc, const char** argv)
830
841
aes.process (reinterpret_cast <unsigned char *>(buffer),
831
842
reinterpret_cast <unsigned char *>(buffer),
832
843
buffer_len);
833
- std::cout .write (buffer, buffer_len);
844
+ out .write (buffer, buffer_len);
834
845
}
835
846
}
836
847
837
848
return 0 ;
838
849
}
839
850
840
- static int decrypt_file_to_stdout (const Key_file& key_file, const unsigned char * header, std::istream& in)
851
+ static int decrypt_file_to_stream (const Key_file& key_file, const unsigned char * header, std::istream& in, std::ostream& out = std::cout )
841
852
{
842
853
const unsigned char * nonce = header + 10 ;
843
854
uint32_t key_version = 0 ; // TODO: get the version from the file header
@@ -855,7 +866,7 @@ static int decrypt_file_to_stdout (const Key_file& key_file, const unsigned char
855
866
in.read (reinterpret_cast <char *>(buffer), sizeof (buffer));
856
867
aes.process (buffer, buffer, in.gcount ());
857
868
hmac.add (buffer, in.gcount ());
858
- std::cout .write (reinterpret_cast <char *>(buffer), in.gcount ());
869
+ out .write (reinterpret_cast <char *>(buffer), in.gcount ());
859
870
}
860
871
861
872
unsigned char digest[Hmac_sha1_state::LEN];
@@ -871,8 +882,8 @@ static int decrypt_file_to_stdout (const Key_file& key_file, const unsigned char
871
882
return 0 ;
872
883
}
873
884
874
- // Decrypt contents of stdin and write to stdout
875
- int smudge (int argc, const char ** argv)
885
+ // Decrypt contents of &in and write to &out
886
+ int smudge (int argc, const char ** argv, std::istream& in, std::ostream& out )
876
887
{
877
888
const char * key_name = 0 ;
878
889
const char * key_path = 0 ;
@@ -891,21 +902,21 @@ int smudge (int argc, const char** argv)
891
902
892
903
// Read the header to get the nonce and make sure it's actually encrypted
893
904
unsigned char header[10 + Aes_ctr_decryptor::NONCE_LEN];
894
- std::cin .read (reinterpret_cast <char *>(header), sizeof (header));
895
- if (std::cin .gcount () != sizeof (header) || std::memcmp (header, " \0 GITCRYPT\0 " , 10 ) != 0 ) {
905
+ in .read (reinterpret_cast <char *>(header), sizeof (header));
906
+ if (in .gcount () != sizeof (header) || std::memcmp (header, " \0 GITCRYPT\0 " , 10 ) != 0 ) {
896
907
// File not encrypted - just copy it out to stdout
897
908
std::clog << " git-crypt: Warning: file not encrypted" << std::endl;
898
909
std::clog << " git-crypt: Run 'git-crypt status' to make sure all files are properly encrypted." << std::endl;
899
910
std::clog << " git-crypt: If 'git-crypt status' reports no problems, then an older version of" << std::endl;
900
911
std::clog << " git-crypt: this file may be unencrypted in the repository's history. If this" << std::endl;
901
912
std::clog << " git-crypt: file contains sensitive information, you can use 'git filter-branch'" << std::endl;
902
913
std::clog << " git-crypt: to remove its old versions from the history." << std::endl;
903
- std::cout .write (reinterpret_cast <char *>(header), std::cin .gcount ()); // include the bytes which we already read
904
- std::cout << std::cin .rdbuf ();
914
+ out .write (reinterpret_cast <char *>(header), in .gcount ()); // include the bytes which we already read
915
+ out << in .rdbuf ();
905
916
return 0 ;
906
917
}
907
918
908
- return decrypt_file_to_stdout (key_file, header, std::cin );
919
+ return decrypt_file_to_stream (key_file, header, in, out );
909
920
}
910
921
911
922
int diff (int argc, const char ** argv)
@@ -947,7 +958,107 @@ int diff (int argc, const char** argv)
947
958
}
948
959
949
960
// Go ahead and decrypt it
950
- return decrypt_file_to_stdout (key_file, header, in);
961
+ return decrypt_file_to_stream (key_file, header, in);
962
+ }
963
+
964
+ int merge (int argc, const char ** argv)
965
+ {
966
+ const char * key_name = 0 ; // unused but needed
967
+ const char * key_path = 0 ; // unused but needed
968
+ const char * current_path = 0 ; // %A
969
+ const char * base_path = 0 ; // %O
970
+ const char * other_path = 0 ; // %B
971
+ const char * marker_size = 0 ; // %L
972
+
973
+ int argi = parse_plumbing_options (&key_name, &key_path, argc, argv);
974
+ if (argc - argi == 4 ) {
975
+ current_path = argv[argi];
976
+ base_path = argv[argi + 1 ];
977
+ other_path = argv[argi + 2 ];
978
+ marker_size = argv[argi + 3 ];
979
+ } else {
980
+ std::clog << " Usage: git-crypt merge [--key-name=NAME] [--key-file=PATH] CURRENT BASE OTHER MARKER_SIZE" << std::endl;
981
+ return 2 ;
982
+ }
983
+
984
+ // Run smudge on input files
985
+ std::vector<std::string> smudge_files;
986
+ smudge_files.push_back (current_path);
987
+ smudge_files.push_back (base_path);
988
+ smudge_files.push_back (other_path);
989
+
990
+ for (std::vector<std::string>::const_iterator file (smudge_files.begin ()); file != smudge_files.end (); ++file) {
991
+ std::ifstream in (*file, std::ifstream::binary);
992
+ if (!in) {
993
+ std::clog << " git-crypt: " << *file << " : unable to open for reading" << std::endl;
994
+ return 1 ;
995
+ }
996
+ in.exceptions (std::ifstream::badbit);
997
+
998
+ std::ofstream out (*file + " .tmp" , std::ofstream::binary | std::ofstream::trunc );
999
+ if (!out) {
1000
+ std::clog << " git-crypt: " << *file << " .tmp: unable to open for writing" << std::endl;
1001
+ return 1 ;
1002
+ }
1003
+ out.exceptions (std::ifstream::badbit);
1004
+
1005
+ if (smudge (argi, argv, in, out) != 0 ) {
1006
+ std::clog << " Error: failed to smudge " << *file << " : unable to merge file" << std::endl;
1007
+ return 1 ;
1008
+ }
1009
+ in.close ();
1010
+ out.close ();
1011
+ }
1012
+
1013
+ // git merge-file --marker-size <marker_size> <current_path> <base_path> <other_path>
1014
+ std::vector<std::string> command;
1015
+ command.push_back (" git" );
1016
+ command.push_back (" merge-file" );
1017
+ command.push_back (" -L" );
1018
+ command.push_back (" ours" );
1019
+ command.push_back (" -L" );
1020
+ command.push_back (" base" );
1021
+ command.push_back (" -L" );
1022
+ command.push_back (" theirs" );
1023
+ command.push_back (" --marker-size" );
1024
+ command.push_back (marker_size);
1025
+ command.push_back (std::string (current_path) + " .tmp" );
1026
+ command.push_back (std::string (base_path) + " .tmp" );
1027
+ command.push_back (std::string (other_path) + " .tmp" );
1028
+ int ret = exit_status (exec_command (command));
1029
+
1030
+ // Run clean on output file
1031
+ // We have to clean (encrypt) the output file because git runs smudge filter on it
1032
+ // afterwards which would complain about the file not being encrypted.
1033
+ {
1034
+ std::ifstream in (std::string (current_path) + " .tmp" , std::ifstream::binary);
1035
+ if (!in) {
1036
+ std::clog << " git-crypt: " << current_path << " .tmp: unable to open for reading" << std::endl;
1037
+ return 1 ;
1038
+ }
1039
+ in.exceptions (std::ifstream::badbit);
1040
+
1041
+ std::ofstream out (current_path, std::ofstream::binary | std::ofstream::trunc );
1042
+ if (!out) {
1043
+ std::clog << " git-crypt: " << current_path << " : unable to open for writing" << std::endl;
1044
+ return 1 ;
1045
+ }
1046
+ out.exceptions (std::ifstream::badbit);
1047
+
1048
+ if (clean (argi, argv, in, out) != 0 ) {
1049
+ std::clog << " Error: failed to clean " << current_path << " : unable to merge file" << std::endl;
1050
+ return 1 ;
1051
+ }
1052
+ in.close ();
1053
+ out.close ();
1054
+ }
1055
+
1056
+ // Clean-up temporary files
1057
+ for (std::vector<std::string>::const_iterator file (smudge_files.begin ()); file != smudge_files.end (); ++file) {
1058
+ remove_file (*file + " .tmp" );
1059
+ }
1060
+
1061
+ return ret;
951
1062
}
952
1063
953
1064
void help_init (std::ostream& out)
0 commit comments