@@ -478,6 +478,207 @@ 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
+ # For compatibility and simplicity, we follow Git's behavior and do not
572
+ # attempt to unshallow an existing repository. This should not be a
573
+ # problem, because all we guarantee is that the correct source code is
574
+ # available whenever mix.exs or mix.lock change. If one wanted to have a
575
+ # full clone, they can always run `deps.clean` and `deps.get` again.
576
+ Process . put ( :git_repo_opts , depth: 1 )
577
+
578
+ in_fixture ( "no_mixfile" , fn ->
579
+ Mix.Project . push ( GitApp )
580
+
581
+ Mix.Tasks.Deps.Get . run ( [ ] )
582
+ message = "* Getting git_repo (#{ fixture_path ( "git_repo" ) } )"
583
+ assert_received { :mix_shell , :info , [ ^ message ] }
584
+ assert_shallow ( "deps/git_repo" , 1 )
585
+
586
+ # Remove depth
587
+ update_dep ( [ ] )
588
+ Mix.Tasks.Deps.Get . run ( [ ] )
589
+ refute File . read! ( "mix.lock" ) =~ "depth:"
590
+ assert File . exists? ( "deps/git_repo/.git/shallow" )
591
+
592
+ assert System . cmd ( "git" , ~w[ --git-dir=deps/git_repo/.git rev-list --count HEAD] ) ==
593
+ { "1\n " , 0 }
594
+ end )
595
+ end
596
+
597
+ @ tag :git_sparse
598
+ test "with sparse checkout" do
599
+ Process . put ( :git_repo_opts , sparse: "sparse_dir" , depth: 1 )
600
+
601
+ in_fixture ( "no_mixfile" , fn ->
602
+ Mix.Project . push ( GitApp )
603
+
604
+ Mix.Tasks.Deps.Get . run ( [ ] )
605
+ message = "* Getting git_repo (#{ fixture_path ( "git_repo" ) } )"
606
+ assert_received { :mix_shell , :info , [ ^ message ] }
607
+ assert_shallow ( "deps/git_repo" , 1 )
608
+
609
+ refute File . exists? ( "deps/git_repo/mix.exs" )
610
+ assert File . exists? ( "deps/git_repo/sparse_dir/mix.exs" )
611
+ assert File . read! ( "mix.lock" ) =~ "sparse: \" sparse_dir\" "
612
+ end )
613
+ end
614
+
615
+ test "with subdir" do
616
+ Process . put ( :git_repo_opts , subdir: "sparse_dir" , depth: 1 )
617
+
618
+ in_fixture ( "no_mixfile" , fn ->
619
+ Mix.Project . push ( GitApp )
620
+
621
+ Mix.Tasks.Deps.Get . run ( [ ] )
622
+ message = "* Getting git_repo (#{ fixture_path ( "git_repo" ) } )"
623
+ assert_received { :mix_shell , :info , [ ^ message ] }
624
+ assert_shallow ( "deps/git_repo" , 1 )
625
+
626
+ assert File . exists? ( "deps/git_repo/mix.exs" )
627
+ assert File . exists? ( "deps/git_repo/sparse_dir/mix.exs" )
628
+ assert File . read! ( "mix.lock" ) =~ "subdir: \" sparse_dir\" "
629
+ end )
630
+ end
631
+
632
+ test "does not affect submodules depth" do
633
+ # The expectation is that we can add an explicit option in the future,
634
+ # just like git-clone has `--shallow-submodules`.
635
+ Process . put ( :git_repo_opts , submodules: true , depth: 1 )
636
+
637
+ in_fixture ( "no_mixfile" , fn ->
638
+ Mix.Project . push ( GitApp )
639
+
640
+ Mix.Tasks.Deps.Get . run ( [ ] )
641
+ message = "* Getting git_repo (#{ fixture_path ( "git_repo" ) } )"
642
+ assert_received { :mix_shell , :info , [ ^ message ] }
643
+ assert_shallow ( "deps/git_repo" , 1 )
644
+
645
+ assert File . read! ( "mix.lock" ) =~ "submodules: true"
646
+ # TODO: assert submodule is not shallow. This would likely require
647
+ # changes to the fixtures. Apparently, not even the submodules-specific
648
+ # tests check that the cloned repo contains submodules as expected.
649
+ end )
650
+ end
651
+
652
+ defp update_dep ( git_repo_opts ) do
653
+ # Flush the errors we got, move to a clean slate
654
+ Mix . shell ( ) . flush ( )
655
+ Mix.Task . clear ( )
656
+ Process . put ( :git_repo_opts , git_repo_opts )
657
+ Mix.Project . pop ( )
658
+ Mix.Project . push ( GitApp )
659
+ end
660
+
661
+ defp assert_shallow ( repo_path , depth ) do
662
+ assert File . read! ( "mix.lock" ) =~ "depth: #{ depth } "
663
+
664
+ # Check if the repository is a shallow clone
665
+ assert File . exists? ( repo_path <> "/.git/shallow" )
666
+
667
+ # Check the number of commits in the current branch.
668
+ #
669
+ # We could consider all branches with `git rev-list --count --all`, as in
670
+ # practice there should be only a single branch. However, the test fixture
671
+ # sets up two branches, and that brings us to an interesting situation:
672
+ # instead of guaranteeing that the `:depth` option would keep the
673
+ # repository lean even after refspec changes, we only guarantee the number
674
+ # of commits in the current branch, perhaps leaving more objects around
675
+ # than strictly necessary. This allows us to keep the implementation
676
+ # simple, while still providing a reasonable guarantee.
677
+ assert System . cmd ( "git" , ~w[ --git-dir=#{ repo_path } /.git rev-list --count HEAD] ) ==
678
+ { "#{ depth } \n " , 0 }
679
+ end
680
+ end
681
+
481
682
defp refresh ( post_config ) do
482
683
% { name: name , file: file } = Mix.Project . pop ( )
483
684
Mix.ProjectStack . post_config ( post_config )
0 commit comments