@@ -115,6 +115,8 @@ var flAskPassURL = pflag.String("askpass-url", envString("GIT_ASKPASS_URL", ""),
115115
116116var flGitCmd = pflag .String ("git" , envString ("GIT_SYNC_GIT" , "git" ),
117117 "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" )
118120
119121var flHTTPBind = pflag .String ("http-bind" , envString ("GIT_SYNC_HTTP_BIND" , "" ),
120122 "the bind address (including port) for git-sync's HTTP endpoint" )
@@ -485,6 +487,14 @@ func main() {
485487 }
486488 }
487489
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+
488498 // The scope of the initialization context ends here, so we call cancel to release resources associated with it.
489499 cancel ()
490500
@@ -941,12 +951,15 @@ func cmdForLog(command string, args ...string) string {
941951 if strings .ContainsAny (command , " \t \n " ) {
942952 command = fmt .Sprintf ("%q" , command )
943953 }
954+ // Don't modify the passed-in args.
955+ argsCopy := make ([]string , len (args ))
956+ copy (argsCopy , args )
944957 for i := range args {
945958 if strings .ContainsAny (args [i ], " \t \n " ) {
946- args [i ] = fmt .Sprintf ("%q" , args [i ])
959+ argsCopy [i ] = fmt .Sprintf ("%q" , args [i ])
947960 }
948961 }
949- return command + " " + strings .Join (args , " " )
962+ return command + " " + strings .Join (argsCopy , " " )
950963}
951964
952965func runCommand (ctx context.Context , cwd , command string , args ... string ) (string , error ) {
@@ -1039,8 +1052,7 @@ func (git *repoSync) SetupCookieFile(ctx context.Context) error {
10391052 return fmt .Errorf ("can't access git cookiefile: %w" , err )
10401053 }
10411054
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 {
10441056 return fmt .Errorf ("can't configure git cookiefile: %w" , err )
10451057 }
10461058
@@ -1102,6 +1114,167 @@ func (git *repoSync) CallAskPassURL(ctx context.Context) error {
11021114 return nil
11031115}
11041116
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+
11051278// This string is formatted for 80 columns. Please keep it that way.
11061279// DO NOT USE TABS.
11071280var manual = `
@@ -1164,17 +1337,20 @@ OPTIONS
11641337 Create a shallow clone with history truncated to the specified
11651338 number of commits.
11661339
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-
11741340 --git <string>, $GIT_SYNC_GIT
11751341 The git command to run (subject to PATH search, mostly for testing).
11761342 (default: git)
11771343
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+
11781354 -h, --help
11791355 Print help text and exit.
11801356
@@ -1190,6 +1366,13 @@ OPTIONS
11901366 Enable the pprof debug endpoints on git-sync's HTTP endpoint (see
11911367 --http-bind). (default: false)
11921368
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+
11931376 --man
11941377 Print this manual and exit.
11951378
0 commit comments