From 189dacb500d165d6d1cb24304c4f8585157086ad Mon Sep 17 00:00:00 2001 From: Jerry Zhang Date: Fri, 19 Jun 2026 13:40:48 -0700 Subject: [PATCH] amend: Refresh committer date Previously amend would keep all timestamps the same. However we want to refresh just the commit timestamp on any commits being modified. Commits that are simply recreated on top do not have their timestamps modified. --- revup/amend.py | 6 ++++- tests/git_env.py | 10 +++++++-- tests/test_amend.py | 55 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 3 deletions(-) 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):