diff --git a/Documentation/git-replay.txt b/Documentation/git-replay.txt
index e7551aec54..f424c1a676 100644
--- a/Documentation/git-replay.txt
+++ b/Documentation/git-replay.txt
@@ -42,6 +42,10 @@ When `--advance` is specified, the update-ref command(s) in the output
 will update the branch passed as an argument to `--advance` to point at
 the new commits (in other words, this mimics a cherry-pick operation).
 
+--write-pack::
+	Write any new objects into a separate packfile instead of as
+	individual loose objects.
+
 <revision-range>::
 	Range of commits to replay. More than one <revision-range> can
 	be passed, but in `--advance <branch>` mode, they should have
diff --git a/builtin/replay.c b/builtin/replay.c
index c3d53ff0cd..72b7b7f43a 100644
--- a/builtin/replay.c
+++ b/builtin/replay.c
@@ -17,6 +17,7 @@
 #include "strmap.h"
 #include <oidset.h>
 #include <tree.h>
+#include "tmp-objdir.h"
 
 static const char *short_commit_name(struct commit *commit)
 {
@@ -272,6 +273,7 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
 	struct commit *onto = NULL;
 	const char *onto_name = NULL;
 	int contained = 0;
+	int write_pack = 0;
 
 	struct rev_info revs;
 	struct commit *last_commit = NULL;
@@ -279,6 +281,7 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
 	struct merge_options merge_opt;
 	struct merge_result result;
 	struct strset *update_refs = NULL;
+	struct tmp_objdir *tmp_objdir = NULL;
 	kh_oid_map_t *replayed_commits;
 	int i, ret = 0;
 
@@ -296,6 +299,8 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
 			   N_("replay onto given commit")),
 		OPT_BOOL(0, "contained", &contained,
 			 N_("advance all branches contained in revision-range")),
+		OPT_BOOL(0, "write-pack", &write_pack,
+			 N_("write new objects to a pack instead of as loose")),
 		OPT_END()
 	};
 
@@ -352,8 +357,15 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
 	init_merge_options(&merge_opt, the_repository);
 	memset(&result, 0, sizeof(result));
 	merge_opt.show_rename_progress = 0;
+	merge_opt.write_pack = write_pack;
 	last_commit = onto;
 	replayed_commits = kh_init_oid_map();
+
+	if (merge_opt.write_pack) {
+		tmp_objdir = tmp_objdir_create("replay");
+		tmp_objdir_replace_primary_odb(tmp_objdir, 0);
+	}
+
 	while ((commit = get_revision(&revs))) {
 		const struct name_decoration *decoration;
 		khint_t pos;
@@ -417,5 +429,11 @@ int cmd_replay(int argc, const char **argv, const char *prefix)
 	/* Return */
 	if (ret < 0)
 		exit(128);
+	if (ret && tmp_objdir) {
+		if (tmp_objdir_repack(tmp_objdir) < 0)
+			ret = 0;
+		else if (tmp_objdir_migrate(tmp_objdir) < 0)
+			ret = 0;
+	}
 	return ret ? 0 : 1;
 }
diff --git a/t/t3650-replay-basics.sh b/t/t3650-replay-basics.sh
index 389670262e..e7048748c2 100755
--- a/t/t3650-replay-basics.sh
+++ b/t/t3650-replay-basics.sh
@@ -67,6 +67,43 @@ test_expect_success 'using replay to rebase two branches, one on top of other' '
 	test_cmp expect result
 '
 
+packdir=.git/objects/pack
+
+test_expect_success 'using replay to rebase two branches, with --write-pack' '
+	# Remove the results of the previous rebase, ensuring that they
+	# are pruned from the object store.
+	git gc --prune=now &&
+	test_must_fail git cat-file -t "$(cut -d " " -f 3 expect)" &&
+
+	# Create an extra packfile to ensure that the tmp-objdir repack
+	# takes place outside of the main object store.
+	git checkout --detach &&
+	test_commit unreachable &&
+	git repack -d &&
+	git checkout main &&
+
+	find $packdir -type f -name "*.idx" | sort >packs.before &&
+	git replay --write-pack --onto main topic1..topic2 >result &&
+	find $packdir -type f -name "*.idx" | sort >packs.after &&
+
+	comm -13 packs.before packs.after >packs.new &&
+
+	# Ensure that we got a single new pack.
+	test_line_count = 1 result &&
+	test_line_count = 1 packs.new &&
+
+	# ... and that the rest of the results match our expeectations.
+	git log --format=%s $(cut -f 3 -d " " result) >actual &&
+	test_write_lines E D M L B A >expect &&
+	test_cmp expect actual &&
+
+	printf "update refs/heads/topic2 " >expect &&
+	printf "%s " $(cut -f 3 -d " " result) >>expect &&
+	git rev-parse topic2 >>expect &&
+
+	test_cmp expect result
+'
+
 test_expect_success 'using replay on bare repo to rebase two branches, one on top of other' '
 	git -C bare replay --onto main topic1..topic2 >result-bare &&
 	test_cmp expect result-bare
