diff --git a/revup/amend.py b/revup/amend.py index 1e891f5..3aea807 100644 --- a/revup/amend.py +++ b/revup/amend.py @@ -211,7 +211,8 @@ async def rebuild_stack_last_touched( amended.author_date = commit_obj.author_date amended.committer_name = commit_obj.committer_name amended.committer_email = commit_obj.committer_email - amended.committer_date = commit_obj.committer_date + # Refresh committer date, like git commit --amend. + amended.committer_date = "" amended.commit_msg = commit_obj.commit_msg new_commit = await git_ctx.commit_tree(amended) else: @@ -319,6 +320,9 @@ async def get_has_unstaged() -> bool: if len(stack) == 0: raise RevupUsageException(f"Couldn't find any commits between HEAD and {commit}~") + # Refresh the amended commit's committer date, like git commit --amend. + stack[0].committer_date = "" + if args.insert: # Create a new empty commit after the given commit stack[0].parents = [stack[0].commit_id] diff --git a/tests/git_env.py b/tests/git_env.py index f520fc9..630dd6f 100644 --- a/tests/git_env.py +++ b/tests/git_env.py @@ -97,7 +97,7 @@ async def write_file(self, name, content): async def read_file(self, name): return (self.tmp_dir / name).read_text() - async def commit(self, message, files=None): + async def commit(self, message, files=None, committer_date=None): if files: for name, content in files.items(): await self.write_file(name, content) @@ -105,7 +105,7 @@ async def commit(self, message, files=None): ts = self._next_timestamp() env = { "GIT_AUTHOR_DATE": ts, - "GIT_COMMITTER_DATE": ts, + "GIT_COMMITTER_DATE": committer_date if committer_date is not None else ts, } await self.git_ctx.git("commit", "-m", message, "--allow-empty", env=env) self.commit_count += 1 @@ -121,6 +121,12 @@ async def get_commit_message(self, ref="HEAD"): async def get_commit_hash(self, ref="HEAD"): return GitCommitHash(await self.git_ctx.git_stdout("rev-parse", ref)) + async def get_author_date(self, ref="HEAD"): + return await self.git_ctx.git_stdout("log", "-1", "--format=%at", ref) + + async def get_committer_date(self, ref="HEAD"): + return await self.git_ctx.git_stdout("log", "-1", "--format=%ct", ref) + async def get_file_at_commit(self, name, ref="HEAD"): return await self.git_ctx.git_stdout("show", f"{ref}:{name}") diff --git a/tests/test_amend.py b/tests/test_amend.py index 62da8ea..8052260 100644 --- a/tests/test_amend.py +++ b/tests/test_amend.py @@ -645,6 +645,61 @@ async def test_amend_changes_commit_hashes(self): assert new_head != orig_head +class TestAmendCommitterDate: + @async_test + async def test_amend_refreshes_committer_date_preserves_author(self): + async with GitTestEnvironment() as env: + await env.commit("root", {"root.txt": "r"}) + # Epoch 0 so any refresh is strictly greater + await env.commit("first", {"a.txt": "a"}, committer_date="@0 +0000") + orig_author = await env.get_author_date() + + await env.stage_file("a.txt", "modified") + args = make_amend_args(edit=False) + ret = await amend.main(args, env.git_ctx) + + assert ret == 0 + assert await env.get_author_date() == orig_author + assert int(await env.get_committer_date()) > 0 + + @async_test + async def test_amend_earlier_commit_refreshes_committer_date(self): + async with GitTestEnvironment() as env: + await env.commit("root", {"root.txt": "r"}) + await env.commit("first", {"a.txt": "a"}, committer_date="@0 +0000") + await env.commit("second", {"b.txt": "b"}) + orig_author = await env.get_author_date("HEAD~1") + top_committer = await env.get_committer_date("HEAD") + + await env.stage_file("a.txt", "modified") + args = make_amend_args(ref_or_topic="HEAD~1", edit=False) + ret = await amend.main(args, env.git_ctx) + + assert ret == 0 + assert await env.get_author_date("HEAD~1") == orig_author + assert int(await env.get_committer_date("HEAD~1")) > 0 + # Recreated commit keeps its date + assert await env.get_committer_date("HEAD") == top_committer + + @async_test + async def test_last_touched_refreshes_committer_date(self): + async with GitTestEnvironment() as env: + await env.commit("root", {"root.txt": "r"}) + await env.git_ctx.git("branch", "origin/main", "HEAD") + # Old but valid timestamp; the refresh must move committer date past it. + await env.commit( + "first\n\nTopic: alpha", {"a.txt": "v1"}, committer_date="@1000000000 +0000" + ) + orig_author = await env.get_author_date("HEAD") + + await env.stage_file("a.txt", "v2") + args = make_amend_args(last_touched=True, parse_topics=True) + await amend.main(args, env.git_ctx) + + assert await env.get_author_date("HEAD") == orig_author + assert int(await env.get_committer_date("HEAD")) > 1000000000 + + class TestAmendSingleCommit: @async_test async def test_amend_only_commit_above_root(self):