Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 40 additions & 26 deletions setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -963,15 +963,48 @@ void read_gitfile_error_die(int error_code, const char *path)
*/
const char *read_gitfile_gently(const char *path, int *return_error_code)
{
const int max_file_size = 1 << 20; /* 1MB */
int error_code = 0;
char *buf = NULL;
char *dir = NULL;
const char *slash;
static struct strbuf realpath = STRBUF_INIT;

buf = xstrdup_or_null(read_gitfile_raw(path, &error_code));
if (error_code)
goto cleanup_return;

if (!is_absolute_path(buf) && (slash = strrchr(path, '/'))) {
size_t pathlen = slash+1 - path;
char *dir = xstrfmt("%.*s%.*s", (int)pathlen, path,
(int)strlen(buf), buf);
free(buf);
buf = dir;
}
if (!is_git_directory(buf)) {
error_code = READ_GITFILE_ERR_NOT_A_REPO;
goto cleanup_return;
}

strbuf_realpath(&realpath, buf, 1);

cleanup_return:
if (return_error_code)
*return_error_code = error_code;
else if (error_code)
read_gitfile_error_die(error_code, path);

free(buf);
return error_code ? NULL : realpath.buf;
}

const char *read_gitfile_raw(const char *path, int *return_error_code)
{
const int max_file_size = 1 << 20; /* 1MB */
int error_code = 0;
char *buf = NULL;
struct stat st;
int fd;
ssize_t len;
static struct strbuf realpath = STRBUF_INIT;
static struct strbuf contents = STRBUF_INIT;

if (stat(path, &st)) {
if (errno == ENOENT || errno == ENOTDIR)
Expand Down Expand Up @@ -1014,32 +1047,13 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
error_code = READ_GITFILE_ERR_NO_PATH;
goto cleanup_return;
}
buf[len] = '\0';
dir = buf + 8;

if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) {
size_t pathlen = slash+1 - path;
dir = xstrfmt("%.*s%.*s", (int)pathlen, path,
(int)(len - 8), buf + 8);
free(buf);
buf = dir;
}
if (!is_git_directory(dir)) {
error_code = READ_GITFILE_ERR_NOT_A_REPO;
goto cleanup_return;
}

strbuf_realpath(&realpath, dir, 1);
path = realpath.buf;
strbuf_reset(&contents);
strbuf_add(&contents, buf+8, len-8);

cleanup_return:
if (return_error_code)
*return_error_code = error_code;
else if (error_code)
read_gitfile_error_die(error_code, path);

*return_error_code = error_code;
free(buf);
return error_code ? NULL : path;
return error_code ? NULL : contents.buf;
}

static void apply_gitdir_and_environment(struct repository *repo, const char *path)
Expand Down
1 change: 1 addition & 0 deletions setup.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ int is_nonbare_repository_dir(struct strbuf *path);
#define READ_GITFILE_ERR_IS_A_DIR 10
void read_gitfile_error_die(int error_code, const char *path);
const char *read_gitfile_gently(const char *path, int *return_error_code);
const char *read_gitfile_raw(const char *path, int *return_error_code);
#define read_gitfile(path) read_gitfile_gently((path), NULL)
const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
Expand Down
54 changes: 42 additions & 12 deletions t/t2406-worktree-repair.sh
Original file line number Diff line number Diff line change
Expand Up @@ -228,30 +228,60 @@ test_expect_success 'repair worktree with relative path with missing gitfile' '
test_cmp expect wt/.git
'

test_expect_success 'repair absolute worktree to use relative paths' '
test_when_finished "rm -rf main side sidemoved" &&
test_expect_success 'repair absolute to relative from side worktree' '
test_when_finished "rm -rf main side" &&
test_create_repo main &&
test_commit -C main init &&
git -C main worktree add --detach ../side &&
echo "../../../../sidemoved/.git" >expect-gitdir &&
echo "../../../../side/.git" >expect-gitdir &&
echo "gitdir: ../main/.git/worktrees/side" >expect-gitfile &&
mv side sidemoved &&
git -C main worktree repair --relative-paths ../sidemoved &&
git -C main worktree repair --relative-paths ../side 2>main/err &&
test_grep "gitdir absolute/relative path mismatch" main/err &&
test_cmp expect-gitdir main/.git/worktrees/side/gitdir &&
test_cmp expect-gitfile sidemoved/.git
test_cmp expect-gitfile side/.git
'

test_expect_success 'repair relative worktree to use absolute paths' '
test_when_finished "rm -rf main side sidemoved" &&
test_expect_success 'repair relative to absolute from side worktree' '
test_when_finished "rm -rf main side" &&
test_create_repo main &&
test_commit -C main init &&
git -C main worktree add --relative-paths --detach ../side &&
echo "$(pwd)/sidemoved/.git" >expect-gitdir &&
echo "$(pwd)/side/.git" >expect-gitdir &&
echo "gitdir: $(pwd)/main/.git/worktrees/side" >expect-gitfile &&
mv side sidemoved &&
git -C main worktree repair ../sidemoved &&
git -C main worktree repair ../side 2>main/err &&
test_grep "gitdir absolute/relative path mismatch" main/err &&
test_cmp expect-gitdir main/.git/worktrees/side/gitdir &&
test_cmp expect-gitfile sidemoved/.git
test_cmp expect-gitfile side/.git
'

test_expect_success 'repair absolute to relative from main worktree' '
test_when_finished "rm -rf main side" &&
test_create_repo main &&
git -C main config worktree.useRelativePaths false &&
test_commit -C main init &&
git -C main worktree add --detach ../side &&
echo "../../../../side/.git" >expect-gitdir &&
echo "gitdir: ../main/.git/worktrees/side" >expect-gitfile &&
git -C main config worktree.useRelativePaths true &&
git -C main worktree repair 2>main/err &&
test_grep ".git file absolute/relative path mismatch" main/err &&
test_cmp expect-gitdir main/.git/worktrees/side/gitdir &&
test_cmp expect-gitfile side/.git
'

test_expect_success 'repair relative to absolute from main worktree' '
test_when_finished "rm -rf main side" &&
test_create_repo main &&
git -C main config worktree.useRelativePaths true &&
test_commit -C main init &&
git -C main worktree add --detach ../side &&
echo "$(pwd)/side/.git" >expect-gitdir &&
echo "gitdir: $(pwd)/main/.git/worktrees/side" >expect-gitfile &&
git -C main config worktree.useRelativePaths false &&
git -C main worktree repair 2>main/err &&
test_grep ".git file absolute/relative path mismatch" main/err &&
test_cmp expect-gitdir main/.git/worktrees/side/gitdir &&
test_cmp expect-gitfile side/.git
'

test_done
13 changes: 3 additions & 10 deletions worktree.c
Original file line number Diff line number Diff line change
Expand Up @@ -667,7 +667,7 @@ static void repair_gitfile(struct worktree *wt,
strbuf_realpath(&repo, path, 1);
strbuf_addf(&dotgit, "%s/.git", wt->path);
strbuf_addf(&gitdir, "%s/gitdir", repo.buf);
dotgit_contents = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
dotgit_contents = xstrdup_or_null(read_gitfile_raw(dotgit.buf, &err));

if (dotgit_contents) {
if (is_absolute_path(dotgit_contents)) {
Expand All @@ -681,7 +681,7 @@ static void repair_gitfile(struct worktree *wt,
if (err == READ_GITFILE_ERR_NOT_A_FILE ||
err == READ_GITFILE_ERR_IS_A_DIR)
fn(1, wt->path, _(".git is not a file"), cb_data);
else if (err)
else if (err || !is_git_directory(backlink.buf))
repair = _(".git file broken");
else if (fspathcmp(backlink.buf, repo.buf))
repair = _(".git file incorrect");
Expand Down Expand Up @@ -857,14 +857,7 @@ void repair_worktree_at_path(struct repository *repo,
strbuf_realpath_forgiving(&inferred_backlink, inferred_backlink.buf, 0);
dotgit_contents = xstrdup_or_null(read_gitfile_gently(dotgit.buf, &err));
if (dotgit_contents) {
if (is_absolute_path(dotgit_contents)) {
strbuf_addstr(&backlink, dotgit_contents);
} else {
strbuf_addbuf(&backlink, &dotgit);
strbuf_strip_suffix(&backlink, ".git");
strbuf_addstr(&backlink, dotgit_contents);
strbuf_realpath_forgiving(&backlink, backlink.buf, 0);
}
strbuf_addstr(&backlink, dotgit_contents);
} else if (err == READ_GITFILE_ERR_NOT_A_FILE ||
err == READ_GITFILE_ERR_IS_A_DIR) {
fn(1, dotgit.buf, _("unable to locate repository; .git is not a file"), cb_data);
Expand Down
Loading