@@ -109,6 +109,7 @@ const defaultDirMode = os.FileMode(0775) // subject to umask
109
109
type repoSync struct {
110
110
cmd string // the git command to run
111
111
root absPath // absolute path to the root directory
112
+ localRepo absPath // absolute path to the local repo
112
113
remoteRepo string // remote repo to sync
113
114
ref string // the ref to sync
114
115
depth int // for shallow sync
@@ -598,6 +599,7 @@ func main() {
598
599
cmd : * flGitCmd ,
599
600
root : absRoot ,
600
601
remoteRepo : * flRepo ,
602
+ localRepo : absRoot .Join (".repo" ),
601
603
ref : * flRef ,
602
604
depth : * flDepth ,
603
605
submodules : submodulesMode (* flSubmodules ),
@@ -1056,40 +1058,37 @@ func (git *repoSync) initRepo(ctx context.Context) error {
1056
1058
needGitInit := false
1057
1059
1058
1060
// Check out the git root, and see if it is already usable.
1059
- _ , err := os .Stat (git .root .String ())
1061
+ _ , err := os .Stat (git .localRepo .String ())
1060
1062
switch {
1061
1063
case os .IsNotExist (err ):
1062
1064
// Probably the first sync. defaultDirMode ensures that this is usable
1063
1065
// as a volume when the consumer isn't running as the same UID.
1064
- git .log .V (1 ).Info ("repo directory does not exist, creating it" , "path" , git .root )
1065
- if err := os .MkdirAll (git .root .String (), defaultDirMode ); err != nil {
1066
+ git .log .V (1 ).Info ("repo directory does not exist, creating it" , "path" , git .localRepo )
1067
+ if err := os .MkdirAll (git .localRepo .String (), defaultDirMode ); err != nil {
1066
1068
return err
1067
1069
}
1068
1070
needGitInit = true
1069
1071
case err != nil :
1070
1072
return err
1071
1073
default :
1072
1074
// Make sure the directory we found is actually usable.
1073
- git .log .V (3 ).Info ("repo directory exists" , "path" , git .root )
1075
+ git .log .V (3 ).Info ("repo directory exists" , "path" , git .localRepo )
1074
1076
if git .sanityCheckRepo (ctx ) {
1075
- git .log .V (4 ).Info ("repo directory is valid" , "path" , git .root )
1077
+ git .log .V (4 ).Info ("repo directory is valid" , "path" , git .localRepo )
1076
1078
} else {
1077
- // Maybe a previous run crashed? Git won't use this dir. We remove
1078
- // the contents rather than the dir itself, because a common use-case
1079
- // is to have a volume mounted at git.root, which makes removing it
1080
- // impossible.
1081
- git .log .V (0 ).Info ("repo directory was empty or failed checks" , "path" , git .root )
1082
- if err := removeDirContents (git .root , git .log ); err != nil {
1083
- return fmt .Errorf ("can't wipe unusable root directory: %w" , err )
1079
+ // Maybe a previous run crashed? Git won't use this dir.
1080
+ git .log .V (0 ).Info ("repo directory was empty or failed checks" , "path" , git .localRepo )
1081
+ if err := os .RemoveAll (git .localRepo .String ()); err != nil {
1082
+ return fmt .Errorf ("can't remove unusable repo directory: %w" , err )
1084
1083
}
1085
1084
needGitInit = true
1086
1085
}
1087
1086
}
1088
1087
1089
1088
if needGitInit {
1090
1089
// Running `git init` in an existing repo is safe (according to git docs).
1091
- git .log .V (0 ).Info ("initializing repo directory" , "path" , git .root )
1092
- if _ , _ , err := git .Run (ctx , git .root , "init" , "-b" , "git-sync" ); err != nil {
1090
+ git .log .V (0 ).Info ("initializing repo directory" , "path" , git .localRepo )
1091
+ if _ , _ , err := git .Run (ctx , git .localRepo , "init" , "-b" , "git-sync" ); err != nil {
1093
1092
return err
1094
1093
}
1095
1094
if ! git .sanityCheckRepo (ctx ) {
@@ -1099,17 +1098,17 @@ func (git *repoSync) initRepo(ctx context.Context) error {
1099
1098
1100
1099
// The "origin" remote has special meaning, like in relative-path
1101
1100
// submodules.
1102
- if stdout , stderr , err := git .Run (ctx , git .root , "remote" , "get-url" , "origin" ); err != nil {
1101
+ if stdout , stderr , err := git .Run (ctx , git .localRepo , "remote" , "get-url" , "origin" ); err != nil {
1103
1102
if ! strings .Contains (stderr , "No such remote" ) {
1104
1103
return err
1105
1104
}
1106
1105
// It doesn't exist - make it.
1107
- if _ , _ , err := git .Run (ctx , git .root , "remote" , "add" , "origin" , git .remoteRepo ); err != nil {
1106
+ if _ , _ , err := git .Run (ctx , git .localRepo , "remote" , "add" , "origin" , git .remoteRepo ); err != nil {
1108
1107
return err
1109
1108
}
1110
1109
} else if strings .TrimSpace (stdout ) != git .remoteRepo {
1111
1110
// It exists, but is wrong.
1112
- if _ , _ , err := git .Run (ctx , git .root , "remote" , "set-url" , "origin" , git .remoteRepo ); err != nil {
1111
+ if _ , _ , err := git .Run (ctx , git .localRepo , "remote" , "set-url" , "origin" , git .remoteRepo ); err != nil {
1113
1112
return err
1114
1113
}
1115
1114
}
@@ -1142,32 +1141,32 @@ func (git *repoSync) removeStaleWorktrees() (int, error) {
1142
1141
1143
1142
// sanityCheckRepo tries to make sure that the repo dir is a valid git repository.
1144
1143
func (git * repoSync ) sanityCheckRepo (ctx context.Context ) bool {
1145
- git .log .V (3 ).Info ("sanity-checking git repo" , "repo" , git .root )
1144
+ git .log .V (3 ).Info ("sanity-checking git repo" , "repo" , git .localRepo )
1146
1145
// If it is empty, we are done.
1147
- if empty , err := dirIsEmpty (git .root ); err != nil {
1148
- git .log .Error (err , "can't list repo directory" , "path" , git .root )
1146
+ if empty , err := dirIsEmpty (git .localRepo ); err != nil {
1147
+ git .log .Error (err , "can't list repo directory" , "path" , git .localRepo )
1149
1148
return false
1150
1149
} else if empty {
1151
- git .log .V (3 ).Info ("repo directory is empty" , "path" , git .root )
1150
+ git .log .V (3 ).Info ("repo directory is empty" , "path" , git .localRepo )
1152
1151
return false
1153
1152
}
1154
1153
1155
1154
// Check that this is actually the root of the repo.
1156
- if root , _ , err := git .Run (ctx , git .root , "rev-parse" , "--show-toplevel" ); err != nil {
1157
- git .log .Error (err , "can't get repo toplevel" , "path" , git .root )
1155
+ if root , _ , err := git .Run (ctx , git .localRepo , "rev-parse" , "--show-toplevel" ); err != nil {
1156
+ git .log .Error (err , "can't get repo toplevel" , "path" , git .localRepo )
1158
1157
return false
1159
1158
} else {
1160
1159
root = strings .TrimSpace (root )
1161
- if root != git .root .String () {
1162
- git .log .Error (nil , "repo directory is under another repo" , "path" , git .root , "parent" , root )
1160
+ if root != git .localRepo .String () {
1161
+ git .log .Error (nil , "repo directory is under another repo" , "path" , git .localRepo , "parent" , root )
1163
1162
return false
1164
1163
}
1165
1164
}
1166
1165
1167
1166
// Consistency-check the repo. Don't use --verbose because it can be
1168
1167
// REALLY verbose.
1169
- if _ , _ , err := git .Run (ctx , git .root , "fsck" , "--no-progress" , "--connectivity-only" ); err != nil {
1170
- git .log .Error (err , "repo fsck failed" , "path" , git .root )
1168
+ if _ , _ , err := git .Run (ctx , git .localRepo , "fsck" , "--no-progress" , "--connectivity-only" ); err != nil {
1169
+ git .log .Error (err , "repo fsck failed" , "path" , git .localRepo )
1171
1170
return false
1172
1171
}
1173
1172
@@ -1179,7 +1178,7 @@ func (git *repoSync) sanityCheckRepo(ctx context.Context) bool {
1179
1178
// files checked out - git could have died halfway through and the repo will
1180
1179
// still pass this check.
1181
1180
func (git * repoSync ) sanityCheckWorktree (ctx context.Context , worktree worktree ) bool {
1182
- git .log .V (3 ).Info ("sanity-checking worktree" , "repo" , git .root , "worktree" , worktree )
1181
+ git .log .V (3 ).Info ("sanity-checking worktree" , "repo" , git .localRepo , "worktree" , worktree )
1183
1182
1184
1183
// If it is empty, we are done.
1185
1184
if empty , err := dirIsEmpty (worktree .Path ()); err != nil {
@@ -1219,13 +1218,6 @@ func dirIsEmpty(dir absPath) (bool, error) {
1219
1218
return len (dirents ) == 0 , nil
1220
1219
}
1221
1220
1222
- // removeDirContents iterated the specified dir and removes all contents
1223
- func removeDirContents (dir absPath , log * logging.Logger ) error {
1224
- return removeDirContentsIf (dir , log , func (fi os.FileInfo ) (bool , error ) {
1225
- return true , nil
1226
- })
1227
- }
1228
-
1229
1221
func removeDirContentsIf (dir absPath , log * logging.Logger , fn func (fi os.FileInfo ) (bool , error )) error {
1230
1222
dirents , err := os .ReadDir (dir .String ())
1231
1223
if err != nil {
@@ -1309,7 +1301,7 @@ func (git *repoSync) removeWorktree(ctx context.Context, worktree worktree) erro
1309
1301
if err := os .RemoveAll (worktree .Path ().String ()); err != nil {
1310
1302
return fmt .Errorf ("error removing directory: %w" , err )
1311
1303
}
1312
- if _ , _ , err := git .Run (ctx , git .root , "worktree" , "prune" , "--verbose" ); err != nil {
1304
+ if _ , _ , err := git .Run (ctx , git .localRepo , "worktree" , "prune" , "--verbose" ); err != nil {
1313
1305
return err
1314
1306
}
1315
1307
return nil
@@ -1330,7 +1322,7 @@ func (git *repoSync) createWorktree(ctx context.Context, hash string) (worktree,
1330
1322
}
1331
1323
1332
1324
git .log .V (1 ).Info ("adding worktree" , "path" , worktree .Path (), "hash" , hash )
1333
- _ , _ , err := git .Run (ctx , git .root , "worktree" , "add" , "--force" , "--detach" , worktree .Path ().String (), hash , "--no-checkout" )
1325
+ _ , _ , err := git .Run (ctx , git .localRepo , "worktree" , "add" , "--force" , "--detach" , worktree .Path ().String (), hash , "--no-checkout" )
1334
1326
if err != nil {
1335
1327
return "" , err
1336
1328
}
@@ -1348,7 +1340,7 @@ func (git *repoSync) configureWorktree(ctx context.Context, worktree worktree) e
1348
1340
// using relative paths, so that other containers can use a different volume
1349
1341
// mount name.
1350
1342
rootDotGit := ""
1351
- if rel , err := filepath .Rel (worktree .Path ().String (), git .root .String ()); err != nil {
1343
+ if rel , err := filepath .Rel (worktree .Path ().String (), git .localRepo .String ()); err != nil {
1352
1344
return err
1353
1345
} else {
1354
1346
rootDotGit = filepath .Join (rel , ".git" )
@@ -1360,7 +1352,7 @@ func (git *repoSync) configureWorktree(ctx context.Context, worktree worktree) e
1360
1352
1361
1353
// If sparse checkout is requested, configure git for it, otherwise
1362
1354
// unconfigure it.
1363
- gitInfoPath := filepath .Join (git .root .String (), ".git/worktrees" , hash , "info" )
1355
+ gitInfoPath := filepath .Join (git .localRepo .String (), ".git/worktrees" , hash , "info" )
1364
1356
gitSparseConfigPath := filepath .Join (gitInfoPath , "sparse-checkout" )
1365
1357
if git .sparseFile == "" {
1366
1358
os .RemoveAll (gitSparseConfigPath )
@@ -1441,13 +1433,13 @@ func (git *repoSync) cleanup(ctx context.Context) error {
1441
1433
1442
1434
// Let git know we don't need those old commits any more.
1443
1435
git .log .V (3 ).Info ("pruning worktrees" )
1444
- if _ , _ , err := git .Run (ctx , git .root , "worktree" , "prune" , "--verbose" ); err != nil {
1436
+ if _ , _ , err := git .Run (ctx , git .localRepo , "worktree" , "prune" , "--verbose" ); err != nil {
1445
1437
cleanupErrs = append (cleanupErrs , err )
1446
1438
}
1447
1439
1448
1440
// Expire old refs.
1449
1441
git .log .V (3 ).Info ("expiring unreachable refs" )
1450
- if _ , _ , err := git .Run (ctx , git .root , "reflog" , "expire" , "--expire-unreachable=all" , "--all" ); err != nil {
1442
+ if _ , _ , err := git .Run (ctx , git .localRepo , "reflog" , "expire" , "--expire-unreachable=all" , "--all" ); err != nil {
1451
1443
cleanupErrs = append (cleanupErrs , err )
1452
1444
}
1453
1445
@@ -1463,7 +1455,7 @@ func (git *repoSync) cleanup(ctx context.Context) error {
1463
1455
args = append (args , "--aggressive" )
1464
1456
}
1465
1457
git .log .V (3 ).Info ("running git garbage collection" )
1466
- if _ , _ , err := git .Run (ctx , git .root , args ... ); err != nil {
1458
+ if _ , _ , err := git .Run (ctx , git .localRepo , args ... ); err != nil {
1467
1459
cleanupErrs = append (cleanupErrs , err )
1468
1460
}
1469
1461
}
@@ -1566,7 +1558,7 @@ func (git *repoSync) SyncRepo(ctx context.Context, refreshCreds func(context.Con
1566
1558
// their underlying commit hashes, but has no effect if we fetched a
1567
1559
// branch, plain tag, or hash.
1568
1560
remoteHash := ""
1569
- if output , _ , err := git .Run (ctx , git .root , "rev-parse" , "FETCH_HEAD^{}" ); err != nil {
1561
+ if output , _ , err := git .Run (ctx , git .localRepo , "rev-parse" , "FETCH_HEAD^{}" ); err != nil {
1570
1562
return false , "" , err
1571
1563
} else {
1572
1564
remoteHash = strings .Trim (output , "\n " )
@@ -1598,14 +1590,14 @@ func (git *repoSync) SyncRepo(ctx context.Context, refreshCreds func(context.Con
1598
1590
// Reset the repo (note: not the worktree - that happens later) to the new
1599
1591
// ref. This makes subsequent fetches much less expensive. It uses --soft
1600
1592
// so no files are checked out.
1601
- if _ , _ , err := git .Run (ctx , git .root , "reset" , "--soft" , remoteHash ); err != nil {
1593
+ if _ , _ , err := git .Run (ctx , git .localRepo , "reset" , "--soft" , remoteHash ); err != nil {
1602
1594
return false , "" , err
1603
1595
}
1604
1596
1605
1597
// If we have a new hash, make a new worktree
1606
1598
newWorktree := currentWorktree
1607
1599
if changed {
1608
- // Create a worktree for this hash in git.root .
1600
+ // Create a worktree for this hash.
1609
1601
if wt , err := git .createWorktree (ctx , remoteHash ); err != nil {
1610
1602
return false , "" , err
1611
1603
} else {
@@ -1678,15 +1670,15 @@ func (git *repoSync) fetch(ctx context.Context, ref string) error {
1678
1670
args = append (args , "--unshallow" )
1679
1671
}
1680
1672
}
1681
- if _ , _ , err := git .Run (ctx , git .root , args ... ); err != nil {
1673
+ if _ , _ , err := git .Run (ctx , git .localRepo , args ... ); err != nil {
1682
1674
return err
1683
1675
}
1684
1676
1685
1677
return nil
1686
1678
}
1687
1679
1688
1680
func (git * repoSync ) isShallow (ctx context.Context ) (bool , error ) {
1689
- boolStr , _ , err := git .Run (ctx , git .root , "rev-parse" , "--is-shallow-repository" )
1681
+ boolStr , _ , err := git .Run (ctx , git .localRepo , "rev-parse" , "--is-shallow-repository" )
1690
1682
if err != nil {
1691
1683
return false , fmt .Errorf ("can't determine repo shallowness: %w" , err )
1692
1684
}
0 commit comments