@@ -497,11 +497,28 @@ func vcsFromDir(dir, srcRoot string) (vcs *vcsCmd, root string, err error) {
497
497
return nil , "" , fmt .Errorf ("directory %q is outside source root %q" , dir , srcRoot )
498
498
}
499
499
500
+ var vcsRet * vcsCmd
501
+ var rootRet string
502
+
500
503
origDir := dir
501
504
for len (dir ) > len (srcRoot ) {
502
505
for _ , vcs := range vcsList {
503
506
if _ , err := os .Stat (filepath .Join (dir , "." + vcs .cmd )); err == nil {
504
- return vcs , filepath .ToSlash (dir [len (srcRoot )+ 1 :]), nil
507
+ root := filepath .ToSlash (dir [len (srcRoot )+ 1 :])
508
+ // Record first VCS we find, but keep looking,
509
+ // to detect mistakes like one kind of VCS inside another.
510
+ if vcsRet == nil {
511
+ vcsRet = vcs
512
+ rootRet = root
513
+ continue
514
+ }
515
+ // Allow .git inside .git, which can arise due to submodules.
516
+ if vcsRet == vcs && vcs .cmd == "git" {
517
+ continue
518
+ }
519
+ // Otherwise, we have one VCS inside a different VCS.
520
+ return nil , "" , fmt .Errorf ("directory %q uses %s, but parent %q uses %s" ,
521
+ filepath .Join (srcRoot , rootRet ), vcsRet .cmd , filepath .Join (srcRoot , root ), vcs .cmd )
505
522
}
506
523
}
507
524
@@ -514,9 +531,48 @@ func vcsFromDir(dir, srcRoot string) (vcs *vcsCmd, root string, err error) {
514
531
dir = ndir
515
532
}
516
533
534
+ if vcsRet != nil {
535
+ return vcsRet , rootRet , nil
536
+ }
537
+
517
538
return nil , "" , fmt .Errorf ("directory %q is not using a known version control system" , origDir )
518
539
}
519
540
541
+ // checkNestedVCS checks for an incorrectly-nested VCS-inside-VCS
542
+ // situation for dir, checking parents up until srcRoot.
543
+ func checkNestedVCS (vcs * vcsCmd , dir , srcRoot string ) error {
544
+ if len (dir ) <= len (srcRoot ) || dir [len (srcRoot )] != filepath .Separator {
545
+ return fmt .Errorf ("directory %q is outside source root %q" , dir , srcRoot )
546
+ }
547
+
548
+ otherDir := dir
549
+ for len (otherDir ) > len (srcRoot ) {
550
+ for _ , otherVCS := range vcsList {
551
+ if _ , err := os .Stat (filepath .Join (dir , "." + otherVCS .cmd )); err == nil {
552
+ // Allow expected vcs in original dir.
553
+ if otherDir == dir && otherVCS == vcs {
554
+ continue
555
+ }
556
+ // Allow .git inside .git, which can arise due to submodules.
557
+ if otherVCS == vcs && vcs .cmd == "git" {
558
+ continue
559
+ }
560
+ // Otherwise, we have one VCS inside a different VCS.
561
+ return fmt .Errorf ("directory %q uses %s, but parent %q uses %s" , dir , vcs .cmd , otherDir , otherVCS .cmd )
562
+ }
563
+ }
564
+ // Move to parent.
565
+ newDir := filepath .Dir (otherDir )
566
+ if len (newDir ) >= len (otherDir ) {
567
+ // Shouldn't happen, but just in case, stop.
568
+ break
569
+ }
570
+ otherDir = newDir
571
+ }
572
+
573
+ return nil
574
+ }
575
+
520
576
// repoRoot represents a version control system, a repo, and a root of
521
577
// where to put it on disk.
522
578
type repoRoot struct {
0 commit comments