def do_remove(
        project: Project,
        selection: GroupSelection,
        sync: bool = True,
        packages: Collection[str] = (),
        no_editable: bool = False,
        no_self: bool = False,
        dry_run: bool = False,
        fail_fast: bool = False,
        hooks: HookManager | None = None,
    ) -> None:
        """Remove packages from working set and pyproject.toml"""

        from pdm.cli.actions import do_lock, do_sync
        from pdm.cli.utils import check_project_file
        from pdm.models.requirements import parse_requirement
        from pdm.utils import cd

        hooks = hooks or HookManager(project)
        check_project_file(project)
        if not packages:
            raise PdmUsageError("Must specify at least one package to remove.")
        group = selection.one()
        lock_groups = project.lockfile.groups

        deps, setter = project.use_pyproject_dependencies(group, selection.dev or False)
        project.core.ui.echo(
            f"Removing {'[bold]global[/] ' if project.is_global else ''}packages from [primary]{group}[/] "
            f"{'dev-' if selection.dev else ''}dependencies: " + ", ".join(f"[req]{name}[/]" for name in packages)
        )
        tracked_names: set[str] = set()
        with cd(project.root):
            for name in packages:
                req = parse_requirement(name)
                matched_indexes = sorted((i for i, r in enumerate(deps) if req.matches(r)), reverse=True)
                if not matched_indexes:
                    raise ProjectError(f"[req]{name}[/] does not exist in [primary]{group}[/] dependencies.")
                for i in matched_indexes:
                    del deps[i]
                tracked_names.add(normalize_name(name))
        setter(deps)

        if not dry_run:
            project.pyproject.write()
        if lock_groups and group not in lock_groups:
            project.core.ui.warn(f"Group [success]{group}[/] isn't in lockfile, skipping lock.")
            return
        # It may remove the whole group, exclude it from lock groups first
        project_groups = project.iter_groups()
        if lock_groups is not None:
            lock_groups = [g for g in lock_groups if g in project_groups]
        do_lock(project, "reuse", dry_run=dry_run, tracked_names=tracked_names, hooks=hooks, groups=lock_groups)
        if sync:
            do_sync(
                project,
                selection=GroupSelection(project, default=False, groups=[group], exclude_non_existing=True),
                clean=True,
                no_editable=no_editable,
                no_self=no_self,
                dry_run=dry_run,
                fail_fast=fail_fast,
                hooks=hooks,
            )
Remove packages from working set and pyproject.toml
src/pdm/cli/commands/remove.py