diff --git a/lib/util/git/index.spec.ts b/lib/util/git/index.spec.ts
index 7387019ab7ac911b9a29e1a12fb11822a1dfe649..e3f76563782a3121223a0aa5f88446183011180c 100644
--- a/lib/util/git/index.spec.ts
+++ b/lib/util/git/index.spec.ts
@@ -753,4 +753,29 @@ describe('util/git/index', () => {
       });
     });
   });
+
+  describe('pushCommitAsRef', () => {
+    it('creates non-branch ref', async () => {
+      const commit = git.getBranchCommit('develop');
+      await git.pushCommitAsRef(commit, 'refs/foo/bar');
+      const repo = Git(tmpDir.path);
+      const res = (await repo.raw(['ls-remote'])).split(/\s+/);
+      expect(res).toContain('refs/foo/bar');
+    });
+  });
+
+  describe('listCommitTree', () => {
+    it('creates non-branch ref', async () => {
+      const commit = git.getBranchCommit('develop');
+      const res = await git.listCommitTree(commit);
+      expect(res).toEqual([
+        {
+          mode: '100644',
+          path: 'past_file',
+          sha: '913705ab2ca79368053a476efa48aa6912d052c5',
+          type: 'blob',
+        },
+      ]);
+    });
+  });
 });
diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts
index 056aa958914e3a98ca4f95d80b977f10b32d61e2..18e7c52c733dd11ae844d3f5050aeb75ab9bbd93 100644
--- a/lib/util/git/index.ts
+++ b/lib/util/git/index.ts
@@ -40,6 +40,7 @@ import type {
   LocalConfig,
   StatusResult,
   StorageConfig,
+  TreeItem,
 } from './types';
 
 export { setNoVerify } from './config';
@@ -704,11 +705,12 @@ export async function prepareCommit({
 }: CommitFilesConfig): Promise<CommitResult | null> {
   const { localDir } = GlobalConfig.get();
   await syncGit();
-  logger.debug(`Preparing files for commiting to branch ${branchName}`);
+  logger.debug(`Preparing files for committing to branch ${branchName}`);
   await handleCommitAuth(localDir);
   try {
     await git.reset(ResetMode.HARD);
     await git.raw(['clean', '-fd']);
+    const parentCommitSha = config.currentBranchSha;
     await git.checkout(['-B', branchName, 'origin/' + config.currentBranch]);
     const deletedFiles: string[] = [];
     const addedModifiedFiles: string[] = [];
@@ -787,7 +789,7 @@ export async function prepareCommit({
       { deletedFiles, ignoredFiles, result: commitRes },
       `git commit`
     );
-    const commit = commitRes?.commit || 'unknown';
+    const commitSha = commitRes?.commit || 'unknown';
     if (!force && !(await hasDiff(`origin/${branchName}`))) {
       logger.debug(
         { branchName, deletedFiles, addedModifiedFiles, ignoredFiles },
@@ -797,7 +799,8 @@ export async function prepareCommit({
     }
 
     const result: CommitResult = {
-      sha: commit,
+      parentCommitSha,
+      commitSha,
       files: files.filter((fileChange) => {
         if (fileChange.type === 'deletion') {
           return deletedFiles.includes(fileChange.path);
@@ -898,3 +901,48 @@ export function getUrl({
     pathname: repository + '.git',
   });
 }
+
+export async function pushCommitAsRef(
+  commitSha: string,
+  refName: string
+): Promise<void> {
+  await git.raw(['update-ref', refName, commitSha]);
+  await git.raw(['push', '--force', 'origin', refName]);
+}
+
+const treeItemRegex = regEx(
+  /^(?<mode>\d{6})\s+(?<type>blob|tree)\s+(?<sha>[0-9a-f]{40})\s+(?<path>.*)$/
+);
+
+const treeShaRegex = regEx(/tree\s+(?<treeSha>[0-9a-f]{40})\s*/);
+
+/**
+ *
+ * $ git cat-file -p <commit-sha>
+ *
+ * > tree <tree-sha>
+ * > parent 59b8b0e79319b7dc38f7a29d618628f3b44c2fd7
+ * > ...
+ *
+ * $ git cat-file -p <tree-sha>
+ *
+ * > 040000 tree 389400684d1f004960addc752be13097fe85d776    .devcontainer
+ * > 100644 blob 7d2edde437ad4e7bceb70dbfe70e93350d99c98b    .editorconfig
+ * > ...
+ *
+ */
+export async function listCommitTree(commitSha: string): Promise<TreeItem[]> {
+  const commitOutput = await git.raw(['cat-file', '-p', commitSha]);
+  const { treeSha } = treeShaRegex.exec(commitOutput)?.groups ?? {};
+  const contents = await git.raw(['cat-file', '-p', treeSha]);
+  const lines = contents.split(newlineRegex);
+  const result: TreeItem[] = [];
+  for (const line of lines) {
+    const matchGroups = treeItemRegex.exec(line)?.groups;
+    if (matchGroups) {
+      const { path, mode, type, sha } = matchGroups;
+      result.push({ path, mode, type, sha });
+    }
+  }
+  return result;
+}
diff --git a/lib/util/git/types.ts b/lib/util/git/types.ts
index a84f56e0e9c2ee2121a4ceb833350ce35b1abfeb..c09dce923ef3939dda34b7b9225f39a7cb1678f6 100644
--- a/lib/util/git/types.ts
+++ b/lib/util/git/types.ts
@@ -93,10 +93,18 @@ export interface SourceBranchConflict {
 }
 
 export interface CommitResult {
-  sha: string;
+  parentCommitSha: string;
+  commitSha: string;
   files: FileChange[];
 }
 
+export interface TreeItem {
+  path: string;
+  mode: string;
+  type: string;
+  sha: string;
+}
+
 /**
  * Represents a git authentication rule in the form of e.g.:
  * git config --global url."https://api@github.com/".insteadOf "https://github.com/"