Search code examples
gitgithuboctokit-js

Octokit/git add empty commit to remote branch?


I am trying to make an empty commit to a remote branch. I do no want to make the change locally and then push it up. I do not want to have to make any changes to my local working tree.

Here is what I have:

  const tree = await octokit.rest.git.getTree({
    owner: GITHUB_OWNER,
    repo: GITHUB_REPO,
    tree_sha: "myBranch",
  })
  const commitObject = await octokit.rest.git.createCommit({
    owner: GITHUB_OWNER,
    repo: GITHUB_REPO,
    message: "my commit message",
    tree: tree.data.sha,
  })

Basically, I am trying to get the tree of the branch myBranch and then use that tree to create a new commit (no changes) just with a commit message.

I am currently getting an error:

RequestError [HttpError]: Tree SHA is not a tree object

If I was going to do this locally I would do this:

git co -b myBranch origin/myBranch
git commit --allow-empty -m "my commit message"
git push origin myBranch:myBranch

However this involves messing with my local working tree, I want this to be a script that I can run without worrying about what branch or changes are are in my local working tree.

If there is a way to acheive this with raw git, without using octokit that is fine too.


Solution

  • You can absolutely do this with raw Git, but you need to do some additional scripting. Git provides plumbing commands to update the index, create trees, and create commits.

    The script below takes two arguments, a branch name and a commit, and creates an empty commit on the given branch which has as its parent the previous commit. No changes are made to the local working tree, and this will work even in a bare repository.

    You can also modify the script as indicated to modify the created commit using tooling like git hash-object (to add files into the repository) and git update-index (to add those to the index). A temporary index file is used to avoid changing the main one in the repository.

    The temporary directory and the index are only used if you actually want to make changes to the commit; you can otherwise omit them if you're not actually modifying the contents of the commit.

    #!/bin/sh -e
    
    tempdir=$(mktemp -d)
    trap 'rm -fr "$tempdir"' EXIT
    
    COMMIT="${2:-HEAD}"
    
    export GIT_INDEX_FILE="$tempdir/index"
    
    # To make changes to the commit:
    # git read-tree "$COMMIT"
    # Make any changes here, such as with `git update-index`.
    # tree=$(git write-tree)
    
    # To use the same contents as the previous commit:
    tree=$(git rev-parse --verify "$COMMIT^{tree}")
    
    # Either way:
    commit=$(git commit-tree -m "commit message" -p "$COMMIT" "$tree")
    git update-ref "refs/heads/$1" "$commit"
    

    I will, however, add that except for very limited circumstances, you typically don't want to create an empty commit. Whatever your goal is, you should typically investigate other alternatives first.