@@ -478,6 +478,204 @@ defmodule Mix.Tasks.DepsGitTest do
478
478
purge ( [ GitRepo , GitRepo.MixProject ] )
479
479
end
480
480
481
+ describe "Git depth option" do
482
+ @ describetag :git_depth
483
+
484
+ test "gets and updates Git repos with depth option" do
485
+ Process . put ( :git_repo_opts , depth: 1 )
486
+
487
+ in_fixture ( "no_mixfile" , fn ->
488
+ Mix.Project . push ( GitApp )
489
+
490
+ Mix.Tasks.Deps.Get . run ( [ ] )
491
+ message = "* Getting git_repo (#{ fixture_path ( "git_repo" ) } )"
492
+ assert_received { :mix_shell , :info , [ ^ message ] }
493
+ assert_shallow ( "deps/git_repo" , 1 )
494
+
495
+ # Expand depth
496
+ update_dep ( depth: 2 )
497
+ Mix.Tasks.Deps.Get . run ( [ ] )
498
+ assert_shallow ( "deps/git_repo" , 2 )
499
+
500
+ # Reduce depth
501
+ update_dep ( depth: 1 )
502
+ Mix.Tasks.Deps.Get . run ( [ ] )
503
+ assert_shallow ( "deps/git_repo" , 1 )
504
+ end )
505
+ end
506
+
507
+ test "with tag" do
508
+ Process . put ( :git_repo_opts , depth: 1 , tag: "with_module" )
509
+
510
+ in_fixture ( "no_mixfile" , fn ->
511
+ Mix.Project . push ( GitApp )
512
+
513
+ Mix.Tasks.Deps.Get . run ( [ ] )
514
+ message = "* Getting git_repo (#{ fixture_path ( "git_repo" ) } - with_module)"
515
+ assert_received { :mix_shell , :info , [ ^ message ] }
516
+ assert_shallow ( "deps/git_repo" , 1 )
517
+ end )
518
+ end
519
+
520
+ test "with branch" do
521
+ Process . put ( :git_repo_opts , depth: 1 , branch: "main" )
522
+
523
+ in_fixture ( "no_mixfile" , fn ->
524
+ Mix.Project . push ( GitApp )
525
+
526
+ Mix.Tasks.Deps.Get . run ( [ ] )
527
+ message = "* Getting git_repo (#{ fixture_path ( "git_repo" ) } - main)"
528
+ assert_received { :mix_shell , :info , [ ^ message ] }
529
+ assert_shallow ( "deps/git_repo" , 1 )
530
+ end )
531
+ end
532
+
533
+ test "with ref" do
534
+ [ last , _ | _ ] = get_git_repo_revs ( "git_repo" )
535
+
536
+ Process . put ( :git_repo_opts , depth: 1 , ref: last )
537
+
538
+ in_fixture ( "no_mixfile" , fn ->
539
+ Mix.Project . push ( GitApp )
540
+
541
+ Mix.Tasks.Deps.Get . run ( [ ] )
542
+ message = "* Getting git_repo (#{ fixture_path ( "git_repo" ) } - #{ last } )"
543
+ assert_received { :mix_shell , :info , [ ^ message ] }
544
+ assert_shallow ( "deps/git_repo" , 1 )
545
+ end )
546
+ end
547
+
548
+ test "changing refspec updates retaining depth" do
549
+ [ last , first | _ ] = get_git_repo_revs ( "git_repo" )
550
+
551
+ Process . put ( :git_repo_opts , ref: first , depth: 1 )
552
+
553
+ in_fixture ( "no_mixfile" , fn ->
554
+ Mix.Project . push ( GitApp )
555
+
556
+ Mix.Tasks.Deps.Get . run ( [ ] )
557
+ message = "* Getting git_repo (#{ fixture_path ( "git_repo" ) } - #{ first } )"
558
+ assert_received { :mix_shell , :info , [ ^ message ] }
559
+ assert_shallow ( "deps/git_repo" , 1 )
560
+ assert File . read! ( "mix.lock" ) =~ first
561
+
562
+ # Change refspec
563
+ update_dep ( ref: last , depth: 1 )
564
+ Mix.Tasks.Deps.Get . run ( [ ] )
565
+ assert_shallow ( "deps/git_repo" , 1 )
566
+ assert File . read! ( "mix.lock" ) =~ last
567
+ end )
568
+ end
569
+
570
+ test "removing depth retains shallow repository" do
571
+ # The expectation is that one needs to deps.clean and deps.get the
572
+ # dependency for a full clone.
573
+ Process . put ( :git_repo_opts , depth: 1 )
574
+
575
+ in_fixture ( "no_mixfile" , fn ->
576
+ Mix.Project . push ( GitApp )
577
+
578
+ Mix.Tasks.Deps.Get . run ( [ ] )
579
+ message = "* Getting git_repo (#{ fixture_path ( "git_repo" ) } )"
580
+ assert_received { :mix_shell , :info , [ ^ message ] }
581
+ assert_shallow ( "deps/git_repo" , 1 )
582
+
583
+ # Remove depth
584
+ update_dep ( [ ] )
585
+ Mix.Tasks.Deps.Get . run ( [ ] )
586
+ refute File . read! ( "mix.lock" ) =~ "depth:"
587
+ assert File . exists? ( "deps/git_repo/.git/shallow" )
588
+
589
+ assert System . cmd ( "git" , ~w[ --git-dir=deps/git_repo/.git rev-list --count HEAD] ) ==
590
+ { "1\n " , 0 }
591
+ end )
592
+ end
593
+
594
+ @ tag :git_sparse
595
+ test "with sparse checkout" do
596
+ Process . put ( :git_repo_opts , sparse: "sparse_dir" , depth: 1 )
597
+
598
+ in_fixture ( "no_mixfile" , fn ->
599
+ Mix.Project . push ( GitApp )
600
+
601
+ Mix.Tasks.Deps.Get . run ( [ ] )
602
+ message = "* Getting git_repo (#{ fixture_path ( "git_repo" ) } )"
603
+ assert_received { :mix_shell , :info , [ ^ message ] }
604
+ assert_shallow ( "deps/git_repo" , 1 )
605
+
606
+ refute File . exists? ( "deps/git_repo/mix.exs" )
607
+ assert File . exists? ( "deps/git_repo/sparse_dir/mix.exs" )
608
+ assert File . read! ( "mix.lock" ) =~ "sparse: \" sparse_dir\" "
609
+ end )
610
+ end
611
+
612
+ test "with subdir" do
613
+ Process . put ( :git_repo_opts , subdir: "sparse_dir" , depth: 1 )
614
+
615
+ in_fixture ( "no_mixfile" , fn ->
616
+ Mix.Project . push ( GitApp )
617
+
618
+ Mix.Tasks.Deps.Get . run ( [ ] )
619
+ message = "* Getting git_repo (#{ fixture_path ( "git_repo" ) } )"
620
+ assert_received { :mix_shell , :info , [ ^ message ] }
621
+ assert_shallow ( "deps/git_repo" , 1 )
622
+
623
+ assert File . exists? ( "deps/git_repo/mix.exs" )
624
+ assert File . exists? ( "deps/git_repo/sparse_dir/mix.exs" )
625
+ assert File . read! ( "mix.lock" ) =~ "subdir: \" sparse_dir\" "
626
+ end )
627
+ end
628
+
629
+ test "does not affect submodules depth" do
630
+ # The expectation is that we can add an explicit option in the future,
631
+ # just like git-clone has `--shallow-submodules`.
632
+ Process . put ( :git_repo_opts , submodules: true , depth: 1 )
633
+
634
+ in_fixture ( "no_mixfile" , fn ->
635
+ Mix.Project . push ( GitApp )
636
+
637
+ Mix.Tasks.Deps.Get . run ( [ ] )
638
+ message = "* Getting git_repo (#{ fixture_path ( "git_repo" ) } )"
639
+ assert_received { :mix_shell , :info , [ ^ message ] }
640
+ assert_shallow ( "deps/git_repo" , 1 )
641
+
642
+ assert File . read! ( "mix.lock" ) =~ "submodules: true"
643
+ # TODO: assert submodule is not shallow. This would likely require
644
+ # changes to the fixtures. Apparently, not even the submodules-specific
645
+ # tests check that the cloned repo contains submodules as expected.
646
+ end )
647
+ end
648
+
649
+ defp update_dep ( git_repo_opts ) do
650
+ # Flush the errors we got, move to a clean slate
651
+ Mix . shell ( ) . flush ( )
652
+ Mix.Task . clear ( )
653
+ Process . put ( :git_repo_opts , git_repo_opts )
654
+ Mix.Project . pop ( )
655
+ Mix.Project . push ( GitApp )
656
+ end
657
+
658
+ defp assert_shallow ( repo_path , depth ) do
659
+ assert File . read! ( "mix.lock" ) =~ "depth: #{ depth } "
660
+
661
+ # Check if the repository is a shallow clone
662
+ assert File . exists? ( repo_path <> "/.git/shallow" )
663
+
664
+ # Check the number of commits in the current branch.
665
+ #
666
+ # We could consider all branches with `git rev-list --count --all`, as in
667
+ # practice there should be only a single branch. However, the test fixture
668
+ # sets up two branches, and that brings us to an interesting situation:
669
+ # instead of guaranteeing that the `:depth` option would keep the
670
+ # repository lean even after refspec changes, we only guarantee the number
671
+ # of commits in the current branch, perhaps leaving more objects around
672
+ # than strictly necessary. This allows us to keep the implementation
673
+ # simple, while still providing a reasonable guarantee.
674
+ assert System . cmd ( "git" , ~w[ --git-dir=#{ repo_path } /.git rev-list --count HEAD] ) ==
675
+ { "#{ depth } \n " , 0 }
676
+ end
677
+ end
678
+
481
679
defp refresh ( post_config ) do
482
680
% { name: name , file: file } = Mix.Project . pop ( )
483
681
Mix.ProjectStack . post_config ( post_config )
0 commit comments