diff --git a/docs/reset.md b/docs/reset.md new file mode 100644 index 0000000..dec36eb --- /dev/null +++ b/docs/reset.md @@ -0,0 +1,37 @@ +# NAME + +reset - reset current branch to upstream + +# SYNOPSIS + +**revup reset** [**--help**] + +# DESCRIPTION + +The `revup reset` command performs a hard reset of the current branch to match its upstream tracking branch. This is equivalent to running `git reset --hard @{u}`. + +This command is useful when you want to discard all local changes and commits on the current branch and sync it exactly with the upstream version. + +# OPTIONS + +**--help**, **-h** +: Show help message and exit + +# EXAMPLES + +Reset the current branch to match upstream: + +``` +revup reset +``` + +# NOTES + +- The command will fail if the current branch doesn't have an upstream tracking branch configured +- This operation discards all local changes and commits, so use with caution +- All uncommitted or unpushed changes will be lost permanently +- The working directory will be updated to match the upstream state + +# SEE ALSO + +**revup-upload**(1), **revup-restack**(1), **revup-amend**(1) diff --git a/revup/git.py b/revup/git.py index 9bf049b..b56a70a 100644 --- a/revup/git.py +++ b/revup/git.py @@ -796,6 +796,13 @@ async def soft_reset(self, new_commit: GitCommitHash, env: Dict) -> None: # TODO: only strictly needs to drop entries for HEAD self.clear_cache() + async def hard_reset(self, new_commit: str, env: Optional[Dict[str, str]] = None) -> None: + """ + Perform a hard reset to the given commit. + """ + await self.git("reset", "--hard", new_commit, env=env or {}) + self.clear_cache() + async def credential(self, **kwargs: str) -> str: cred = Credential(self, description=kwargs) await cred.fill() diff --git a/revup/reset.py b/revup/reset.py new file mode 100644 index 0000000..afd6bf6 --- /dev/null +++ b/revup/reset.py @@ -0,0 +1,33 @@ +import argparse + +from rich import get_console + +from revup import git + + +async def main(_: argparse.Namespace, git_ctx: git.Git) -> int: + """ + Handles the "reset" command. + Resets the current branch to match the upstream tracking branch. + """ + # Get the current branch + current_branch = await git_ctx.git_stdout("branch", "--show-current") + if not current_branch: + raise RuntimeError("Not on a branch") + + # Get the upstream tracking branch + upstream_ref = "@{u}" + + with get_console().status(f"Resetting {current_branch} to {upstream_ref}..."): + try: + # Verify the upstream exists + await git_ctx.git_stdout("rev-parse", "--verify", upstream_ref) + + # Perform the hard reset + await git_ctx.hard_reset(upstream_ref) + + print(f"Successfully reset {current_branch} to {upstream_ref}") + return 0 + except RuntimeError as e: + print(f"Error: {str(e)}") + return 1 diff --git a/revup/revup.py b/revup/revup.py index eac4415..b36ef36 100755 --- a/revup/revup.py +++ b/revup/revup.py @@ -200,6 +200,10 @@ async def main() -> int: "cherry-pick", add_help=False, ) + reset_parser = subparsers.add_parser( + "reset", + add_help=False, + ) amend_parser = subparsers.add_parser("amend", aliases=["commit"], add_help=False) config_parser = subparsers.add_parser( "config", @@ -214,6 +218,7 @@ async def main() -> int: revup_parser, amend_parser, cherry_pick_parser, + reset_parser, restack_parser, upload_parser, ] @@ -267,6 +272,8 @@ async def main() -> int: cherry_pick_parser.add_argument("branch", nargs=1) cherry_pick_parser.add_argument("--base-branch", "-b") + reset_parser.add_argument("--help", "-h", action=HelpAction, nargs=0) + config_parser.add_argument("--help", "-h", action=HelpAction, nargs=0) config_parser.add_argument("flag", nargs=1) config_parser.add_argument("value", nargs="?") @@ -389,6 +396,11 @@ async def main() -> int: return await restack.main(args=args, git_ctx=git_ctx) + elif args.cmd == "reset": + from revup import reset + + return await reset.main(args, git_ctx) + async with github_connection(args=args, git_ctx=git_ctx, conf=conf) as ( github_ep, repo_info,