clone.c 15.3 KB
Newer Older
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
3 4 5 6 7
 *
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
 */

8 9
#include "clone.h"

10 11
#include "git2/clone.h"
#include "git2/remote.h"
12
#include "git2/revparse.h"
13 14
#include "git2/branch.h"
#include "git2/config.h"
15
#include "git2/checkout.h"
16 17
#include "git2/commit.h"
#include "git2/tree.h"
18 19

#include "remote.h"
20
#include "futils.h"
21
#include "refs.h"
22
#include "path.h"
23
#include "repository.h"
24
#include "odb.h"
25

26
static int clone_local_into(git_repository *repo, git_remote *remote, const git_fetch_options *fetch_opts, const git_checkout_options *co_opts, const char *branch, int link);
27

28
static int create_branch(
29 30 31
	git_reference **branch,
	git_repository *repo,
	const git_oid *target,
32 33
	const char *name,
	const char *log_message)
34
{
Vicent Marti committed
35
	git_commit *head_obj = NULL;
36
	git_reference *branch_ref = NULL;
37
	git_buf refname = GIT_BUF_INIT;
38
	int error;
Ben Straub committed
39 40

	/* Find the target commit */
Vicent Marti committed
41
	if ((error = git_commit_lookup(&head_obj, repo, target)) < 0)
42
		return error;
Ben Straub committed
43 44

	/* Create the new branch */
45 46
	if ((error = git_buf_printf(&refname, GIT_REFS_HEADS_DIR "%s", name)) < 0)
		return error;
Ben Straub committed
47

48
	error = git_reference_create(&branch_ref, repo, git_buf_cstr(&refname), target, 0, log_message);
49
	git_buf_dispose(&refname);
Vicent Marti committed
50
	git_commit_free(head_obj);
51

52
	if (!error)
53 54 55 56
		*branch = branch_ref;
	else
		git_reference_free(branch_ref);

57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
	return error;
}

static int setup_tracking_config(
	git_repository *repo,
	const char *branch_name,
	const char *remote_name,
	const char *merge_target)
{
	git_config *cfg;
	git_buf remote_key = GIT_BUF_INIT, merge_key = GIT_BUF_INIT;
	int error = -1;

	if (git_repository_config__weakptr(&cfg, repo) < 0)
		return -1;

	if (git_buf_printf(&remote_key, "branch.%s.remote", branch_name) < 0)
		goto cleanup;

	if (git_buf_printf(&merge_key, "branch.%s.merge", branch_name) < 0)
		goto cleanup;

	if (git_config_set_string(cfg, git_buf_cstr(&remote_key), remote_name) < 0)
		goto cleanup;

	if (git_config_set_string(cfg, git_buf_cstr(&merge_key), merge_target) < 0)
		goto cleanup;

	error = 0;

cleanup:
88 89
	git_buf_dispose(&remote_key);
	git_buf_dispose(&merge_key);
90 91 92 93 94 95 96
	return error;
}

static int create_tracking_branch(
	git_reference **branch,
	git_repository *repo,
	const git_oid *target,
97 98
	const char *branch_name,
	const char *log_message)
99 100 101
{
	int error;

102
	if ((error = create_branch(branch, repo, target, branch_name, log_message)) < 0)
103 104 105 106 107 108 109
		return error;

	return setup_tracking_config(
		repo,
		branch_name,
		GIT_REMOTE_ORIGIN,
		git_reference_name(*branch));
110 111
}

112 113 114
static int update_head_to_new_branch(
	git_repository *repo,
	const git_oid *target,
115 116
	const char *name,
	const char *reflog_message)
117
{
Michael Schubert committed
118
	git_reference *tracking_branch = NULL;
119
	int error;
120 121 122 123

	if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR))
		name += strlen(GIT_REFS_HEADS_DIR);

124
	error = create_tracking_branch(&tracking_branch, repo, target, name,
125
			reflog_message);
Ben Straub committed
126

127 128
	if (!error)
		error = git_repository_set_head(
129
			repo, git_reference_name(tracking_branch));
130 131 132

	git_reference_free(tracking_branch);

133 134 135 136
	/* if it already existed, then the user's refspec created it for us, ignore it' */
	if (error == GIT_EEXISTS)
		error = 0;

137
	return error;
138 139
}

140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
static int update_head_to_default(git_repository *repo)
{
	git_buf initialbranch = GIT_BUF_INIT;
	const char *branch_name;
	int error = 0;

	if ((error = git_repository_initialbranch(&initialbranch, repo)) < 0)
		goto done;

	if (git__prefixcmp(initialbranch.ptr, GIT_REFS_HEADS_DIR) != 0) {
		git_error_set(GIT_ERROR_INVALID, "invalid initial branch '%s'", initialbranch.ptr);
		error = -1;
		goto done;
	}

	branch_name = initialbranch.ptr + strlen(GIT_REFS_HEADS_DIR);

	error = setup_tracking_config(repo, branch_name, GIT_REMOTE_ORIGIN,
		initialbranch.ptr);

done:
	git_buf_dispose(&initialbranch);
	return error;
}

165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
static int update_remote_head(
	git_repository *repo,
	git_remote *remote,
	git_buf *target,
	const char *reflog_message)
{
	git_refspec *refspec;
	git_reference *remote_head = NULL;
	git_buf remote_head_name = GIT_BUF_INIT;
	git_buf remote_branch_name = GIT_BUF_INIT;
	int error;

	/* Determine the remote tracking ref name from the local branch */
	refspec = git_remote__matching_refspec(remote, git_buf_cstr(target));

	if (refspec == NULL) {
		git_error_set(GIT_ERROR_NET, "the remote's default branch does not fit the refspec configuration");
		error = GIT_EINVALIDSPEC;
		goto cleanup;
	}

	if ((error = git_refspec_transform(
		&remote_branch_name,
		refspec,
		git_buf_cstr(target))) < 0)
		goto cleanup;

	if ((error = git_buf_printf(&remote_head_name,
		"%s%s/%s",
		GIT_REFS_REMOTES_DIR,
		git_remote_name(remote),
		GIT_HEAD_FILE)) < 0)
		goto cleanup;

	error = git_reference_symbolic_create(
		&remote_head,
		repo,
		git_buf_cstr(&remote_head_name),
		git_buf_cstr(&remote_branch_name),
		true,
		reflog_message);

cleanup:
	git_reference_free(remote_head);
	git_buf_dispose(&remote_branch_name);
	git_buf_dispose(&remote_head_name);
	return error;
}

214 215 216 217
static int update_head_to_remote(
		git_repository *repo,
		git_remote *remote,
		const char *reflog_message)
218
{
219
	int error = 0;
220 221
	size_t refs_len;
	const git_remote_head *remote_head, **refs;
222
	const git_oid *remote_head_id;
223
	git_buf branch = GIT_BUF_INIT;
Ben Straub committed
224

225 226
	if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0)
		return error;
227

228 229
	/* We cloned an empty repository or one with an unborn HEAD */
	if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE))
230
		return update_head_to_default(repo);
231

232
	/* We know we have HEAD, let's see where it points */
233
	remote_head = refs[0];
234 235
	assert(remote_head);

236
	remote_head_id = &remote_head->oid;
237 238 239 240

	error = git_remote_default_branch(&branch, remote);
	if (error == GIT_ENOTFOUND) {
		error = git_repository_set_head_detached(
241
			repo, remote_head_id);
242 243 244
		goto cleanup;
	}

245
	if ((error = update_remote_head(repo, remote, &branch, reflog_message)) < 0)
246
		goto cleanup;
247

248 249 250 251
	error = update_head_to_new_branch(
		repo,
		remote_head_id,
		git_buf_cstr(&branch),
252
		reflog_message);
Ben Straub committed
253

254
cleanup:
255
	git_buf_dispose(&branch);
256

257
	return error;
258 259
}

260 261
static int update_head_to_branch(
		git_repository *repo,
262
		const char *remote_name,
263 264
		const char *branch,
		const char *reflog_message)
265 266 267 268
{
	int retcode;
	git_buf remote_branch_name = GIT_BUF_INIT;
	git_reference* remote_ref = NULL;
nulltoken committed
269

270
	assert(remote_name && branch);
271 272

	if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
273
		remote_name, branch)) < 0 )
274 275 276 277 278
		goto cleanup;

	if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0)
		goto cleanup;

279
	retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch,
280
			reflog_message);
281 282 283

cleanup:
	git_reference_free(remote_ref);
284
	git_buf_dispose(&remote_branch_name);
285 286 287
	return retcode;
}

288 289 290 291 292 293 294
static int default_repository_create(git_repository **out, const char *path, int bare, void *payload)
{
	GIT_UNUSED(payload);

	return git_repository_init(out, path, bare);
}

295 296 297 298 299 300 301
static int default_remote_create(
		git_remote **out,
		git_repository *repo,
		const char *name,
		const char *url,
		void *payload)
{
302
	GIT_UNUSED(payload);
303

304
	return git_remote_create(out, repo, name, url);
305 306
}

307 308 309 310
/*
 * submodules?
 */

311 312 313 314 315 316 317
static int create_and_configure_origin(
		git_remote **out,
		git_repository *repo,
		const char *url,
		const git_clone_options *options)
{
	int error;
Ben Straub committed
318
	git_remote *origin = NULL;
319
	char buf[GIT_PATH_MAX];
320 321
	git_remote_create_cb remote_create = options->remote_cb;
	void *payload = options->remote_cb_payload;
322 323 324 325 326 327 328 329

	/* If the path exists and is a dir, the url should be the absolute path */
	if (git_path_root(url) < 0 && git_path_exists(url) && git_path_isdir(url)) {
		if (p_realpath(url, buf) == NULL)
			return -1;

		url = buf;
	}
330

331 332
	if (!remote_create) {
		remote_create = default_remote_create;
333
		payload = NULL;
334
	}
335

336
	if ((error = remote_create(&origin, repo, "origin", url, payload)) < 0)
337 338 339 340 341 342
		goto on_error;

	*out = origin;
	return 0;

on_error:
Ben Straub committed
343
	git_remote_free(origin);
344 345
	return error;
}
346

347 348 349
static bool should_checkout(
	git_repository *repo,
	bool is_bare,
350
	const git_checkout_options *opts)
351 352 353 354 355 356 357
{
	if (is_bare)
		return false;

	if (!opts)
		return false;

358
	if (opts->checkout_strategy == GIT_CHECKOUT_NONE)
359 360
		return false;

361
	return !git_repository_head_unborn(repo);
362
}
Ben Straub committed
363

364
static int checkout_branch(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const char *reflog_message)
365 366 367 368 369
{
	int error;

	if (branch)
		error = update_head_to_branch(repo, git_remote_name(remote), branch,
370
				reflog_message);
371 372
	/* Point HEAD to the same ref as the remote's head */
	else
373
		error = update_head_to_remote(repo, remote, reflog_message);
374 375 376 377 378 379 380

	if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
		error = git_checkout_head(repo, co_opts);

	return error;
}

381
static int clone_into(git_repository *repo, git_remote *_remote, const git_fetch_options *opts, const git_checkout_options *co_opts, const char *branch)
382
{
383
	int error;
384
	git_buf reflog_message = GIT_BUF_INIT;
385
	git_fetch_options fetch_opts;
386
	git_remote *remote;
387

388
	assert(repo && _remote);
389 390

	if (!git_repository_is_empty(repo)) {
391
		git_error_set(GIT_ERROR_INVALID, "the repository is not empty");
392 393 394
		return -1;
	}

395
	if ((error = git_remote_dup(&remote, _remote)) < 0)
396 397
		return error;

398 399
	memcpy(&fetch_opts, opts, sizeof(git_fetch_options));
	fetch_opts.update_fetchhead = 0;
400
	fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
401
	git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
402

403
	if ((error = git_remote_fetch(remote, NULL, &fetch_opts, git_buf_cstr(&reflog_message))) != 0)
404 405
		goto cleanup;

406
	error = checkout_branch(repo, remote, co_opts, branch, git_buf_cstr(&reflog_message));
407 408

cleanup:
409
	git_remote_free(remote);
410
	git_buf_dispose(&reflog_message);
411 412 413 414

	return error;
}

415
int git_clone__should_clone_local(const char *url_or_path, git_clone_local_t local)
416
{
417 418 419
	git_buf fromurl = GIT_BUF_INIT;
	const char *path = url_or_path;
	bool is_url, is_local;
420 421

	if (local == GIT_CLONE_NO_LOCAL)
422
		return 0;
423

424
	if ((is_url = git_path_is_local_file_url(url_or_path)) != 0) {
425 426 427 428
		if (git_path_fromurl(&fromurl, url_or_path) < 0) {
			is_local = -1;
			goto done;
		}
429

430 431
		path = fromurl.ptr;
	}
432

433 434
	is_local = (!is_url || local != GIT_CLONE_LOCAL_AUTO) &&
		git_path_isdir(path);
435

436
done:
437
	git_buf_dispose(&fromurl);
438
	return is_local;
439 440
}

441
static int git__clone(
442
	git_repository **out,
443 444
	const char *url,
	const char *local_path,
445 446
	const git_clone_options *_options,
	int use_existing)
447
{
448
	int error = 0;
Ben Straub committed
449
	git_repository *repo = NULL;
450
	git_remote *origin;
451
	git_clone_options options = GIT_CLONE_OPTIONS_INIT;
452
	uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES;
453
	git_repository_create_cb repository_cb;
454 455

	assert(out && url && local_path);
Ben Straub committed
456

457 458 459
	if (_options)
		memcpy(&options, _options, sizeof(git_clone_options));

460
	GIT_ERROR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
461

462
	/* Only clone to a new directory or an empty directory */
463
	if (git_path_exists(local_path) && !use_existing && !git_path_is_empty_dir(local_path)) {
464
		git_error_set(GIT_ERROR_INVALID,
Russell Belfer committed
465
			"'%s' exists and is not an empty directory", local_path);
466
		return GIT_EEXISTS;
Ben Straub committed
467 468
	}

469 470 471
	/* Only remove the root directory on failure if we create it */
	if (git_path_exists(local_path))
		rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
472

473 474 475 476 477 478
	if (options.repository_cb)
		repository_cb = options.repository_cb;
	else
		repository_cb = default_repository_create;

	if ((error = repository_cb(&repo, local_path, options.bare, options.repository_cb_payload)) < 0)
479
		return error;
480

481
	if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
482
		int clone_local = git_clone__should_clone_local(url, options.local);
483 484
		int link = options.local != GIT_CLONE_LOCAL_NO_LINKS;

485
		if (clone_local == 1)
486
			error = clone_local_into(
487
				repo, origin, &options.fetch_opts, &options.checkout_opts,
488
				options.checkout_branch, link);
489
		else if (clone_local == 0)
490
			error = clone_into(
491
				repo, origin, &options.fetch_opts, &options.checkout_opts,
492
				options.checkout_branch);
493 494
		else
			error = -1;
495

496 497
		git_remote_free(origin);
	}
498

499
	if (error != 0) {
500
		git_error_state last_error = {0};
501
		git_error_state_capture(&last_error, error);
502

503 504
		git_repository_free(repo);
		repo = NULL;
505

506
		(void)git_futils_rmdir_r(local_path, NULL, rmdir_flags);
507

508
		git_error_state_restore(&last_error);
509
	}
Ben Straub committed
510

511
	*out = repo;
512
	return error;
513
}
514

515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
int git_clone(
	git_repository **out,
	const char *url,
	const char *local_path,
	const git_clone_options *_options)
{
	return git__clone(out, url, local_path, _options, 0);
}

int git_clone__submodule(
	git_repository **out,
	const char *url,
	const char *local_path,
	const git_clone_options *_options)
{
	return git__clone(out, url, local_path, _options, 1);
}

533
int git_clone_options_init(git_clone_options *opts, unsigned int version)
534
{
535 536 537
	GIT_INIT_STRUCTURE_FROM_TEMPLATE(
		opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT);
	return 0;
538
}
539

540
#ifndef GIT_DEPRECATE_HARD
541 542 543 544
int git_clone_init_options(git_clone_options *opts, unsigned int version)
{
	return git_clone_options_init(opts, version);
}
545
#endif
546

547 548 549
static bool can_link(const char *src, const char *dst, int link)
{
#ifdef GIT_WIN32
550 551 552
	GIT_UNUSED(src);
	GIT_UNUSED(dst);
	GIT_UNUSED(link);
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
	return false;
#else

	struct stat st_src, st_dst;

	if (!link)
		return false;

	if (p_stat(src, &st_src) < 0)
		return false;

	if (p_stat(dst, &st_dst) < 0)
		return false;

	return st_src.st_dev == st_dst.st_dev;
#endif
}

571
static int clone_local_into(git_repository *repo, git_remote *remote, const git_fetch_options *fetch_opts, const git_checkout_options *co_opts, const char *branch, int link)
572
{
573
	int error, flags;
574 575 576 577
	git_repository *src;
	git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT;
	git_buf reflog_message = GIT_BUF_INIT;

578
	assert(repo && remote);
579 580

	if (!git_repository_is_empty(repo)) {
581
		git_error_set(GIT_ERROR_INVALID, "the repository is not empty");
582 583 584 585 586 587 588 589
		return -1;
	}

	/*
	 * Let's figure out what path we should use for the source
	 * repo, if it's not rooted, the path should be relative to
	 * the repository's worktree/gitdir.
	 */
590 591
	if ((error = git_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0)
		return error;
592 593 594

	/* Copy .git/objects/ from the source to the target */
	if ((error = git_repository_open(&src, git_buf_cstr(&src_path))) < 0) {
595
		git_buf_dispose(&src_path);
596 597 598
		return error;
	}

599 600
	if (git_repository_item_path(&src_odb, src, GIT_REPOSITORY_ITEM_OBJECTS) < 0
		|| git_repository_item_path(&dst_odb, repo, GIT_REPOSITORY_ITEM_OBJECTS) < 0) {
601 602 603 604
		error = -1;
		goto cleanup;
	}

605 606 607 608
	flags = 0;
	if (can_link(git_repository_path(src), git_repository_path(repo), link))
		flags |= GIT_CPDIR_LINK_FILES;

609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
	error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
				flags, GIT_OBJECT_DIR_MODE);

	/*
	 * can_link() doesn't catch all variations, so if we hit an
	 * error and did want to link, let's try again without trying
	 * to link.
	 */
	if (error < 0 && link) {
		flags &= ~GIT_CPDIR_LINK_FILES;
		error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb),
					flags, GIT_OBJECT_DIR_MODE);
	}

	if (error < 0)
624 625 626 627
		goto cleanup;

	git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote));

628
	if ((error = git_remote_fetch(remote, NULL, fetch_opts, git_buf_cstr(&reflog_message))) != 0)
629 630
		goto cleanup;

631
	error = checkout_branch(repo, remote, co_opts, branch, git_buf_cstr(&reflog_message));
632 633

cleanup:
634 635 636 637
	git_buf_dispose(&reflog_message);
	git_buf_dispose(&src_path);
	git_buf_dispose(&src_odb);
	git_buf_dispose(&dst_odb);
638 639 640
	git_repository_free(src);
	return error;
}