Merge branch 'ds/scalar-updates'

Scalar updates.

* ds/scalar-updates:
  scalar reconfigure: help users remove buggy repos
  setup: add discover_git_directory_reason()
  scalar: add --[no-]src option
This commit is contained in:
Junio C Hamano
2023-08-29 13:51:44 -07:00
5 changed files with 116 additions and 45 deletions

View File

@ -8,7 +8,8 @@ scalar - A tool for managing large Git repositories
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
scalar clone [--single-branch] [--branch <main-branch>] [--full-clone] <url> [<enlistment>] scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]
[--[no-]src] <url> [<enlistment>]
scalar list scalar list
scalar register [<enlistment>] scalar register [<enlistment>]
scalar unregister [<enlistment>] scalar unregister [<enlistment>]
@ -80,6 +81,11 @@ remote-tracking branch for the branch this option was used for the initial
cloning. If the HEAD at the remote did not point at any branch when cloning. If the HEAD at the remote did not point at any branch when
`--single-branch` clone was made, no remote-tracking branch is created. `--single-branch` clone was made, no remote-tracking branch is created.
--[no-]src::
By default, `scalar clone` places the cloned repository within a
`<entlistment>/src` directory. Use `--no-src` to place the cloned
repository directly in the `<enlistment>` directory.
--[no-]full-clone:: --[no-]full-clone::
A sparse-checkout is initialized by default. This behavior can be A sparse-checkout is initialized by default. This behavior can be
turned off via `--full-clone`. turned off via `--full-clone`.

View File

@ -409,6 +409,7 @@ static int cmd_clone(int argc, const char **argv)
{ {
const char *branch = NULL; const char *branch = NULL;
int full_clone = 0, single_branch = 0, show_progress = isatty(2); int full_clone = 0, single_branch = 0, show_progress = isatty(2);
int src = 1;
struct option clone_options[] = { struct option clone_options[] = {
OPT_STRING('b', "branch", &branch, N_("<branch>"), OPT_STRING('b', "branch", &branch, N_("<branch>"),
N_("branch to checkout after clone")), N_("branch to checkout after clone")),
@ -417,10 +418,13 @@ static int cmd_clone(int argc, const char **argv)
OPT_BOOL(0, "single-branch", &single_branch, OPT_BOOL(0, "single-branch", &single_branch,
N_("only download metadata for the branch that will " N_("only download metadata for the branch that will "
"be checked out")), "be checked out")),
OPT_BOOL(0, "src", &src,
N_("create repository within 'src' directory")),
OPT_END(), OPT_END(),
}; };
const char * const clone_usage[] = { const char * const clone_usage[] = {
N_("scalar clone [<options>] [--] <repo> [<dir>]"), N_("scalar clone [--single-branch] [--branch <main-branch>] [--full-clone]\n"
"\t[--[no-]src] <url> [<enlistment>]"),
NULL NULL
}; };
const char *url; const char *url;
@ -456,7 +460,10 @@ static int cmd_clone(int argc, const char **argv)
if (is_directory(enlistment)) if (is_directory(enlistment))
die(_("directory '%s' exists already"), enlistment); die(_("directory '%s' exists already"), enlistment);
dir = xstrfmt("%s/src", enlistment); if (src)
dir = xstrfmt("%s/src", enlistment);
else
dir = xstrdup(enlistment);
strbuf_reset(&buf); strbuf_reset(&buf);
if (branch) if (branch)
@ -657,6 +664,7 @@ static int cmd_reconfigure(int argc, const char **argv)
git_config(get_scalar_repos, &scalar_repos); git_config(get_scalar_repos, &scalar_repos);
for (i = 0; i < scalar_repos.nr; i++) { for (i = 0; i < scalar_repos.nr; i++) {
int succeeded = 0;
const char *dir = scalar_repos.items[i].string; const char *dir = scalar_repos.items[i].string;
strbuf_reset(&commondir); strbuf_reset(&commondir);
@ -667,30 +675,56 @@ static int cmd_reconfigure(int argc, const char **argv)
if (errno != ENOENT) { if (errno != ENOENT) {
warning_errno(_("could not switch to '%s'"), dir); warning_errno(_("could not switch to '%s'"), dir);
res = -1; goto loop_end;
continue;
} }
strbuf_addstr(&buf, dir); strbuf_addstr(&buf, dir);
if (remove_deleted_enlistment(&buf)) if (remove_deleted_enlistment(&buf))
res = error(_("could not remove stale " error(_("could not remove stale "
"scalar.repo '%s'"), dir); "scalar.repo '%s'"), dir);
else else {
warning(_("removing stale scalar.repo '%s'"), warning(_("removed stale scalar.repo '%s'"),
dir); dir);
succeeded = 1;
}
strbuf_release(&buf); strbuf_release(&buf);
} else if (discover_git_directory(&commondir, &gitdir) < 0) { goto loop_end;
warning_errno(_("git repository gone in '%s'"), dir); }
switch (discover_git_directory_reason(&commondir, &gitdir)) {
case GIT_DIR_INVALID_OWNERSHIP:
warning(_("repository at '%s' has different owner"), dir);
goto loop_end;
case GIT_DIR_INVALID_GITFILE:
case GIT_DIR_INVALID_FORMAT:
warning(_("repository at '%s' has a format issue"), dir);
goto loop_end;
case GIT_DIR_DISCOVERED:
succeeded = 1;
break;
default:
warning(_("repository not found in '%s'"), dir);
break;
}
git_config_clear();
the_repository = &r;
r.commondir = commondir.buf;
r.gitdir = gitdir.buf;
if (set_recommended_config(1) >= 0)
succeeded = 1;
loop_end:
if (!succeeded) {
res = -1; res = -1;
} else { warning(_("to unregister this repository from Scalar, run\n"
git_config_clear(); "\tgit config --global --unset --fixed-value scalar.repo \"%s\""),
dir);
the_repository = &r;
r.commondir = commondir.buf;
r.gitdir = gitdir.buf;
if (set_recommended_config(1) < 0)
res = -1;
} }
} }

34
setup.c
View File

@ -1221,19 +1221,6 @@ static const char *allowed_bare_repo_to_string(
return NULL; return NULL;
} }
enum discovery_result {
GIT_DIR_NONE = 0,
GIT_DIR_EXPLICIT,
GIT_DIR_DISCOVERED,
GIT_DIR_BARE,
/* these are errors */
GIT_DIR_HIT_CEILING = -1,
GIT_DIR_HIT_MOUNT_POINT = -2,
GIT_DIR_INVALID_GITFILE = -3,
GIT_DIR_INVALID_OWNERSHIP = -4,
GIT_DIR_DISALLOWED_BARE = -5,
};
/* /*
* We cannot decide in this function whether we are in the work tree or * We cannot decide in this function whether we are in the work tree or
* not, since the config can only be read _after_ this function was called. * not, since the config can only be read _after_ this function was called.
@ -1385,21 +1372,23 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
} }
} }
int discover_git_directory(struct strbuf *commondir, enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
struct strbuf *gitdir) struct strbuf *gitdir)
{ {
struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT; struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT;
size_t gitdir_offset = gitdir->len, cwd_len; size_t gitdir_offset = gitdir->len, cwd_len;
size_t commondir_offset = commondir->len; size_t commondir_offset = commondir->len;
struct repository_format candidate = REPOSITORY_FORMAT_INIT; struct repository_format candidate = REPOSITORY_FORMAT_INIT;
enum discovery_result result;
if (strbuf_getcwd(&dir)) if (strbuf_getcwd(&dir))
return -1; return GIT_DIR_CWD_FAILURE;
cwd_len = dir.len; cwd_len = dir.len;
if (setup_git_directory_gently_1(&dir, gitdir, NULL, 0) <= 0) { result = setup_git_directory_gently_1(&dir, gitdir, NULL, 0);
if (result <= 0) {
strbuf_release(&dir); strbuf_release(&dir);
return -1; return result;
} }
/* /*
@ -1429,11 +1418,11 @@ int discover_git_directory(struct strbuf *commondir,
strbuf_setlen(commondir, commondir_offset); strbuf_setlen(commondir, commondir_offset);
strbuf_setlen(gitdir, gitdir_offset); strbuf_setlen(gitdir, gitdir_offset);
clear_repository_format(&candidate); clear_repository_format(&candidate);
return -1; return GIT_DIR_INVALID_FORMAT;
} }
clear_repository_format(&candidate); clear_repository_format(&candidate);
return 0; return result;
} }
const char *setup_git_directory_gently(int *nongit_ok) const char *setup_git_directory_gently(int *nongit_ok)
@ -1515,10 +1504,11 @@ const char *setup_git_directory_gently(int *nongit_ok)
} }
*nongit_ok = 1; *nongit_ok = 1;
break; break;
case GIT_DIR_NONE: case GIT_DIR_CWD_FAILURE:
case GIT_DIR_INVALID_FORMAT:
/* /*
* As a safeguard against setup_git_directory_gently_1 returning * As a safeguard against setup_git_directory_gently_1 returning
* this value, fallthrough to BUG. Otherwise it is possible to * these values, fallthrough to BUG. Otherwise it is possible to
* set startup_info->have_repository to 1 when we did nothing to * set startup_info->have_repository to 1 when we did nothing to
* find a repository. * find a repository.
*/ */

35
setup.h
View File

@ -42,16 +42,45 @@ const char *resolve_gitdir_gently(const char *suspect, int *return_error_code);
#define resolve_gitdir(path) resolve_gitdir_gently((path), NULL) #define resolve_gitdir(path) resolve_gitdir_gently((path), NULL)
void setup_work_tree(void); void setup_work_tree(void);
/*
* discover_git_directory_reason() is similar to discover_git_directory(),
* except it returns an enum value instead. It is important to note that
* a zero-valued return here is actually GIT_DIR_NONE, which is different
* from discover_git_directory.
*/
enum discovery_result {
GIT_DIR_EXPLICIT = 1,
GIT_DIR_DISCOVERED = 2,
GIT_DIR_BARE = 3,
/* these are errors */
GIT_DIR_HIT_CEILING = -1,
GIT_DIR_HIT_MOUNT_POINT = -2,
GIT_DIR_INVALID_GITFILE = -3,
GIT_DIR_INVALID_OWNERSHIP = -4,
GIT_DIR_DISALLOWED_BARE = -5,
GIT_DIR_INVALID_FORMAT = -6,
GIT_DIR_CWD_FAILURE = -7,
};
enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
struct strbuf *gitdir);
/* /*
* Find the commondir and gitdir of the repository that contains the current * Find the commondir and gitdir of the repository that contains the current
* working directory, without changing the working directory or other global * working directory, without changing the working directory or other global
* state. The result is appended to commondir and gitdir. If the discovered * state. The result is appended to commondir and gitdir. If the discovered
* gitdir does not correspond to a worktree, then 'commondir' and 'gitdir' will * gitdir does not correspond to a worktree, then 'commondir' and 'gitdir' will
* both have the same result appended to the buffer. The return value is * both have the same result appended to the buffer. The return value is
* either 0 upon success and non-zero if no repository was found. * either 0 upon success and -1 if no repository was found.
*/ */
int discover_git_directory(struct strbuf *commondir, static inline int discover_git_directory(struct strbuf *commondir,
struct strbuf *gitdir); struct strbuf *gitdir)
{
if (discover_git_directory_reason(commondir, gitdir) <= 0)
return -1;
return 0;
}
const char *setup_git_directory_gently(int *); const char *setup_git_directory_gently(int *);
const char *setup_git_directory(void); const char *setup_git_directory(void);
char *prefix_path(const char *prefix, int len, const char *path); char *prefix_path(const char *prefix, int len, const char *path);

View File

@ -180,4 +180,16 @@ test_expect_success 'scalar clone warns when background maintenance fails' '
grep "could not turn on maintenance" err grep "could not turn on maintenance" err
' '
test_expect_success '`scalar clone --no-src`' '
scalar clone --src "file://$(pwd)/to-clone" with-src &&
scalar clone --no-src "file://$(pwd)/to-clone" without-src &&
test_path_is_dir with-src/src &&
test_path_is_missing without-src/src &&
(cd with-src/src && ls ?*) >with &&
(cd without-src && ls ?*) >without &&
test_cmp with without
'
test_done test_done