diff mbox series

[v4,13/15] scalar: implement the `delete` command

Message ID 914c16c7fcd948374fb2c0582c435d0d6ac775a0.1631630356.git.gitgitgadget@gmail.com (mailing list archive)
State New, archived
Headers show
Series Upstreaming the Scalar command | expand

Commit Message

Matthew John Cheetham Sept. 14, 2021, 2:39 p.m. UTC
From: Matthew John Cheetham <mjcheetham@outlook.com>

Delete an enlistment by first unregistering the repository and then
deleting the enlistment directory (usually the directory containing the
worktree `src/` directory).

On Windows, if the current directory is inside the enlistment's
directory, change to the parent of the enlistment directory, to allow us
to delete the enlistment (directories used by processes e.g. as current
working directories cannot be deleted on Windows).

Co-authored-by: Victoria Dye <vdye@github.com>
Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
---
 contrib/scalar/scalar.c          | 55 ++++++++++++++++++++++++++++++++
 contrib/scalar/scalar.txt        |  8 +++++
 contrib/scalar/t/t9099-scalar.sh |  9 ++++++
 3 files changed, 72 insertions(+)

Comments

Elijah Newren Sept. 28, 2021, 6:24 a.m. UTC | #1
On Tue, Sep 14, 2021 at 7:39 AM Matthew John Cheetham via GitGitGadget
<gitgitgadget@gmail.com> wrote:
>
> From: Matthew John Cheetham <mjcheetham@outlook.com>
>
> Delete an enlistment by first unregistering the repository and then
> deleting the enlistment directory (usually the directory containing the
> worktree `src/` directory).
>
> On Windows, if the current directory is inside the enlistment's
> directory, change to the parent of the enlistment directory, to allow us
> to delete the enlistment (directories used by processes e.g. as current
> working directories cannot be deleted on Windows).

But if the current directory is inside the enlistment's directory,
didn't that happen because the parent process' current directory was
inside the enlistment directory?  Or was there some kind of directory
switching that scalar itself was doing causing it to be inside the
enlistment directory?

If the the current directory was inside the enlistment's directory
because it inherited a parent process' current directory, wouldn't
that also prevent deleting it?  If so, should there be a special check
for that case and pre-emptively returning an error rather than
attempting the recursive directory deletion and just spitting out an
error when it fails?

(Also seems slightly related to
https://github.com/gitgitgadget/git/pull/1037, which I'll submit as
soon as en/removing_untracked_fixes hits next.)

>
> Co-authored-by: Victoria Dye <vdye@github.com>
> Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> ---
>  contrib/scalar/scalar.c          | 55 ++++++++++++++++++++++++++++++++
>  contrib/scalar/scalar.txt        |  8 +++++
>  contrib/scalar/t/t9099-scalar.sh |  9 ++++++
>  3 files changed, 72 insertions(+)
>
> diff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c
> index 67fa5305225..00bedb0bf66 100644
> --- a/contrib/scalar/scalar.c
> +++ b/contrib/scalar/scalar.c
> @@ -8,6 +8,7 @@
>  #include "config.h"
>  #include "run-command.h"
>  #include "refs.h"
> +#include "dir.h"
>
>  /*
>   * Remove the deepest subdirectory in the provided path string. Path must not
> @@ -334,6 +335,33 @@ static char *remote_default_branch(const char *url)
>         return NULL;
>  }
>
> +static int delete_enlistment(struct strbuf *enlistment)
> +{
> +#ifdef WIN32
> +       struct strbuf parent = STRBUF_INIT;
> +#endif
> +
> +       if (unregister_dir())
> +               die(_("failed to unregister repository"));
> +
> +#ifdef WIN32
> +       /*
> +        * Change the current directory to one outside of the enlistment so
> +        * that we may delete everything underneath it.
> +        */
> +       strbuf_addbuf(&parent, enlistment);
> +       strbuf_parent_directory(&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))
> +               die(_("failed to delete enlistment directory"));
> +
> +       return 0;
> +}
> +
>  static int cmd_clone(int argc, const char **argv)
>  {
>         const char *branch = NULL;
> @@ -694,6 +722,32 @@ static int cmd_unregister(int argc, const char **argv)
>         return unregister_dir();
>  }
>
> +static int cmd_delete(int argc, const char **argv)
> +{
> +       struct option options[] = {
> +               OPT_END(),
> +       };
> +       const char * const usage[] = {
> +               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, &enlistment);
> +
> +       res = delete_enlistment(&enlistment);
> +       strbuf_release(&enlistment);
> +
> +       return res;
> +}
> +
>  static struct {
>         const char *name;
>         int (*fn)(int, const char **);
> @@ -704,6 +758,7 @@ static struct {
>         { "unregister", cmd_unregister },
>         { "run", cmd_run },
>         { "reconfigure", cmd_reconfigure },
> +       { "delete", cmd_delete },
>         { NULL, NULL},
>  };
>
> diff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt
> index 2fa96fcabc6..6fc57707718 100644
> --- a/contrib/scalar/scalar.txt
> +++ b/contrib/scalar/scalar.txt
> @@ -14,6 +14,7 @@ scalar register [<enlistment>]
>  scalar unregister [<enlistment>]
>  scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]
>  scalar reconfigure [ --all | <enlistment> ]
> +scalar delete <enlistment>
>
>  DESCRIPTION
>  -----------
> @@ -127,6 +128,13 @@ reconfigure the enlistment.
>  With the `--all` option, all enlistments currently registered with Scalar
>  will be reconfigured. Use this option after each Scalar upgrade.
>
> +Delete
> +~~~~~~
> +
> +delete <enlistment>::
> +       This subcommand lets you delete an existing Scalar enlistment from your
> +       local file system, unregistering the repository.
> +
>  SEE ALSO
>  --------
>  linkgit:git-clone[1], linkgit:git-maintenance[1].
> diff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh
> index 5fe7fabd0e5..7e8771d0eff 100755
> --- a/contrib/scalar/t/t9099-scalar.sh
> +++ b/contrib/scalar/t/t9099-scalar.sh
> @@ -76,4 +76,13 @@ test_expect_success 'scalar reconfigure' '
>         test true = "$(git -C one/src config core.preloadIndex)"
>  '
>
> +test_expect_success 'scalar delete without enlistment shows a usage' '
> +       test_expect_code 129 scalar delete
> +'
> +
> +test_expect_success 'scalar delete with enlistment' '
> +       scalar delete cloned &&
> +       test_path_is_missing cloned
> +'
> +
>  test_done
> --
> gitgitgadget
Johannes Schindelin Oct. 6, 2021, 8:48 p.m. UTC | #2
Hi Matthew and Elijah,

On Mon, 4 Oct 2021, Matthew Cheetham wrote:

> On 28 Sep 2021, at 7:24 am, Elijah Newren <newren@gmail.com> wrote:
>
> > But if the current directory is inside the enlistment's directory,
> > didn't that happen because the parent process' current directory was
> > inside the enlistment directory?  Or was there some kind of directory
> > switching that scalar itself was doing causing it to be inside the
> > enlistment directory?
>
> Yes to the latter. `setup_enlistment_directory` changes the current
> directory much like `setup_git_directory`.
>
> > If the the current directory was inside the enlistment's directory
> > because it inherited a parent process' current directory, wouldn't
> > that also prevent deleting it?  If so, should there be a special check
> > for that case and pre-emptively returning an error rather than
> > attempting the recursive directory deletion and just spitting out an
> > error when it fails?
>
> You are correct. Speaking to Johannes about this I believe he is looking
> to add a check/error in a new patch series revision.

Indeed, I did notice GGG#1037, and I changed the code so that it detects
whether `scalar delete` was called from within the enlistment, and refuses
to run in that case. Users will have to call `scalar delete <path>` from
outside the enlistment.

Ciao,
Dscho
diff mbox series

Patch

diff --git a/contrib/scalar/scalar.c b/contrib/scalar/scalar.c
index 67fa5305225..00bedb0bf66 100644
--- a/contrib/scalar/scalar.c
+++ b/contrib/scalar/scalar.c
@@ -8,6 +8,7 @@ 
 #include "config.h"
 #include "run-command.h"
 #include "refs.h"
+#include "dir.h"
 
 /*
  * Remove the deepest subdirectory in the provided path string. Path must not
@@ -334,6 +335,33 @@  static char *remote_default_branch(const char *url)
 	return NULL;
 }
 
+static int delete_enlistment(struct strbuf *enlistment)
+{
+#ifdef WIN32
+	struct strbuf parent = STRBUF_INIT;
+#endif
+
+	if (unregister_dir())
+		die(_("failed to unregister repository"));
+
+#ifdef WIN32
+	/*
+	 * Change the current directory to one outside of the enlistment so
+	 * that we may delete everything underneath it.
+	 */
+	strbuf_addbuf(&parent, enlistment);
+	strbuf_parent_directory(&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))
+		die(_("failed to delete enlistment directory"));
+
+	return 0;
+}
+
 static int cmd_clone(int argc, const char **argv)
 {
 	const char *branch = NULL;
@@ -694,6 +722,32 @@  static int cmd_unregister(int argc, const char **argv)
 	return unregister_dir();
 }
 
+static int cmd_delete(int argc, const char **argv)
+{
+	struct option options[] = {
+		OPT_END(),
+	};
+	const char * const usage[] = {
+		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, &enlistment);
+
+	res = delete_enlistment(&enlistment);
+	strbuf_release(&enlistment);
+
+	return res;
+}
+
 static struct {
 	const char *name;
 	int (*fn)(int, const char **);
@@ -704,6 +758,7 @@  static struct {
 	{ "unregister", cmd_unregister },
 	{ "run", cmd_run },
 	{ "reconfigure", cmd_reconfigure },
+	{ "delete", cmd_delete },
 	{ NULL, NULL},
 };
 
diff --git a/contrib/scalar/scalar.txt b/contrib/scalar/scalar.txt
index 2fa96fcabc6..6fc57707718 100644
--- a/contrib/scalar/scalar.txt
+++ b/contrib/scalar/scalar.txt
@@ -14,6 +14,7 @@  scalar register [<enlistment>]
 scalar unregister [<enlistment>]
 scalar run ( all | config | commit-graph | fetch | loose-objects | pack-files ) [<enlistment>]
 scalar reconfigure [ --all | <enlistment> ]
+scalar delete <enlistment>
 
 DESCRIPTION
 -----------
@@ -127,6 +128,13 @@  reconfigure the enlistment.
 With the `--all` option, all enlistments currently registered with Scalar
 will be reconfigured. Use this option after each Scalar upgrade.
 
+Delete
+~~~~~~
+
+delete <enlistment>::
+	This subcommand lets you delete an existing Scalar enlistment from your
+	local file system, unregistering the repository.
+
 SEE ALSO
 --------
 linkgit:git-clone[1], linkgit:git-maintenance[1].
diff --git a/contrib/scalar/t/t9099-scalar.sh b/contrib/scalar/t/t9099-scalar.sh
index 5fe7fabd0e5..7e8771d0eff 100755
--- a/contrib/scalar/t/t9099-scalar.sh
+++ b/contrib/scalar/t/t9099-scalar.sh
@@ -76,4 +76,13 @@  test_expect_success 'scalar reconfigure' '
 	test true = "$(git -C one/src config core.preloadIndex)"
 '
 
+test_expect_success 'scalar delete without enlistment shows a usage' '
+	test_expect_code 129 scalar delete
+'
+
+test_expect_success 'scalar delete with enlistment' '
+	scalar delete cloned &&
+	test_path_is_missing cloned
+'
+
 test_done