worktree.c 13.2 KB
Newer Older
1
#include "cache.h"
2
#include "repository.h"
3 4 5
#include "refs.h"
#include "strbuf.h"
#include "worktree.h"
6
#include "dir.h"
7
#include "wt-status.h"
8

9 10 11 12 13 14
void free_worktrees(struct worktree **worktrees)
{
	int i = 0;

	for (i = 0; worktrees[i]; i++) {
		free(worktrees[i]->path);
15
		free(worktrees[i]->id);
16
		free(worktrees[i]->head_ref);
17
		free(worktrees[i]->lock_reason);
18 19 20 21 22
		free(worktrees[i]);
	}
	free (worktrees);
}

23
/**
24
 * Update head_sha1, head_ref and is_detached of the given worktree
25
 */
26
static void add_head_info(struct worktree *wt)
27
{
28 29 30 31 32
	int flags;
	const char *target;

	target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
					 "HEAD",
33
					 0,
34
					 &wt->head_oid, &flags);
35 36 37 38 39 40 41
	if (!target)
		return;

	if (flags & REF_ISSYMREF)
		wt->head_ref = xstrdup(target);
	else
		wt->is_detached = 1;
42 43
}

44 45 46 47
/**
 * get the main worktree
 */
static struct worktree *get_main_worktree(void)
48
{
49
	struct worktree *worktree = NULL;
50
	struct strbuf path = STRBUF_INIT;
51
	struct strbuf worktree_path = STRBUF_INIT;
52
	int is_bare = 0;
53

54
	strbuf_add_absolute_path(&worktree_path, get_git_common_dir());
55 56
	is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
	if (is_bare)
57 58 59 60
		strbuf_strip_suffix(&worktree_path, "/.");

	strbuf_addf(&path, "%s/HEAD", get_git_common_dir());

61
	worktree = xcalloc(1, sizeof(*worktree));
62 63
	worktree->path = strbuf_detach(&worktree_path, NULL);
	worktree->is_bare = is_bare;
64
	add_head_info(worktree);
65

66
	strbuf_release(&path);
67 68
	strbuf_release(&worktree_path);
	return worktree;
69 70
}

71
static struct worktree *get_linked_worktree(const char *id)
72
{
73
	struct worktree *worktree = NULL;
74
	struct strbuf path = STRBUF_INIT;
75
	struct strbuf worktree_path = STRBUF_INIT;
76

77 78
	if (!id)
		die("Missing linked worktree name");
79

80
	strbuf_git_common_path(&path, the_repository, "worktrees/%s/gitdir", id);
81 82
	if (strbuf_read_file(&worktree_path, path.buf, 0) <= 0)
		/* invalid gitdir file */
83
		goto done;
84 85 86 87

	strbuf_rtrim(&worktree_path);
	if (!strbuf_strip_suffix(&worktree_path, "/.git")) {
		strbuf_reset(&worktree_path);
88
		strbuf_add_absolute_path(&worktree_path, ".");
89 90 91
		strbuf_strip_suffix(&worktree_path, "/.");
	}

92
	strbuf_reset(&path);
93 94
	strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);

95
	worktree = xcalloc(1, sizeof(*worktree));
96
	worktree->path = strbuf_detach(&worktree_path, NULL);
97
	worktree->id = xstrdup(id);
98
	add_head_info(worktree);
99 100 101

done:
	strbuf_release(&path);
102 103
	strbuf_release(&worktree_path);
	return worktree;
104 105
}

106 107
static void mark_current_worktree(struct worktree **worktrees)
{
René Scharfe's avatar
René Scharfe committed
108
	char *git_dir = absolute_pathdup(get_git_dir());
109 110 111 112
	int i;

	for (i = 0; worktrees[i]; i++) {
		struct worktree *wt = worktrees[i];
113 114 115 116
		const char *wt_git_dir = get_worktree_git_dir(wt);

		if (!fspathcmp(git_dir, absolute_path(wt_git_dir))) {
			wt->is_current = 1;
117
			break;
118
		}
119
	}
120
	free(git_dir);
121 122
}

123 124 125 126 127 128 129
static int compare_worktree(const void *a_, const void *b_)
{
	const struct worktree *const *a = a_;
	const struct worktree *const *b = b_;
	return fspathcmp((*a)->path, (*b)->path);
}

130
struct worktree **get_worktrees(unsigned flags)
131
{
132
	struct worktree **list = NULL;
133 134 135
	struct strbuf path = STRBUF_INIT;
	DIR *dir;
	struct dirent *d;
136 137
	int counter = 0, alloc = 2;

René Scharfe's avatar
René Scharfe committed
138
	ALLOC_ARRAY(list, alloc);
139

140
	list[counter++] = get_main_worktree();
141 142 143 144

	strbuf_addf(&path, "%s/worktrees", get_git_common_dir());
	dir = opendir(path.buf);
	strbuf_release(&path);
145 146 147
	if (dir) {
		while ((d = readdir(dir)) != NULL) {
			struct worktree *linked = NULL;
148
			if (is_dot_or_dotdot(d->d_name))
149
				continue;
150

Duy Nguyen's avatar
Duy Nguyen committed
151 152 153 154
			if ((linked = get_linked_worktree(d->d_name))) {
				ALLOC_GROW(list, counter + 1, alloc);
				list[counter++] = linked;
			}
155 156 157 158 159
		}
		closedir(dir);
	}
	ALLOC_GROW(list, counter + 1, alloc);
	list[counter] = NULL;
160

161 162 163 164 165 166 167
	if (flags & GWT_SORT_LINKED)
		/*
		 * don't sort the first item (main worktree), which will
		 * always be the first
		 */
		QSORT(list + 1, counter - 1, compare_worktree);

168
	mark_current_worktree(list);
169 170 171
	return list;
}

172 173 174 175 176 177 178 179 180 181
const char *get_worktree_git_dir(const struct worktree *wt)
{
	if (!wt)
		return get_git_dir();
	else if (!wt->id)
		return get_git_common_dir();
	else
		return git_common_path("worktrees/%s", wt->id);
}

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
static struct worktree *find_worktree_by_suffix(struct worktree **list,
						const char *suffix)
{
	struct worktree *found = NULL;
	int nr_found = 0, suffixlen;

	suffixlen = strlen(suffix);
	if (!suffixlen)
		return NULL;

	for (; *list && nr_found < 2; list++) {
		const char	*path	 = (*list)->path;
		int		 pathlen = strlen(path);
		int		 start	 = pathlen - suffixlen;

		/* suffix must start at directory boundary */
		if ((!start || (start > 0 && is_dir_sep(path[start - 1]))) &&
		    !fspathcmp(suffix, path + start)) {
			found = *list;
			nr_found++;
		}
	}
	return nr_found == 1 ? found : NULL;
}

207 208 209 210
struct worktree *find_worktree(struct worktree **list,
			       const char *prefix,
			       const char *arg)
{
211
	struct worktree *wt;
212
	char *path;
213
	char *to_free = NULL;
214

215 216 217
	if ((wt = find_worktree_by_suffix(list, arg)))
		return wt;

218 219
	if (prefix)
		arg = to_free = prefix_filename(prefix, arg);
220 221 222 223 224
	path = real_pathdup(arg, 0);
	if (!path) {
		free(to_free);
		return NULL;
	}
225 226 227 228
	for (; *list; list++)
		if (!fspathcmp(path, real_path((*list)->path)))
			break;
	free(path);
229
	free(to_free);
230 231 232
	return *list;
}

233 234 235 236 237
int is_main_worktree(const struct worktree *wt)
{
	return !wt->id;
}

238
const char *worktree_lock_reason(struct worktree *wt)
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
{
	assert(!is_main_worktree(wt));

	if (!wt->lock_reason_valid) {
		struct strbuf path = STRBUF_INIT;

		strbuf_addstr(&path, worktree_git_path(wt, "locked"));
		if (file_exists(path.buf)) {
			struct strbuf lock_reason = STRBUF_INIT;
			if (strbuf_read_file(&lock_reason, path.buf, 0) < 0)
				die_errno(_("failed to read '%s'"), path.buf);
			strbuf_trim(&lock_reason);
			wt->lock_reason = strbuf_detach(&lock_reason, NULL);
		} else
			wt->lock_reason = NULL;
		wt->lock_reason_valid = 1;
		strbuf_release(&path);
	}

	return wt->lock_reason;
}

261 262 263 264 265 266 267 268 269 270 271 272 273
/* convenient wrapper to deal with NULL strbuf */
static void strbuf_addf_gently(struct strbuf *buf, const char *fmt, ...)
{
	va_list params;

	if (!buf)
		return;

	va_start(params, fmt);
	strbuf_vaddf(buf, fmt, params);
	va_end(params);
}

274 275
int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
		      unsigned flags)
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
{
	struct strbuf wt_path = STRBUF_INIT;
	char *path = NULL;
	int err, ret = -1;

	strbuf_addf(&wt_path, "%s/.git", wt->path);

	if (is_main_worktree(wt)) {
		if (is_directory(wt_path.buf)) {
			ret = 0;
			goto done;
		}
		/*
		 * Main worktree using .git file to point to the
		 * repository would make it impossible to know where
		 * the actual worktree is if this function is executed
		 * from another worktree. No .git file support for now.
		 */
		strbuf_addf_gently(errmsg,
				   _("'%s' at main working tree is not the repository directory"),
				   wt_path.buf);
		goto done;
	}

	/*
	 * Make sure "gitdir" file points to a real .git file and that
	 * file points back here.
	 */
	if (!is_absolute_path(wt->path)) {
		strbuf_addf_gently(errmsg,
				   _("'%s' file does not contain absolute path to the working tree location"),
				   git_common_path("worktrees/%s/gitdir", wt->id));
		goto done;
	}

311 312 313 314 315 316
	if (flags & WT_VALIDATE_WORKTREE_MISSING_OK &&
	    !file_exists(wt->path)) {
		ret = 0;
		goto done;
	}

317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
	if (!file_exists(wt_path.buf)) {
		strbuf_addf_gently(errmsg, _("'%s' does not exist"), wt_path.buf);
		goto done;
	}

	path = xstrdup_or_null(read_gitfile_gently(wt_path.buf, &err));
	if (!path) {
		strbuf_addf_gently(errmsg, _("'%s' is not a .git file, error code %d"),
				   wt_path.buf, err);
		goto done;
	}

	ret = fspathcmp(path, real_path(git_common_path("worktrees/%s", wt->id)));

	if (ret)
		strbuf_addf_gently(errmsg, _("'%s' does not point back to '%s'"),
				   wt->path, git_common_path("worktrees/%s", wt->id));
done:
	free(path);
	strbuf_release(&wt_path);
	return ret;
}

340 341 342 343 344
void update_worktree_location(struct worktree *wt, const char *path_)
{
	struct strbuf path = STRBUF_INIT;

	if (is_main_worktree(wt))
345
		BUG("can't relocate main worktree");
346 347 348 349 350 351 352 353 354 355 356

	strbuf_realpath(&path, path_, 1);
	if (fspathcmp(wt->path, path.buf)) {
		write_file(git_common_path("worktrees/%s/gitdir", wt->id),
			   "%s/.git", path.buf);
		free(wt->path);
		wt->path = strbuf_detach(&path, NULL);
	}
	strbuf_release(&path);
}

357 358
int is_worktree_being_rebased(const struct worktree *wt,
			      const char *target)
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
{
	struct wt_status_state state;
	int found_rebase;

	memset(&state, 0, sizeof(state));
	found_rebase = wt_status_check_rebase(wt, &state) &&
		((state.rebase_in_progress ||
		  state.rebase_interactive_in_progress) &&
		 state.branch &&
		 starts_with(target, "refs/heads/") &&
		 !strcmp(state.branch, target + strlen("refs/heads/")));
	free(state.branch);
	free(state.onto);
	return found_rebase;
}

375 376
int is_worktree_being_bisected(const struct worktree *wt,
			       const char *target)
377
{
378 379 380 381 382 383 384 385 386 387 388 389
	struct wt_status_state state;
	int found_rebase;

	memset(&state, 0, sizeof(state));
	found_rebase = wt_status_check_bisect(wt, &state) &&
		state.branch &&
		starts_with(target, "refs/heads/") &&
		!strcmp(state.branch, target + strlen("refs/heads/"));
	free(state.branch);
	return found_rebase;
}

390 391 392 393 394 395
/*
 * note: this function should be able to detect shared symref even if
 * HEAD is temporarily detached (e.g. in the middle of rebase or
 * bisect). New commands that do similar things should update this
 * function as well.
 */
396 397
const struct worktree *find_shared_symref(const char *symref,
					  const char *target)
398
{
399 400
	const struct worktree *existing = NULL;
	static struct worktree **worktrees;
401 402
	int i = 0;

403 404
	if (worktrees)
		free_worktrees(worktrees);
405
	worktrees = get_worktrees(0);
406

407
	for (i = 0; worktrees[i]; i++) {
408
		struct worktree *wt = worktrees[i];
409 410 411 412
		const char *symref_target;
		struct ref_store *refs;
		int flags;

413 414
		if (wt->is_bare)
			continue;
415

416 417 418 419 420
		if (wt->is_detached && !strcmp(symref, "HEAD")) {
			if (is_worktree_being_rebased(wt, target)) {
				existing = wt;
				break;
			}
421 422 423 424
			if (is_worktree_being_bisected(wt, target)) {
				existing = wt;
				break;
			}
425 426
		}

427 428
		refs = get_worktree_ref_store(wt);
		symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
429
							NULL, &flags);
430 431
		if ((flags & REF_ISSYMREF) &&
		    symref_target && !strcmp(symref_target, target)) {
432
			existing = wt;
433 434
			break;
		}
435
	}
436

437 438
	return existing;
}
439 440 441 442 443 444 445

int submodule_uses_worktrees(const char *path)
{
	char *submodule_gitdir;
	struct strbuf sb = STRBUF_INIT;
	DIR *dir;
	struct dirent *d;
446
	int ret = 0;
447
	struct repository_format format = REPOSITORY_FORMAT_INIT;
448 449 450 451 452 453 454

	submodule_gitdir = git_pathdup_submodule(path, "%s", "");
	if (!submodule_gitdir)
		return 0;

	/* The env would be set for the superproject. */
	get_common_dir_noenv(&sb, submodule_gitdir);
455
	free(submodule_gitdir);
456 457 458 459 460 461 462 463 464

	/*
	 * The check below is only known to be good for repository format
	 * version 0 at the time of writing this code.
	 */
	strbuf_addstr(&sb, "/config");
	read_repository_format(&format, sb.buf);
	if (format.version != 0) {
		strbuf_release(&sb);
465
		clear_repository_format(&format);
466 467
		return 1;
	}
468
	clear_repository_format(&format);
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490

	/* Replace config by worktrees. */
	strbuf_setlen(&sb, sb.len - strlen("config"));
	strbuf_addstr(&sb, "worktrees");

	/* See if there is any file inside the worktrees directory. */
	dir = opendir(sb.buf);
	strbuf_release(&sb);

	if (!dir)
		return 0;

	while ((d = readdir(dir)) != NULL) {
		if (is_dot_or_dotdot(d->d_name))
			continue;

		ret = 1;
		break;
	}
	closedir(dir);
	return ret;
}
491

492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
int parse_worktree_ref(const char *worktree_ref, const char **name,
		       int *name_length, const char **ref)
{
	if (skip_prefix(worktree_ref, "main-worktree/", &worktree_ref)) {
		if (!*worktree_ref)
			return -1;
		if (name)
			*name = NULL;
		if (name_length)
			*name_length = 0;
		if (ref)
			*ref = worktree_ref;
		return 0;
	}
	if (skip_prefix(worktree_ref, "worktrees/", &worktree_ref)) {
		const char *slash = strchr(worktree_ref, '/');

		if (!slash || slash == worktree_ref || !slash[1])
			return -1;
		if (name)
			*name = worktree_ref;
		if (name_length)
			*name_length = slash - worktree_ref;
		if (ref)
			*ref = slash + 1;
		return 0;
	}
	return -1;
}

522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560
void strbuf_worktree_ref(const struct worktree *wt,
			 struct strbuf *sb,
			 const char *refname)
{
	switch (ref_type(refname)) {
	case REF_TYPE_PSEUDOREF:
	case REF_TYPE_PER_WORKTREE:
		if (wt && !wt->is_current) {
			if (is_main_worktree(wt))
				strbuf_addstr(sb, "main-worktree/");
			else
				strbuf_addf(sb, "worktrees/%s/", wt->id);
		}
		break;

	case REF_TYPE_MAIN_PSEUDOREF:
	case REF_TYPE_OTHER_PSEUDOREF:
		break;

	case REF_TYPE_NORMAL:
		/*
		 * For shared refs, don't prefix worktrees/ or
		 * main-worktree/. It's not necessary and
		 * files-backend.c can't handle it anyway.
		 */
		break;
	}
	strbuf_addstr(sb, refname);
}

const char *worktree_ref(const struct worktree *wt, const char *refname)
{
	static struct strbuf sb = STRBUF_INIT;

	strbuf_reset(&sb);
	strbuf_worktree_ref(wt, &sb, refname);
	return sb.buf;
}

561 562 563 564 565 566 567 568
int other_head_refs(each_ref_fn fn, void *cb_data)
{
	struct worktree **worktrees, **p;
	int ret = 0;

	worktrees = get_worktrees(0);
	for (p = worktrees; *p; p++) {
		struct worktree *wt = *p;
569 570
		struct object_id oid;
		int flag;
571 572 573 574

		if (wt->is_current)
			continue;

575 576 577 578 579
		if (!refs_read_ref_full(get_main_ref_store(the_repository),
					worktree_ref(wt, "HEAD"),
					RESOLVE_REF_READING,
					&oid, &flag))
			ret = fn(worktree_ref(wt, "HEAD"), &oid, flag, cb_data);
580 581 582 583 584 585
		if (ret)
			break;
	}
	free_worktrees(worktrees);
	return ret;
}