@@ -39,13 +39,23 @@ static const char * const builtin_clone_usage[] = {
39
39
40
40
static int option_no_checkout , option_bare , option_mirror ;
41
41
static 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 ;
43
43
static char * option_origin = NULL ;
44
44
static char * option_branch = NULL ;
45
45
static const char * real_git_dir ;
46
46
static char * option_upload_pack = "git-upload-pack" ;
47
47
static int option_verbosity ;
48
48
static 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
+ }
49
59
50
60
static struct option builtin_clone_options [] = {
51
61
OPT__VERBOSITY (& option_verbosity ),
@@ -71,8 +81,8 @@ static struct option builtin_clone_options[] = {
71
81
"initialize submodules in the clone" ),
72
82
OPT_STRING (0 , "template" , & option_template , "template-directory" ,
73
83
"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 ),
76
86
OPT_STRING ('o' , "origin" , & option_origin , "branch" ,
77
87
"use <branch> instead of 'origin' to track upstream" ),
78
88
OPT_STRING ('b' , "branch" , & option_branch , "branch" ,
@@ -214,39 +224,80 @@ static void strip_trailing_slashes(char *dir)
214
224
* end = '\0' ;
215
225
}
216
226
217
- static void setup_reference ( const char * repo )
227
+ static int add_one_reference ( struct string_list_item * item , void * cb_data )
218
228
{
219
- const char * ref_git ;
220
- char * ref_git_copy ;
221
-
229
+ char * ref_git ;
230
+ struct strbuf alternate = STRBUF_INIT ;
222
231
struct remote * remote ;
223
232
struct transport * transport ;
224
233
const struct ref * extra ;
225
234
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 )))
231
242
die (_ ("reference repository '%s' is not a local directory." ),
232
- option_reference );
233
-
234
- ref_git_copy = xstrdup (ref_git );
243
+ item -> string );
235
244
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 );
237
248
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 );
240
251
for (extra = transport_get_remote_refs (transport ); extra ;
241
252
extra = extra -> next )
242
253
add_extra_ref (extra -> name , extra -> old_sha1 , 0 );
243
254
244
255
transport_disconnect (transport );
256
+ free (ref_git );
257
+ return 0 ;
258
+ }
245
259
246
- free (ref_git_copy );
260
+ static void setup_reference (void )
261
+ {
262
+ for_each_string_list (& option_reference , add_one_reference , NULL );
247
263
}
248
264
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 )
250
301
{
251
302
struct dirent * de ;
252
303
struct stat buf ;
@@ -282,7 +333,14 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
282
333
}
283
334
if (S_ISDIR (buf .st_mode )) {
284
335
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 );
286
344
continue ;
287
345
}
288
346
@@ -305,17 +363,20 @@ static const struct ref *clone_local(const char *src_repo,
305
363
const char * dest_repo )
306
364
{
307
365
const struct ref * ret ;
308
- struct strbuf src = STRBUF_INIT ;
309
- struct strbuf dest = STRBUF_INIT ;
310
366
struct remote * remote ;
311
367
struct transport * transport ;
312
368
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 ;
316
377
strbuf_addf (& src , "%s/objects" , src_repo );
317
378
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 );
319
380
strbuf_release (& src );
320
381
strbuf_release (& dest );
321
382
}
@@ -538,8 +599,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
538
599
git_config_set (key .buf , repo );
539
600
strbuf_reset (& key );
540
601
541
- if (option_reference )
542
- setup_reference (git_dir );
602
+ if (option_reference . nr )
603
+ setup_reference ();
543
604
544
605
fetch_pattern = value .buf ;
545
606
refspec = parse_fetch_refspec (1 , & fetch_pattern );
0 commit comments