4747#include < errno.h>
4848#include < vector>
4949
50- static void configure_git_filters ( )
50+ static void git_config ( const std::string& name, const std::string& value )
5151{
52- std::string git_crypt_path (our_exe_path ());
53-
54- // git config filter.git-crypt.smudge "/path/to/git-crypt smudge"
55- std::string command (" git config filter.git-crypt.smudge " );
56- command += escape_shell_arg (escape_shell_arg (git_crypt_path) + " smudge" );
57-
58- if (!successful_exit (system (command.c_str ()))) {
59- throw Error (" 'git config' failed" );
60- }
52+ std::vector<std::string> command;
53+ command.push_back (" git" );
54+ command.push_back (" config" );
55+ command.push_back (name);
56+ command.push_back (value);
6157
62- // git config filter.git-crypt.clean "/path/to/git-crypt clean"
63- command = " git config filter.git-crypt.clean " ;
64- command += escape_shell_arg (escape_shell_arg (git_crypt_path) + " clean" );
65-
66- if (!successful_exit (system (command.c_str ()))) {
58+ if (!successful_exit (exec_command (command))) {
6759 throw Error (" 'git config' failed" );
6860 }
61+ }
6962
70- // git config diff.git-crypt.textconv "/path/to/git-crypt diff"
71- command = " git config diff.git-crypt.textconv " ;
72- command += escape_shell_arg (escape_shell_arg (git_crypt_path) + " diff " );
63+ static void configure_git_filters ()
64+ {
65+ std::string escaped_git_crypt_path (escape_shell_arg (our_exe_path ()) );
7366
74- if (! successful_exit ( system (command. c_str ()))) {
75- throw Error ( " ' git config' failed " );
76- }
67+ git_config ( " filter.git-crypt.smudge " , escaped_git_crypt_path + " smudge " );
68+ git_config ( " filter. git-crypt.clean " , escaped_git_crypt_path + " clean " );
69+ git_config ( " diff.git-crypt.textconv " , escaped_git_crypt_path + " diff " );
7770}
7871
7972static std::string get_internal_key_path ()
8073{
81- std::stringstream output;
74+ // git rev-parse --git-dir
75+ std::vector<std::string> command;
76+ command.push_back (" git" );
77+ command.push_back (" rev-parse" );
78+ command.push_back (" --git-dir" );
79+
80+ std::stringstream output;
8281
83- if (!successful_exit (exec_command (" git rev-parse --git-dir " , output))) {
82+ if (!successful_exit (exec_command (command , output))) {
8483 throw Error (" 'git rev-parse --git-dir' failed - is this a Git repository?" );
8584 }
8685
87- std::string path;
86+ std::string path;
8887 std::getline (output, path);
8988 path += " /git-crypt/key" ;
9089 return path;
9190}
9291
9392static std::string get_repo_keys_path ()
9493{
95- std::stringstream output;
94+ // git rev-parse --show-toplevel
95+ std::vector<std::string> command;
96+ command.push_back (" git" );
97+ command.push_back (" rev-parse" );
98+ command.push_back (" --show-toplevel" );
99+
100+ std::stringstream output;
96101
97- if (!successful_exit (exec_command (" git rev-parse --show-toplevel " , output))) {
102+ if (!successful_exit (exec_command (command , output))) {
98103 throw Error (" 'git rev-parse --show-toplevel' failed - is this a Git repository?" );
99104 }
100105
101- std::string path;
106+ std::string path;
102107 std::getline (output, path);
103108
104109 if (path.empty ()) {
@@ -110,6 +115,52 @@ static std::string get_repo_keys_path ()
110115 return path;
111116}
112117
118+ static std::string get_path_to_top ()
119+ {
120+ // git rev-parse --show-cdup
121+ std::vector<std::string> command;
122+ command.push_back (" git" );
123+ command.push_back (" rev-parse" );
124+ command.push_back (" --show-cdup" );
125+
126+ std::stringstream output;
127+
128+ if (!successful_exit (exec_command (command, output))) {
129+ throw Error (" 'git rev-parse --show-cdup' failed - is this a Git repository?" );
130+ }
131+
132+ std::string path_to_top;
133+ std::getline (output, path_to_top);
134+
135+ return path_to_top;
136+ }
137+
138+ static void get_git_status (std::ostream& output)
139+ {
140+ // git status -uno --porcelain
141+ std::vector<std::string> command;
142+ command.push_back (" git" );
143+ command.push_back (" status" );
144+ command.push_back (" -uno" ); // don't show untracked files
145+ command.push_back (" --porcelain" );
146+
147+ if (!successful_exit (exec_command (command, output))) {
148+ throw Error (" 'git status' failed - is this a Git repository?" );
149+ }
150+ }
151+
152+ static bool check_if_head_exists ()
153+ {
154+ // git rev-parse HEAD
155+ std::vector<std::string> command;
156+ command.push_back (" git" );
157+ command.push_back (" rev-parse" );
158+ command.push_back (" HEAD" );
159+
160+ std::stringstream output;
161+ return successful_exit (exec_command (command, output));
162+ }
163+
113164static void load_key (Key_file& key_file, const char * legacy_path =0 )
114165{
115166 if (legacy_path) {
@@ -421,20 +472,20 @@ int unlock (int argc, char** argv)
421472 return 2 ;
422473 }
423474
424- // 0. Check to see if HEAD exists. See below why we do this.
425- bool head_exists = successful_exit (system (" git rev-parse HEAD >/dev/null 2>/dev/null" ));
426-
427- // 1. Make sure working directory is clean (ignoring untracked files)
475+ // 0. Make sure working directory is clean (ignoring untracked files)
428476 // We do this because we run 'git checkout -f HEAD' later and we don't
429477 // want the user to lose any changes. 'git checkout -f HEAD' doesn't touch
430478 // untracked files so it's safe to ignore those.
431- int status;
479+
480+ // Running 'git status' also serves as a check that the Git repo is accessible.
481+
432482 std::stringstream status_output;
433- status = exec_command (" git status -uno --porcelain" , status_output);
434- if (!successful_exit (status)) {
435- std::clog << " Error: 'git status' failed - is this a git repository?" << std::endl;
436- return 1 ;
437- } else if (status_output.peek () != -1 && head_exists) {
483+ get_git_status (status_output);
484+
485+ // 1. Check to see if HEAD exists. See below why we do this.
486+ bool head_exists = check_if_head_exists ();
487+
488+ if (status_output.peek () != -1 && head_exists) {
438489 // We only care that the working directory is dirty if HEAD exists.
439490 // If HEAD doesn't exist, we won't be resetting to it (see below) so
440491 // it doesn't matter that the working directory is dirty.
@@ -446,11 +497,7 @@ int unlock (int argc, char** argv)
446497 // 2. Determine the path to the top of the repository. We pass this as the argument
447498 // to 'git checkout' below. (Determine the path now so in case it fails we haven't already
448499 // mucked with the git config.)
449- std::stringstream cdup_output;
450- if (!successful_exit (exec_command (" git rev-parse --show-cdup" , cdup_output))) {
451- std::clog << " Error: 'git rev-parse --show-cdup' failed" << std::endl;
452- return 1 ;
453- }
500+ std::string path_to_top (get_path_to_top ());
454501
455502 // 3. Install the key
456503 Key_file key_file;
@@ -504,17 +551,20 @@ int unlock (int argc, char** argv)
504551 // If HEAD doesn't exist (perhaps because this repo doesn't have any files yet)
505552 // just skip the checkout.
506553 if (head_exists) {
507- std::string path_to_top;
508- std::getline (cdup_output, path_to_top);
509-
510- std::string command (" git checkout -f HEAD -- " );
554+ // git checkout -f HEAD -- path/to/top
555+ std::vector<std::string> command;
556+ command.push_back (" git" );
557+ command.push_back (" checkout" );
558+ command.push_back (" -f" );
559+ command.push_back (" HEAD" );
560+ command.push_back (" --" );
511561 if (path_to_top.empty ()) {
512- command += " ." ;
562+ command. push_back ( " ." ) ;
513563 } else {
514- command += escape_shell_arg (path_to_top);
564+ command. push_back (path_to_top);
515565 }
516566
517- if (!successful_exit (system (command. c_str () ))) {
567+ if (!successful_exit (exec_command (command))) {
518568 std::clog << " Error: 'git checkout' failed" << std::endl;
519569 std::clog << " git-crypt has been set up but existing encrypted files have not been decrypted" << std::endl;
520570 return 1 ;
@@ -563,13 +613,12 @@ int add_collab (int argc, char** argv)
563613
564614 // add/commit the new files
565615 if (!new_files.empty ()) {
566- // git add ...
567- std::string command (" git add" );
568- for (std::vector<std::string>::const_iterator file (new_files.begin ()); file != new_files.end (); ++file) {
569- command += " " ;
570- command += escape_shell_arg (*file);
571- }
572- if (!successful_exit (system (command.c_str ()))) {
616+ // git add NEW_FILE ...
617+ std::vector<std::string> command;
618+ command.push_back (" git" );
619+ command.push_back (" add" );
620+ command.insert (command.end (), new_files.begin (), new_files.end ());
621+ if (!successful_exit (exec_command (command))) {
573622 std::clog << " Error: 'git add' failed" << std::endl;
574623 return 1 ;
575624 }
@@ -582,14 +631,15 @@ int add_collab (int argc, char** argv)
582631 commit_message_builder << ' \t ' << gpg_shorten_fingerprint (*collab) << ' ' << gpg_get_uid (*collab) << ' \n ' ;
583632 }
584633
585- command = " git commit -m " ;
586- command += escape_shell_arg (commit_message_builder.str ());
587- for (std::vector<std::string>::const_iterator file (new_files.begin ()); file != new_files.end (); ++file) {
588- command += " " ;
589- command += escape_shell_arg (*file);
590- }
634+ // git commit -m MESSAGE NEW_FILE ...
635+ command.clear ();
636+ command.push_back (" git" );
637+ command.push_back (" commit" );
638+ command.push_back (" -m" );
639+ command.push_back (commit_message_builder.str ());
640+ command.insert (command.end (), new_files.begin (), new_files.end ());
591641
592- if (!successful_exit (system (command. c_str () ))) {
642+ if (!successful_exit (exec_command (command))) {
593643 std::clog << " Error: 'git commit' failed" << std::endl;
594644 return 1 ;
595645 }
0 commit comments