worktree.c 11.5 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
	path = real_pathdup(arg, 1);
221 222 223 224
	for (; *list; list++)
		if (!fspathcmp(path, real_path((*list)->path)))
			break;
	free(path);
225
	free(to_free);
226 227 228
	return *list;
}

229 230 231 232 233
int is_main_worktree(const struct worktree *wt)
{
	return !wt->id;
}

234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
const char *is_worktree_locked(struct worktree *wt)
{
	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;
}

257 258 259 260 261 262 263 264 265 266 267 268 269
/* 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);
}

270 271
int validate_worktree(const struct worktree *wt, struct strbuf *errmsg,
		      unsigned flags)
272 273 274 275 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
{
	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;
	}

307 308 309 310 311 312
	if (flags & WT_VALIDATE_WORKTREE_MISSING_OK &&
	    !file_exists(wt->path)) {
		ret = 0;
		goto done;
	}

313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
	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;
}

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352
void update_worktree_location(struct worktree *wt, const char *path_)
{
	struct strbuf path = STRBUF_INIT;

	if (is_main_worktree(wt))
		die("BUG: can't relocate main worktree");

	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);
}

353 354
int is_worktree_being_rebased(const struct worktree *wt,
			      const char *target)
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370
{
	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;
}

371 372
int is_worktree_being_bisected(const struct worktree *wt,
			       const char *target)
373
{
374 375 376 377 378 379 380 381 382 383 384 385
	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;
}

386 387 388 389 390 391
/*
 * 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.
 */
392 393
const struct worktree *find_shared_symref(const char *symref,
					  const char *target)
394
{
395 396
	const struct worktree *existing = NULL;
	static struct worktree **worktrees;
397 398
	int i = 0;

399 400
	if (worktrees)
		free_worktrees(worktrees);
401
	worktrees = get_worktrees(0);
402

403
	for (i = 0; worktrees[i]; i++) {
404
		struct worktree *wt = worktrees[i];
405 406 407 408
		const char *symref_target;
		struct ref_store *refs;
		int flags;

409 410
		if (wt->is_bare)
			continue;
411

412 413 414 415 416
		if (wt->is_detached && !strcmp(symref, "HEAD")) {
			if (is_worktree_being_rebased(wt, target)) {
				existing = wt;
				break;
			}
417 418 419 420
			if (is_worktree_being_bisected(wt, target)) {
				existing = wt;
				break;
			}
421 422
		}

423 424
		refs = get_worktree_ref_store(wt);
		symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
425
							NULL, &flags);
426 427
		if ((flags & REF_ISSYMREF) &&
		    symref_target && !strcmp(symref_target, target)) {
428
			existing = wt;
429 430
			break;
		}
431
	}
432

433 434
	return existing;
}
435 436 437 438 439 440 441

int submodule_uses_worktrees(const char *path)
{
	char *submodule_gitdir;
	struct strbuf sb = STRBUF_INIT;
	DIR *dir;
	struct dirent *d;
442
	int ret = 0;
443 444 445 446 447 448 449 450
	struct repository_format format;

	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);
451
	free(submodule_gitdir);
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484

	/*
	 * 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);
		return 1;
	}

	/* 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;
}
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506

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;
		struct ref_store *refs;

		if (wt->is_current)
			continue;

		refs = get_worktree_ref_store(wt);
		ret = refs_head_ref(refs, fn, cb_data);
		if (ret)
			break;
	}
	free_worktrees(worktrees);
	return ret;
}