@@ -25,6 +25,7 @@ defmodule Mix.SCM.Git do
2525 end
2626
2727 def accepts_options ( _app , opts ) do
28+ opts = sparse_opts ( opts )
2829 cond do
2930 gh = opts [ :github ] ->
3031 opts
@@ -41,7 +42,9 @@ defmodule Mix.SCM.Git do
4142
4243 def checked_out? ( opts ) do
4344 # Are we inside a Git repository?
44- File . regular? ( Path . join ( opts [ :dest ] , ".git/HEAD" ) )
45+ git_dest ( opts )
46+ |> Path . join ( ".git/HEAD" )
47+ |> File . regular?
4548 end
4649
4750 def lock_status ( opts ) do
@@ -50,7 +53,7 @@ defmodule Mix.SCM.Git do
5053
5154 cond do
5255 lock_rev = get_lock_rev ( lock , opts ) ->
53- File . cd! ( opts [ :dest ] , fn ->
56+ File . cd! ( git_dest ( opts ) , fn ->
5457 % { origin: origin , rev: rev } = get_rev_info ( )
5558 if get_lock_repo ( lock ) == origin and lock_rev == rev do
5659 :ok
@@ -77,19 +80,30 @@ defmodule Mix.SCM.Git do
7780 def checkout ( opts ) do
7881 assert_git! ( )
7982
80- path = opts [ :dest ]
83+ path = git_dest ( opts )
8184 location = opts [ :git ]
8285
8386 _ = File . rm_rf! ( path )
84- git! ( ~s( clone --no-checkout --progress "#{ location } " "#{ path } ") )
8587
86- File . cd! path , fn -> do_checkout ( opts ) end
88+ fun =
89+ if opts [ :sparse ] do
90+ sparse_check ( git_version ( ) )
91+ File . mkdir_p! ( path )
92+ fn -> init_sparse ( opts ) end
93+ else
94+ git! ( ~s( clone --no-checkout --progress "#{ location } " "#{ path } ") )
95+ fn -> do_checkout ( opts ) end
96+ end
97+
98+ File . cd! path , fun
8799 end
88100
89101 def update ( opts ) do
90102 assert_git! ( )
91103
92- File . cd! opts [ :dest ] , fn ->
104+ File . cd! git_dest ( opts ) , fn ->
105+ sparse_toggle ( opts )
106+
93107 location = opts [ :git ]
94108 update_origin ( location )
95109
@@ -102,24 +116,81 @@ defmodule Mix.SCM.Git do
102116 end
103117 end
104118
119+ defp sparse_opts ( opts ) do
120+ if opts [ :sparse ] do
121+ dest = Path . join ( opts [ :dest ] , opts [ :sparse ] )
122+ opts
123+ |> Keyword . put ( :git_dest , opts [ :dest ] )
124+ |> Keyword . put ( :dest , dest )
125+ else
126+ opts
127+ end
128+ end
129+
130+ defp sparse_check ( version ) do
131+ unless { 1 , 7 , 0 } <= version do
132+ version =
133+ version
134+ |> Tuple . to_list
135+ |> Enum . join ( "." )
136+ Mix . raise "Git >= 1.7.0 is required to use sparse checkout. " <>
137+ "You are running version #{ version } "
138+ end
139+ end
140+
141+ defp sparse_toggle ( opts ) do
142+ git! ( "config core.sparsecheckout #{ opts [ :sparse ] != nil } " )
143+ end
144+
145+
105146 defp progress_switch ( version ) when { 1 , 7 , 1 } <= version , do: " --progress"
106147 defp progress_switch ( _ ) , do: ""
107148
108149 defp tags_switch ( nil ) , do: ""
109150 defp tags_switch ( _ ) , do: " --tags"
110151
152+ defp git_dest ( opts ) do
153+ if opts [ :git_dest ] do
154+ opts [ :git_dest ]
155+ else
156+ opts [ :dest ]
157+ end
158+ end
159+
111160 ## Helpers
112161
113162 defp validate_git_options ( opts ) do
114- case Keyword . take ( opts , [ :branch , :ref , :tag ] ) do
115- [ ] -> opts
163+ err = "You should specify only one of branch, ref or tag, and only once. " <>
164+ "Error on Git dependency: #{ opts [ :git ] } "
165+ validate_single_uniq ( opts , [ :branch , :ref , :tag ] , err )
166+
167+ err = "You should specify only one sparse path. " <>
168+ "Error on Git dependency: #{ opts [ :git ] } "
169+ validate_single_uniq ( opts , [ :sparse ] , err )
170+ end
171+
172+ defp validate_single_uniq ( opts , take , error ) do
173+ case Keyword . take ( opts , take ) do
174+ [ ] -> opts
116175 [ _ ] -> opts
117- _ ->
118- Mix . raise "You should specify only one of branch, ref or tag, and only once. " <>
119- "Error on Git dependency: #{ opts [ :git ] } "
176+ _ -> Mix . raise error
120177 end
121178 end
122179
180+ defp init_sparse ( opts ) do
181+ git! ( "init --quiet" )
182+ git! ( "remote add origin #{ opts [ :git ] } --fetch" )
183+ sparse_toggle ( opts )
184+
185+ sparse_info =
186+ File . cwd!
187+ |> Path . join ( ".git/info/sparse-checkout" )
188+
189+ File . write ( sparse_info , opts [ :sparse ] )
190+
191+ do_checkout ( opts )
192+ end
193+
123194 defp do_checkout ( opts ) do
124195 rev = get_lock_rev ( opts [ :lock ] , opts ) || get_opts_rev ( opts )
125196 git! ( "--git-dir=.git checkout --quiet #{ rev } " )
@@ -147,6 +218,13 @@ defmodule Mix.SCM.Git do
147218
148219 defp get_lock_opts ( opts ) do
149220 lock_opts = Keyword . take ( opts , [ :branch , :ref , :tag ] )
221+ lock_opts =
222+ if opts [ :sparse ] do
223+ lock_opts ++ [ sparse: opts [ :sparse ] ]
224+ else
225+ lock_opts
226+ end
227+
150228 if opts [ :submodules ] do
151229 lock_opts ++ [ submodules: true ]
152230 else
0 commit comments