Skip to content

Commit 84b0514

Browse files
committed
Merge branch 'jc/maint-clone-alternates' into maint
* jc/maint-clone-alternates: clone: clone from a repository with relative alternates clone: allow more than one --reference
2 parents 406c1c4 + e6baf4a commit 84b0514

File tree

3 files changed

+114
-30
lines changed

3 files changed

+114
-30
lines changed

builtin/clone.c

Lines changed: 90 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,23 @@ static const char * const builtin_clone_usage[] = {
3939

4040
static int option_no_checkout, option_bare, option_mirror;
4141
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;
4343
static char *option_origin = NULL;
4444
static char *option_branch = NULL;
4545
static const char *real_git_dir;
4646
static char *option_upload_pack = "git-upload-pack";
4747
static int option_verbosity;
4848
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+
}
4959

5060
static 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);

sha1_file.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ void add_to_alternates_file(const char *reference)
380380
{
381381
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
382382
int fd = hold_lock_file_for_append(lock, git_path("objects/info/alternates"), LOCK_DIE_ON_ERROR);
383-
char *alt = mkpath("%s/objects\n", reference);
383+
char *alt = mkpath("%s\n", reference);
384384
write_or_die(fd, alt, strlen(alt));
385385
if (commit_lock_file(lock))
386386
die("could not close alternates file");

t/t5601-clone.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,27 @@ test_expect_success 'clone separate gitdir where target already exists' '
211211
test_must_fail git clone --separate-git-dir realgitdir src dst
212212
'
213213

214+
test_expect_success 'clone --reference from original' '
215+
git clone --shared --bare src src-1 &&
216+
git clone --bare src src-2 &&
217+
git clone --reference=src-2 --bare src-1 target-8 &&
218+
grep /src-2/ target-8/objects/info/alternates
219+
'
220+
221+
test_expect_success 'clone with more than one --reference' '
222+
git clone --bare src src-3 &&
223+
git clone --bare src src-4 &&
224+
git clone --reference=src-3 --reference=src-4 src target-9 &&
225+
grep /src-3/ target-9/.git/objects/info/alternates &&
226+
grep /src-4/ target-9/.git/objects/info/alternates
227+
'
228+
229+
test_expect_success 'clone from original with relative alternate' '
230+
mkdir nest &&
231+
git clone --bare src nest/src-5 &&
232+
echo ../../../src/.git/objects >nest/src-5/objects/info/alternates &&
233+
git clone --bare nest/src-5 target-10 &&
234+
grep /src/\\.git/objects target-10/objects/info/alternates
235+
'
236+
214237
test_done

0 commit comments

Comments
 (0)