diff --git a/heptapod/testhelpers.py b/heptapod/testhelpers.py
index 79131e35692049ddfe5f3f1a84e0fa5ea6a5154d_aGVwdGFwb2QvdGVzdGhlbHBlcnMucHk=..62c5b7e44fa56e3dde30d8ac1bf691a41abb9d36_aGVwdGFwb2QvdGVzdGhlbHBlcnMucHk= 100644
--- a/heptapod/testhelpers.py
+++ b/heptapod/testhelpers.py
@@ -283,6 +283,10 @@
         return self.commit((rpath, ), message=message, add_remove=True,
                            **commit_opts)
 
+    def commit_empty(self, parent=None, **commit_opts):
+        self.prepare_wdir(parent=parent)
+        return self.commit((), **commit_opts)
+
     def update_bin(self, bin_node, **opts):
         """Update to a revision specified by its node in binary form.
 
diff --git a/heptapod/tests/test_testhelpers.py b/heptapod/tests/test_testhelpers.py
index 79131e35692049ddfe5f3f1a84e0fa5ea6a5154d_aGVwdGFwb2QvdGVzdHMvdGVzdF90ZXN0aGVscGVycy5weQ==..62c5b7e44fa56e3dde30d8ac1bf691a41abb9d36_aGVwdGFwb2QvdGVzdHMvdGVzdF90ZXN0aGVscGVycy5weQ== 100644
--- a/heptapod/tests/test_testhelpers.py
+++ b/heptapod/tests/test_testhelpers.py
@@ -255,6 +255,21 @@
     assert not tmpdir.join('foo').exists()
 
 
+def test_empty_changeset(tmpdir):
+    wrapper = LocalRepoWrapper.init(tmpdir)
+    root_ctx = wrapper.write_commit('foo', content='bar')
+
+    # TODO make a final API, perhaps wrapper.make_empty_commit()
+    ctx = wrapper.commit_empty(branch='new', message='empty')
+    assert ctx.branch() == b'new'
+    assert ctx.description() == b'empty'
+
+    ctx = wrapper.commit_empty(branch='other', message='again',
+                               parent=root_ctx)
+    assert ctx.branch() == b'other'
+    assert ctx.description() == b'again'
+
+
 def test_prune_update_hidden(tmpdir):
     wrapper = LocalRepoWrapper.init(tmpdir,
                                     config=dict(extensions=dict(evolve='')))