garden-rs / garden

Garden grows and cultivates collections of Git trees ~ Official mirror of https://gitlab.com/garden-rs/garden
https://garden-rs.gitlab.io
MIT License
64 stars 9 forks source link

[wip] Feat: garden 'prune' command #4

Closed nickgarber closed 1 year ago

nickgarber commented 2 years ago

Garden scans git repos [1] under management and removes them [2] to maintain sync with config and git-remote-prune, optionally prompting to first reconcile and ultimately to confirm.

In this way garden adheres the state of the managed repos on disk, to the state of the config+repo.


[1] Apply to git-repos that were previously created by garden under a watched/managed directory, such as GARDEN_ROOT. This may recommend that Garden annotate git repos with metadata to durably signal that it's providing lifecycle mgmt.

[2] The repo is presently absent/removed from GARDEN_CONFIG or references a branch that's removed with a git remote prune.

nickgarber commented 2 years ago

An example of where this may be useful is where a worktree is removed either from a referenced remote or from Garden's config file.

davvid commented 1 year ago

This seems like it should support a -d | --depth option to keep it from traversing too deep into the filesystem.

That means having to decide on a sensible default value. If depth=0 means only stay in the current directory, then depth=1 would mean that it would be allowed to look in each subdir from the current directory and scan those for old trees.

--depth=1 seems like a good default. I can probably be convinced to default to zero, but a higher default seems like it'd be better specified via garden.yaml or on the command-line.

If we have a depth cap then we might want to have a way to specify no-limit. We could let --depth=-1 mean "no limit". That seems like that would cover the main use case.

What do you think?

nickgarber commented 1 year ago

Good thinking!

I wanted to see and reference how the find command handles this:

       -maxdepth levels
              Descend at most levels (a non-negative integer) levels of
              directories below the starting-points.  Using -maxdepth 0
              means only apply the tests and actions to the starting-
              points themselves.

       -mindepth levels
              Do not apply any tests or actions at levels less than
              levels (a non-negative integer).  Using -mindepth 1 means
              process all files except the starting-points.

Seems like it's in agreement with your proposal, which isn't mandatory but can help ergonomics.

Would it be acceptable to provide both a mindepth and maxdepth option?

davvid commented 1 year ago

It's a good idea to see what other commands are doing. The fd-find finder has these options:

OPTIONS:
    -d, --max-depth <depth>
            Limit the directory traversal to a given depth. By default, there is no
            limit on the search depth.
        --min-depth <depth>
            Only show search results starting at the given depth. See also: '--max-
            depth' and '--exact-depth'
        --exact-depth <depth>
            Only show search results at the exact given depth. This is an alias for
            '--min-depth <depth> --max-depth <depth>'.

It sounds like supporting both --min-depth and --max-depth are good ideas. --min-depth would default to zero.

davvid commented 1 year ago

An interesting note is that fd-find says, "By default, there is no limit on the search depth."

One reason why we might want garden prune to have a limit by default is that it's a destructive operation that deletes stuff.

Prompting for each removal by default sounds like a good idea too. In that case, we might also want a -y | --no-prompt option (which implies y is answered for each prompt).

nickgarber commented 1 year ago

That's perfect!

davvid commented 1 year ago

garden prune is now available if you build from source. I'll be tagging and releasing v0.4.0 to crates.io before the end of the year.

$ garden prune -h
Usage:
  garden prune [OPTIONS] [PATHS ...]

garden prune - Remove unreferenced Git repositories

Positional arguments:
  paths                 Limit pruning to the specified subdirectories.

Optional arguments:
  -h,--help             Show this help message and exit
  -j,--jobs JOBS        Specify the number of jobs to run concurrently.
  -d,--max-depth MAX_DEPTH
                        Limit the directory traversal to the given depth. The
                        traversal depth is unlimited by default.
  --min-depth MIN_DEPTH Only show search results starting at the given depth.
  --exact-depth EXACT_DEPTH
                        Only show search results at the exact given depth. This
                        is an alias for '--min-depth <depth> --max-depth
                        <depth>'.
  --no-prompt           Prune all repositories without prompting. (DANGEROUS!)

Have fun!

davvid commented 1 year ago

Notable details -- currently it defaults to unlimited depth. The implementation runs in parallel so it's pretty fast.

Right now it performs deletions after prompting for each item.

I think it might be better for it to not actually delete. A no-op dry-run mode would be the default and then we can pass an extra --rm flag to enable the deletion.

One use case for this is that it's useful to run the prune first to see what it reports as being deleted before actually enabling the removal with the --rm option.

I'll look at making the default behavior a dry-run mode before it gets in an officially tagged release. This is a useful but dangerous tool so I want to make it hard to accidentally misuse.

In the default garden prune no-op dry-run mode it'll print this message as it starts:

NOTE: Safe mode enabled. Repositories will not be deleted. Use `--rm` to enable deletion.
davvid commented 1 year ago

The --rm option was added and the safe mode is now enabled by default. I think this is pretty much ready for release but I'll hold off on updating crates.io to gather feedback before the official release.