@@ -115,6 +115,8 @@ var flAskPassURL = pflag.String("askpass-url", envString("GIT_ASKPASS_URL", ""),
115
115
116
116
var flGitCmd = pflag .String ("git" , envString ("GIT_SYNC_GIT" , "git" ),
117
117
"the git command to run (subject to PATH search, mostly for testing)" )
118
+ var flGitConfig = pflag .String ("git-config" , envString ("GIT_SYNC_GIT_CONFIG" , "" ),
119
+ "additional git config options in 'key1:val1,key2:val2' format" )
118
120
119
121
var flHTTPBind = pflag .String ("http-bind" , envString ("GIT_SYNC_HTTP_BIND" , "" ),
120
122
"the bind address (including port) for git-sync's HTTP endpoint" )
@@ -485,6 +487,14 @@ func main() {
485
487
}
486
488
}
487
489
490
+ // This needs to be after all other git-related config flags.
491
+ if * flGitConfig != "" {
492
+ if err := setupExtraGitConfigs (ctx , * flGitConfig ); err != nil {
493
+ log .Error (err , "ERROR: can't set additional git configs" )
494
+ os .Exit (1 )
495
+ }
496
+ }
497
+
488
498
// The scope of the initialization context ends here, so we call cancel to release resources associated with it.
489
499
cancel ()
490
500
@@ -941,12 +951,15 @@ func cmdForLog(command string, args ...string) string {
941
951
if strings .ContainsAny (command , " \t \n " ) {
942
952
command = fmt .Sprintf ("%q" , command )
943
953
}
954
+ // Don't modify the passed-in args.
955
+ argsCopy := make ([]string , len (args ))
956
+ copy (argsCopy , args )
944
957
for i := range args {
945
958
if strings .ContainsAny (args [i ], " \t \n " ) {
946
- args [i ] = fmt .Sprintf ("%q" , args [i ])
959
+ argsCopy [i ] = fmt .Sprintf ("%q" , args [i ])
947
960
}
948
961
}
949
- return command + " " + strings .Join (args , " " )
962
+ return command + " " + strings .Join (argsCopy , " " )
950
963
}
951
964
952
965
func runCommand (ctx context.Context , cwd , command string , args ... string ) (string , error ) {
@@ -1039,8 +1052,7 @@ func (git *repoSync) SetupCookieFile(ctx context.Context) error {
1039
1052
return fmt .Errorf ("can't access git cookiefile: %w" , err )
1040
1053
}
1041
1054
1042
- if _ , err = runCommand (ctx , "" ,
1043
- git .cmd , "config" , "--global" , "http.cookiefile" , pathToCookieFile ); err != nil {
1055
+ if _ , err = runCommand (ctx , "" , git .cmd , "config" , "--global" , "http.cookiefile" , pathToCookieFile ); err != nil {
1044
1056
return fmt .Errorf ("can't configure git cookiefile: %w" , err )
1045
1057
}
1046
1058
@@ -1102,6 +1114,167 @@ func (git *repoSync) CallAskPassURL(ctx context.Context) error {
1102
1114
return nil
1103
1115
}
1104
1116
1117
+ func setupExtraGitConfigs (ctx context.Context , configsFlag string ) error {
1118
+ log .V (1 ).Info ("setting additional git configs" )
1119
+
1120
+ configs , err := parseGitConfigs (configsFlag )
1121
+ if err != nil {
1122
+ return fmt .Errorf ("can't parse --git-config flag: %v" , err )
1123
+ }
1124
+ for _ , kv := range configs {
1125
+ if _ , err := runCommand (ctx , "" , * flGitCmd , "config" , "--global" , kv .key , kv .val ); err != nil {
1126
+ return fmt .Errorf ("error configuring additional git configs %q %q: %v" , kv .key , kv .val , err )
1127
+ }
1128
+ }
1129
+
1130
+ return nil
1131
+ }
1132
+
1133
+ type keyVal struct {
1134
+ key string
1135
+ val string
1136
+ }
1137
+
1138
+ func parseGitConfigs (configsFlag string ) ([]keyVal , error ) {
1139
+ ch := make (chan rune )
1140
+ stop := make (chan bool )
1141
+ go func () {
1142
+ for _ , r := range configsFlag {
1143
+ select {
1144
+ case <- stop :
1145
+ break
1146
+ default :
1147
+ ch <- r
1148
+ }
1149
+ }
1150
+ close (ch )
1151
+ return
1152
+ }()
1153
+
1154
+ result := []keyVal {}
1155
+
1156
+ // This assumes it is at the start of a key.
1157
+ for {
1158
+ cur := keyVal {}
1159
+ var err error
1160
+
1161
+ // Peek and see if we have a key.
1162
+ if r , ok := <- ch ; ! ok {
1163
+ break
1164
+ } else {
1165
+ cur .key , err = parseGitConfigKey (r , ch )
1166
+ if err != nil {
1167
+ return nil , err
1168
+ }
1169
+ }
1170
+
1171
+ // Peek and see if we have a value.
1172
+ if r , ok := <- ch ; ! ok {
1173
+ return nil , fmt .Errorf ("key %q: no value" , cur .key )
1174
+ } else {
1175
+ if r == '"' {
1176
+ cur .val , err = parseGitConfigQVal (ch )
1177
+ if err != nil {
1178
+ return nil , fmt .Errorf ("key %q: %v" , cur .key , err )
1179
+ }
1180
+ } else {
1181
+ cur .val , err = parseGitConfigVal (r , ch )
1182
+ if err != nil {
1183
+ return nil , fmt .Errorf ("key %q: %v" , cur .key , err )
1184
+ }
1185
+ }
1186
+ }
1187
+
1188
+ result = append (result , cur )
1189
+ }
1190
+
1191
+ return result , nil
1192
+ }
1193
+
1194
+ func parseGitConfigKey (r rune , ch <- chan rune ) (string , error ) {
1195
+ buf := make ([]rune , 0 , 64 )
1196
+ buf = append (buf , r )
1197
+
1198
+ for r := range ch {
1199
+ switch {
1200
+ case r == ':' :
1201
+ return string (buf ), nil
1202
+ default :
1203
+ // This can accumulate things that git doesn't allow, but we'll
1204
+ // just let git handle it, rather than try to pre-validate to their
1205
+ // spec.
1206
+ buf = append (buf , r )
1207
+ }
1208
+ }
1209
+ return "" , fmt .Errorf ("unexpected end of key: %q" , string (buf ))
1210
+ }
1211
+
1212
+ func parseGitConfigQVal (ch <- chan rune ) (string , error ) {
1213
+ buf := make ([]rune , 0 , 64 )
1214
+
1215
+ for r := range ch {
1216
+ switch r {
1217
+ case '\\' :
1218
+ if e , err := unescape (ch ); err != nil {
1219
+ return "" , err
1220
+ } else {
1221
+ buf = append (buf , e )
1222
+ }
1223
+ case '"' :
1224
+ // Once we have a closing quote, the next must be either a comma or
1225
+ // end-of-string. This helps reset the state for the next key, if
1226
+ // there is one.
1227
+ r , ok := <- ch
1228
+ if ok && r != ',' {
1229
+ return "" , fmt .Errorf ("unexpected trailing character '%c'" , r )
1230
+ }
1231
+ return string (buf ), nil
1232
+ default :
1233
+ buf = append (buf , r )
1234
+ }
1235
+ }
1236
+ return "" , fmt .Errorf ("unexpected end of value: %q" , string (buf ))
1237
+ }
1238
+
1239
+ func parseGitConfigVal (r rune , ch <- chan rune ) (string , error ) {
1240
+ buf := make ([]rune , 0 , 64 )
1241
+ buf = append (buf , r )
1242
+
1243
+ for r := range ch {
1244
+ switch r {
1245
+ case '\\' :
1246
+ if r , err := unescape (ch ); err != nil {
1247
+ return "" , err
1248
+ } else {
1249
+ buf = append (buf , r )
1250
+ }
1251
+ case ',' :
1252
+ return string (buf ), nil
1253
+ default :
1254
+ buf = append (buf , r )
1255
+ }
1256
+ }
1257
+ // We ran out of characters, but that's OK.
1258
+ return string (buf ), nil
1259
+ }
1260
+
1261
+ // unescape processes most of the documented escapes that git config supports.
1262
+ func unescape (ch <- chan rune ) (rune , error ) {
1263
+ r , ok := <- ch
1264
+ if ! ok {
1265
+ return 0 , fmt .Errorf ("unexpected end of escape sequence" )
1266
+ }
1267
+ switch r {
1268
+ case 'n' :
1269
+ return '\n' , nil
1270
+ case 't' :
1271
+ return '\t' , nil
1272
+ case '"' , ',' , '\\' :
1273
+ return r , nil
1274
+ }
1275
+ return 0 , fmt .Errorf ("unsupported escape character: '%c'" , r )
1276
+ }
1277
+
1105
1278
// This string is formatted for 80 columns. Please keep it that way.
1106
1279
// DO NOT USE TABS.
1107
1280
var manual = `
@@ -1164,17 +1337,20 @@ OPTIONS
1164
1337
Create a shallow clone with history truncated to the specified
1165
1338
number of commits.
1166
1339
1167
- --link <string>, $GIT_SYNC_LINK
1168
- The name of the final symlink (under --root) which will point to the
1169
- current git worktree. This must be a filename, not a path, and may
1170
- not start with a period. The destination of this link (i.e.
1171
- readlink()) is the currently checked out SHA. (default: the leaf
1172
- dir of --repo)
1173
-
1174
1340
--git <string>, $GIT_SYNC_GIT
1175
1341
The git command to run (subject to PATH search, mostly for testing).
1176
1342
(default: git)
1177
1343
1344
+ --git-config <string>, $GIT_SYNC_GIT_CONFIG
1345
+ Additional git config options in 'key1:val1,key2:val2' format. The
1346
+ key parts are passed to 'git config' and must be valid syntax for
1347
+ that command. The val parts can be either quoted or unquoted
1348
+ values. For all values the following escape sequences are
1349
+ supported: '\n' => [newline], '\t' => [tab], '\"' => '"', '\,' =>
1350
+ ',', '\\' => '\'. Within unquoted values, commas MUST be escaped.
1351
+ Within quoted values, commas MAY be escaped, but are not required
1352
+ to be. Any other escape sequence is an error. (default: "")
1353
+
1178
1354
-h, --help
1179
1355
Print help text and exit.
1180
1356
@@ -1190,6 +1366,13 @@ OPTIONS
1190
1366
Enable the pprof debug endpoints on git-sync's HTTP endpoint (see
1191
1367
--http-bind). (default: false)
1192
1368
1369
+ --link <string>, $GIT_SYNC_LINK
1370
+ The name of the final symlink (under --root) which will point to the
1371
+ current git worktree. This must be a filename, not a path, and may
1372
+ not start with a period. The destination of this link (i.e.
1373
+ readlink()) is the currently checked out SHA. (default: the leaf
1374
+ dir of --repo)
1375
+
1193
1376
--man
1194
1377
Print this manual and exit.
1195
1378
0 commit comments