@@ -169,11 +169,16 @@ static void configure_git_filters (const char* key_name)
169169 git_config (std::string (" filter.git-crypt-" ) + key_name + " .required" , " true" );
170170 git_config (std::string (" diff.git-crypt-" ) + key_name + " .textconv" ,
171171 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" );
172175 } else {
173176 git_config (" filter.git-crypt.smudge" , escaped_git_crypt_path + " smudge" );
174177 git_config (" filter.git-crypt.clean" , escaped_git_crypt_path + " clean" );
175178 git_config (" filter.git-crypt.required" , " true" );
176179 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" );
177182 }
178183}
179184
@@ -190,6 +195,12 @@ static void deconfigure_git_filters (const char* key_name)
190195 if (git_has_config (" diff." + attribute_name (key_name) + " .textconv" )) {
191196 git_deconfig (" diff." + attribute_name (key_name));
192197 }
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+ }
193204}
194205
195206static 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,
717728 return parse_options (options, argc, argv);
718729}
719730
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 )
722733{
723734 const char * key_name = 0 ;
724735 const char * key_path = 0 ;
@@ -751,10 +762,10 @@ int clean (int argc, const char** argv)
751762
752763 char buffer[1024 ];
753764
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));
756767
757- const size_t bytes_read = std::cin .gcount ();
768+ const size_t bytes_read = in .gcount ();
758769
759770 hmac.add (reinterpret_cast <unsigned char *>(buffer), bytes_read);
760771 file_size += bytes_read;
@@ -802,8 +813,8 @@ int clean (int argc, const char** argv)
802813 hmac.get (digest);
803814
804815 // 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
807818
808819 // Now encrypt the file and write to stdout
809820 Aes_ctr_encryptor aes (key->aes_key , digest);
@@ -814,7 +825,7 @@ int clean (int argc, const char** argv)
814825 while (file_data_len > 0 ) {
815826 const size_t buffer_len = std::min (sizeof (buffer), file_data_len);
816827 aes.process (file_data, reinterpret_cast <unsigned char *>(buffer), buffer_len);
817- std::cout .write (buffer, buffer_len);
828+ out .write (buffer, buffer_len);
818829 file_data += buffer_len;
819830 file_data_len -= buffer_len;
820831 }
@@ -830,14 +841,14 @@ int clean (int argc, const char** argv)
830841 aes.process (reinterpret_cast <unsigned char *>(buffer),
831842 reinterpret_cast <unsigned char *>(buffer),
832843 buffer_len);
833- std::cout .write (buffer, buffer_len);
844+ out .write (buffer, buffer_len);
834845 }
835846 }
836847
837848 return 0 ;
838849}
839850
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 )
841852{
842853 const unsigned char * nonce = header + 10 ;
843854 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
855866 in.read (reinterpret_cast <char *>(buffer), sizeof (buffer));
856867 aes.process (buffer, buffer, in.gcount ());
857868 hmac.add (buffer, in.gcount ());
858- std::cout .write (reinterpret_cast <char *>(buffer), in.gcount ());
869+ out .write (reinterpret_cast <char *>(buffer), in.gcount ());
859870 }
860871
861872 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
871882 return 0 ;
872883}
873884
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 )
876887{
877888 const char * key_name = 0 ;
878889 const char * key_path = 0 ;
@@ -891,21 +902,21 @@ int smudge (int argc, const char** argv)
891902
892903 // Read the header to get the nonce and make sure it's actually encrypted
893904 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 ) {
896907 // File not encrypted - just copy it out to stdout
897908 std::clog << " git-crypt: Warning: file not encrypted" << std::endl;
898909 std::clog << " git-crypt: Run 'git-crypt status' to make sure all files are properly encrypted." << std::endl;
899910 std::clog << " git-crypt: If 'git-crypt status' reports no problems, then an older version of" << std::endl;
900911 std::clog << " git-crypt: this file may be unencrypted in the repository's history. If this" << std::endl;
901912 std::clog << " git-crypt: file contains sensitive information, you can use 'git filter-branch'" << std::endl;
902913 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 ();
905916 return 0 ;
906917 }
907918
908- return decrypt_file_to_stdout (key_file, header, std::cin );
919+ return decrypt_file_to_stream (key_file, header, in, out );
909920}
910921
911922int diff (int argc, const char ** argv)
@@ -947,7 +958,107 @@ int diff (int argc, const char** argv)
947958 }
948959
949960 // 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;
9511062}
9521063
9531064void help_init (std::ostream& out)
0 commit comments