@@ -39,13 +39,23 @@ static const char * const builtin_clone_usage[] = {
3939
4040static int option_no_checkout , option_bare , option_mirror ;
4141static int option_local , option_no_hardlinks , option_shared , option_recursive ;
42- static char * option_template , * option_reference , * option_depth ;
42+ static char * option_template , * option_depth ;
4343static char * option_origin = NULL ;
4444static char * option_branch = NULL ;
4545static const char * real_git_dir ;
4646static char * option_upload_pack = "git-upload-pack" ;
4747static int option_verbosity ;
4848static int option_progress ;
49+ static struct string_list option_reference ;
50+
51+ static int opt_parse_reference (const struct option * opt , const char * arg , int unset )
52+ {
53+ struct string_list * option_reference = opt -> value ;
54+ if (!arg )
55+ return -1 ;
56+ string_list_append (option_reference , arg );
57+ return 0 ;
58+ }
4959
5060static struct option builtin_clone_options [] = {
5161 OPT__VERBOSITY (& option_verbosity ),
@@ -71,8 +81,8 @@ static struct option builtin_clone_options[] = {
7181 "initialize submodules in the clone" ),
7282 OPT_STRING (0 , "template" , & option_template , "template-directory" ,
7383 "directory from which templates will be used" ),
74- OPT_STRING ( 0 , "reference" , & option_reference , "repo" ,
75- "reference repository" ),
84+ OPT_CALLBACK ( 0 , "reference" , & option_reference , "repo" ,
85+ "reference repository" , & opt_parse_reference ),
7686 OPT_STRING ('o' , "origin" , & option_origin , "branch" ,
7787 "use <branch> instead of 'origin' to track upstream" ),
7888 OPT_STRING ('b' , "branch" , & option_branch , "branch" ,
@@ -214,39 +224,80 @@ static void strip_trailing_slashes(char *dir)
214224 * end = '\0' ;
215225}
216226
217- static void setup_reference ( const char * repo )
227+ static int add_one_reference ( struct string_list_item * item , void * cb_data )
218228{
219- const char * ref_git ;
220- char * ref_git_copy ;
221-
229+ char * ref_git ;
230+ struct strbuf alternate = STRBUF_INIT ;
222231 struct remote * remote ;
223232 struct transport * transport ;
224233 const struct ref * extra ;
225234
226- ref_git = real_path (option_reference );
227-
228- if (is_directory (mkpath ("%s/.git/objects" , ref_git )))
229- ref_git = mkpath ("%s/.git" , ref_git );
230- else if (!is_directory (mkpath ("%s/objects" , ref_git )))
235+ /* Beware: real_path() and mkpath() return static buffer */
236+ ref_git = xstrdup (real_path (item -> string ));
237+ if (is_directory (mkpath ("%s/.git/objects" , ref_git ))) {
238+ char * ref_git_git = xstrdup (mkpath ("%s/.git" , ref_git ));
239+ free (ref_git );
240+ ref_git = ref_git_git ;
241+ } else if (!is_directory (mkpath ("%s/objects" , ref_git )))
231242 die (_ ("reference repository '%s' is not a local directory." ),
232- option_reference );
233-
234- ref_git_copy = xstrdup (ref_git );
243+ item -> string );
235244
236- add_to_alternates_file (ref_git_copy );
245+ strbuf_addf (& alternate , "%s/objects" , ref_git );
246+ add_to_alternates_file (alternate .buf );
247+ strbuf_release (& alternate );
237248
238- remote = remote_get (ref_git_copy );
239- transport = transport_get (remote , ref_git_copy );
249+ remote = remote_get (ref_git );
250+ transport = transport_get (remote , ref_git );
240251 for (extra = transport_get_remote_refs (transport ); extra ;
241252 extra = extra -> next )
242253 add_extra_ref (extra -> name , extra -> old_sha1 , 0 );
243254
244255 transport_disconnect (transport );
256+ free (ref_git );
257+ return 0 ;
258+ }
245259
246- free (ref_git_copy );
260+ static void setup_reference (void )
261+ {
262+ for_each_string_list (& option_reference , add_one_reference , NULL );
247263}
248264
249- static void copy_or_link_directory (struct strbuf * src , struct strbuf * dest )
265+ static void copy_alternates (struct strbuf * src , struct strbuf * dst ,
266+ const char * src_repo )
267+ {
268+ /*
269+ * Read from the source objects/info/alternates file
270+ * and copy the entries to corresponding file in the
271+ * destination repository with add_to_alternates_file().
272+ * Both src and dst have "$path/objects/info/alternates".
273+ *
274+ * Instead of copying bit-for-bit from the original,
275+ * we need to append to existing one so that the already
276+ * created entry via "clone -s" is not lost, and also
277+ * to turn entries with paths relative to the original
278+ * absolute, so that they can be used in the new repository.
279+ */
280+ FILE * in = fopen (src -> buf , "r" );
281+ struct strbuf line = STRBUF_INIT ;
282+
283+ while (strbuf_getline (& line , in , '\n' ) != EOF ) {
284+ char * abs_path , abs_buf [PATH_MAX ];
285+ if (!line .len || line .buf [0 ] == '#' )
286+ continue ;
287+ if (is_absolute_path (line .buf )) {
288+ add_to_alternates_file (line .buf );
289+ continue ;
290+ }
291+ abs_path = mkpath ("%s/objects/%s" , src_repo , line .buf );
292+ normalize_path_copy (abs_buf , abs_path );
293+ add_to_alternates_file (abs_buf );
294+ }
295+ strbuf_release (& line );
296+ fclose (in );
297+ }
298+
299+ static void copy_or_link_directory (struct strbuf * src , struct strbuf * dest ,
300+ const char * src_repo , int src_baselen )
250301{
251302 struct dirent * de ;
252303 struct stat buf ;
@@ -282,7 +333,14 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
282333 }
283334 if (S_ISDIR (buf .st_mode )) {
284335 if (de -> d_name [0 ] != '.' )
285- copy_or_link_directory (src , dest );
336+ copy_or_link_directory (src , dest ,
337+ src_repo , src_baselen );
338+ continue ;
339+ }
340+
341+ /* Files that cannot be copied bit-for-bit... */
342+ if (!strcmp (src -> buf + src_baselen , "/info/alternates" )) {
343+ copy_alternates (src , dest , src_repo );
286344 continue ;
287345 }
288346
@@ -305,17 +363,20 @@ static const struct ref *clone_local(const char *src_repo,
305363 const char * dest_repo )
306364{
307365 const struct ref * ret ;
308- struct strbuf src = STRBUF_INIT ;
309- struct strbuf dest = STRBUF_INIT ;
310366 struct remote * remote ;
311367 struct transport * transport ;
312368
313- if (option_shared )
314- add_to_alternates_file (src_repo );
315- else {
369+ if (option_shared ) {
370+ struct strbuf alt = STRBUF_INIT ;
371+ strbuf_addf (& alt , "%s/objects" , src_repo );
372+ add_to_alternates_file (alt .buf );
373+ strbuf_release (& alt );
374+ } else {
375+ struct strbuf src = STRBUF_INIT ;
376+ struct strbuf dest = STRBUF_INIT ;
316377 strbuf_addf (& src , "%s/objects" , src_repo );
317378 strbuf_addf (& dest , "%s/objects" , dest_repo );
318- copy_or_link_directory (& src , & dest );
379+ copy_or_link_directory (& src , & dest , src_repo , src . len );
319380 strbuf_release (& src );
320381 strbuf_release (& dest );
321382 }
@@ -538,8 +599,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
538599 git_config_set (key .buf , repo );
539600 strbuf_reset (& key );
540601
541- if (option_reference )
542- setup_reference (git_dir );
602+ if (option_reference . nr )
603+ setup_reference ();
543604
544605 fetch_pattern = value .buf ;
545606 refspec = parse_fetch_refspec (1 , & fetch_pattern );
0 commit comments