Skip to content
Merged
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
188 changes: 115 additions & 73 deletions contrib/scalar/scalar.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,55 +19,91 @@ static int is_unattended(void) {
return git_env_bool("Scalar_UNATTENDED", 0);
}

/*
* Remove the deepest subdirectory in the provided path string. Path must not
* include a trailing path separator. Returns 1 if parent directory found,
* otherwise 0.
*/
static int strbuf_parentdir(struct strbuf *buf)
{
size_t len = buf->len;
size_t offset = offset_1st_component(buf->buf);
char *path_sep = find_last_dir_sep(buf->buf + offset);
strbuf_setlen(buf, path_sep ? path_sep - buf->buf : offset);

return buf->len < len;
}

static void setup_enlistment_directory(int argc, const char **argv,
const char * const *usagestr,
const struct option *options)
const struct option *options,
struct strbuf *enlistment_root)
{
struct strbuf path = STRBUF_INIT;
char *root;
int enlistment_found = 0;

if (startup_info->have_repository)
BUG("gitdir already set up?!?");

if (argc > 1)
usage_with_options(usagestr, options);

/* find the worktree, determine its corresponding root */
if (argc == 1) {
char *src = xstrfmt("%s/src", argv[0]);
const char *dir = is_directory(src) ? src : argv[0];
strbuf_add_absolute_path(&path, argv[0]);
} else if (strbuf_getcwd(&path) < 0) {
die(_("need a working directory"));
}

if (chdir(dir) < 0)
die_errno(_("could not switch to '%s'"), dir);
strbuf_trim_trailing_dir_sep(&path);
do {
const size_t len = path.len;

free(src);
} else {
/* find the worktree, and ensure that it is named `src` */
struct strbuf path = STRBUF_INIT;
/* check if currently in enlistment root with src/ workdir */
strbuf_addstr(&path, "/src/.git");
if (is_git_directory(path.buf)) {
strbuf_strip_suffix(&path, "/.git");

if (strbuf_getcwd(&path) < 0)
die(_("need a working directory"));

for (;;) {
size_t len = path.len;

strbuf_addstr(&path, "/src/.git");
if (is_git_directory(path.buf)) {
strbuf_setlen(&path, len);
strbuf_addstr(&path, "/src");
if (chdir(path.buf) < 0)
die_errno(_("could not switch to '%s'"),
path.buf);
strbuf_release(&path);
break;
}
if (enlistment_root)
strbuf_add(enlistment_root, path.buf, len);

while (len > 0 && !is_dir_sep(path.buf[--len]))
; /* keep looking for parent directory */
enlistment_found = 1;
break;
}

if (!len)
die(_("could not find enlistment root"));
/* reset to original path */
strbuf_setlen(&path, len);

/* check if currently in workdir */
strbuf_addstr(&path, "/.git");
if (is_git_directory(path.buf)) {
strbuf_setlen(&path, len);

if (enlistment_root) {
/*
* If the worktree's directory's name is `src`, the enlistment is the
* parent directory, otherwise it is identical to the worktree.
*/
root = strip_path_suffix(path.buf, "src");
strbuf_addstr(enlistment_root, root ? root : path.buf);
free(root);
}

enlistment_found = 1;
break;
}
}

strbuf_setlen(&path, len);
} while (strbuf_parentdir(&path));

if (!enlistment_found)
die(_("could not find enlistment root"));

if (chdir(path.buf) < 0)
die_errno(_("could not switch to '%s'"), path.buf);

strbuf_release(&path);
setup_git_directory();
}

Expand Down Expand Up @@ -789,42 +825,28 @@ static char *remote_default_branch(const char *url)
return NULL;
}

static void strbuf_parentdir(struct strbuf *buf)
static int delete_enlistment(struct strbuf *enlistment)
{
int len = buf->len;
while (len > 0 && !is_dir_sep(buf->buf[--len]))
; /* keep looking for parent directory */
strbuf_setlen(buf, len);
}

static int delete_enlistment(void)
{
struct strbuf enlistment = STRBUF_INIT;
#ifdef WIN32
struct strbuf parent = STRBUF_INIT;
#endif

if (unregister_dir())
die(_("failed to unregister repository"));

/* Compute the enlistment path (parent of the worktree) */
strbuf_addstr(&enlistment, the_repository->worktree);
strbuf_parentdir(&enlistment);

#ifdef WIN32
/* Change current directory to one outside of the enlistment
so that we may delete everything underneath it. */
strbuf_addbuf(&parent, &enlistment);
strbuf_addbuf(&parent, enlistment);
strbuf_parentdir(&parent);
if (chdir(parent.buf) < 0)
die_errno(_("could not switch to '%s'"), parent.buf);
strbuf_release(&parent);
#endif

if (remove_dir_recursively(&enlistment, 0))
if (remove_dir_recursively(enlistment, 0))
die(_("failed to delete enlistment directory"));

strbuf_release(&enlistment);
return 0;
}

Expand Down Expand Up @@ -1185,9 +1207,9 @@ static int cmd_diagnose(int argc, const char **argv)
argc = parse_options(argc, argv, NULL, options,
usage, 0);

setup_enlistment_directory(argc, argv, usage, options);
setup_enlistment_directory(argc, argv, usage, options, &buf);

strbuf_addstr(&buf, "../.scalarDiagnostics/scalar_");
strbuf_addstr(&buf, "/.scalarDiagnostics/scalar_");
strbuf_addftime(&buf, "%Y%m%d_%H%M%S", localtime_r(&now, &tm), 0, 0);
if (run_git("init", "-q", "-b", "dummy", "--bare", buf.buf, NULL)) {
res = error(_("could not initialize temporary repository: %s"),
Expand Down Expand Up @@ -1299,7 +1321,7 @@ static int cmd_register(int argc, const char **argv)
argc = parse_options(argc, argv, NULL, options,
usage, 0);

setup_enlistment_directory(argc, argv, usage, options);
setup_enlistment_directory(argc, argv, usage, options, NULL);

return register_dir();
}
Expand Down Expand Up @@ -1335,7 +1357,7 @@ static int cmd_reconfigure(int argc, const char **argv)
usage, 0);

if (!all) {
setup_enlistment_directory(argc, argv, usage, options);
setup_enlistment_directory(argc, argv, usage, options, NULL);

return set_recommended_config(1);
}
Expand Down Expand Up @@ -1421,7 +1443,7 @@ static int cmd_run(int argc, const char **argv)

argc--;
argv++;
setup_enlistment_directory(argc, argv, usagestr, options);
setup_enlistment_directory(argc, argv, usagestr, options, NULL);
strbuf_release(&buf);

if (i == 0)
Expand All @@ -1440,6 +1462,24 @@ static int cmd_run(int argc, const char **argv)
return 0;
}

static int remove_deleted_enlistment(struct strbuf *path)
{
int res = 0;
strbuf_realpath_forgiving(path, path->buf, 1);

if (run_git("config", "--global",
"--unset", "--fixed-value",
"scalar.repo", path->buf, NULL) < 0)
res = -1;

if (run_git("config", "--global",
"--unset", "--fixed-value",
"maintenance.repo", path->buf, NULL) < 0)
res = -1;

return res;
}

static int cmd_unregister(int argc, const char **argv)
{
struct option options[] = {
Expand All @@ -1459,32 +1499,29 @@ static int cmd_unregister(int argc, const char **argv)
* mistake and _still_ wants to unregister the thing.
*/
if (argc == 1) {
struct strbuf path = STRBUF_INIT;
struct strbuf src_path = STRBUF_INIT, workdir_path = STRBUF_INIT;

strbuf_addf(&path, "%s/src/.git", argv[0]);
if (!is_directory(path.buf)) {
int res = 0;
strbuf_addf(&src_path, "%s/src/.git", argv[0]);
strbuf_addf(&workdir_path, "%s/.git", argv[0]);
if (!is_directory(src_path.buf) && !is_directory(workdir_path.buf)) {
/* remove possible matching registrations */
int res = -1;

strbuf_strip_suffix(&path, "/.git");
strbuf_realpath_forgiving(&path, path.buf, 1);

if (run_git("config", "--global",
"--unset", "--fixed-value",
"scalar.repo", path.buf, NULL) < 0)
res = -1;
strbuf_strip_suffix(&src_path, "/.git");
res = remove_deleted_enlistment(&src_path) && res;

if (run_git("config", "--global",
"--unset", "--fixed-value",
"maintenance.repo", path.buf, NULL) < 0)
res = -1;
strbuf_strip_suffix(&workdir_path, "/.git");
res = remove_deleted_enlistment(&workdir_path) && res;

strbuf_release(&path);
strbuf_release(&src_path);
strbuf_release(&workdir_path);
return res;
}
strbuf_release(&path);
strbuf_release(&src_path);
strbuf_release(&workdir_path);
}

setup_enlistment_directory(argc, argv, usage, options);
setup_enlistment_directory(argc, argv, usage, options, NULL);

return unregister_dir();
}
Expand All @@ -1498,16 +1535,21 @@ static int cmd_delete(int argc, const char **argv)
N_("scalar delete <enlistment>"),
NULL
};
struct strbuf enlistment = STRBUF_INIT;
int res = 0;

argc = parse_options(argc, argv, NULL, options,
usage, 0);

if (argc != 1)
usage_with_options(usage, options);

setup_enlistment_directory(argc, argv, usage, options);
setup_enlistment_directory(argc, argv, usage, options, &enlistment);

return delete_enlistment();
res = delete_enlistment(&enlistment);
strbuf_release(&enlistment);

return res;
}

static int cmd_help(int argc, const char **argv)
Expand Down Expand Up @@ -1586,7 +1628,7 @@ static int cmd_cache_server(int argc, const char **argv)
usage_msg_opt(_("--get/--set/--list are mutually exclusive"),
usage, options);

setup_enlistment_directory(argc, argv, usage, options);
setup_enlistment_directory(argc, argv, usage, options, NULL);

if (list) {
const char *name = list, *url = list;
Expand Down
53 changes: 53 additions & 0 deletions contrib/scalar/t/t9099-scalar.sh
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,57 @@ test_expect_success '`scalar clone` with GVFS-enabled server' '
)
'

test_expect_success '`scalar register` parallel to worktree' '
git init test-repo/src &&
mkdir -p test-repo/out &&
scalar register test-repo/out &&
git config --get --global --fixed-value \
maintenance.repo "$(pwd)/test-repo/src" &&
scalar list >scalar.repos &&
grep -F "$(pwd)/test-repo/src" scalar.repos &&
scalar delete test-repo
'

test_expect_success '`scalar register` & `unregister` with existing repo' '
git init existing &&
scalar register existing &&
git config --get --global --fixed-value \
maintenance.repo "$(pwd)/existing" &&
scalar list >scalar.repos &&
grep -F "$(pwd)/existing" scalar.repos &&
scalar unregister existing &&
test_must_fail git config --get --global --fixed-value \
maintenance.repo "$(pwd)/existing" &&
scalar list >scalar.repos &&
! grep -F "$(pwd)/existing" scalar.repos
'

test_expect_success '`scalar unregister` with existing repo, deleted .git' '
scalar register existing &&
rm -rf existing/.git &&
scalar unregister existing &&
test_must_fail git config --get --global --fixed-value \
maintenance.repo "$(pwd)/existing" &&
scalar list >scalar.repos &&
! grep -F "$(pwd)/existing" scalar.repos
'

test_expect_success '`scalar register` existing repo with `src` folder' '
git init existing &&
mkdir -p existing/src &&
scalar register existing/src &&
scalar list >scalar.repos &&
grep -F "$(pwd)/existing" scalar.repos &&
scalar unregister existing &&
scalar list >scalar.repos &&
! grep -F "$(pwd)/existing" scalar.repos
'

test_expect_success '`scalar delete` with existing repo' '
git init existing &&
scalar register existing &&
scalar delete existing &&
test_path_is_missing existing
'

test_done