This is a generalization of my previous question, aimed at improving my understanding of what can and cannot be done internally to Git.
Suppose I have a commit A, and I want to change some arbitrary metadata, whether it is the parents, the tree, the author/committer, the author/commit date, the commit message, or the gpg signature, to produce a new commit B that shares all metadata with A except the bits I changed. Further, suppose my changes do not require creating a new tree; I am either using the original tree or substituting another existing tree.
I am currently aware of at least two ways to do this:
git checkout A
and then use git commit --amend
with various options. This is native to git and moderately flexible in terms of what can be changed. However, it is interactive and touches the working tree.git cat-file | <insert_sed_script> | git hash-object -w -t commit --stdin
as this answer to my previous question describes.The second way is semantically what I am looking for, but requires an external script that performs text manipulation on the text representation of a commit.
Is text manipulation the only current option for transforming commit objects non-interactively, or is there a more structured and semantic way to manipulate the commit metadata?
parent
and then insert new lines that start with parent", can I write a git command that says "set the parents of this commit to X,Y,Z" and output the commit with that change? (git replace can handle this case, but not the general case.)Part of the motivation for asking this is intellectual curiosity; the other part is that I am considering building such tooling externally (by wrapping the text manipulation) if it does not already exist.
git commit-tree
creates commit objects from a tree. The commit message can be provided via stdin or -m
option. -S
/--gpg-sign
allows to sign the new commit object. Set GIT_AUTHOR_DATE
and GIT_COMMITTER_DATE
to specify the author and committer time, respectively.
Example:
GIT_AUTHOR_NAME='Mer Lin' GIT_AUTHOR_EMAIL='[email protected]' GIT_AUTHOR_DATE='2024-12-26T13:37:42Z' git commit-tree -p first-parent-of-the-new-commit -p optional-second-parent-of-the-new-commit -m 'This commit was created manually
Poviding multi-line commit messages
is straightforward.' -S your-gpg-key-id the-tree-id
To re-use/modify the message of an existing commit, feed it via stdin:
git show --format=%B old-commit | sed '...' | GIT_AUTHOR_NAME='Mer Lin' GIT_AUTHOR_EMAIL='[email protected]' GIT_AUTHOR_DATE='2024-12-26T13:37:42Z' git commit-tree -p first-parent-of-the-new-commit -p optional-second-parent-of-the-new-commit -S your-gpg-key-id the-tree-id
For instance, to append "Merry Christmas!" to your commit's message:
{
git show --format=%B old-commit;
echo 'Merry Christmas!';
} | git commit-tree -p old-commit^ old-commit^{tree}
If you need to copy over committer/author date/name/email from the original commit, populate the environment variables from the info obtained from the original commit:
GIT_AUTHOR_EMAIL="$(git show --format=%ae old-commit)" \
GIT_AUTHOR_NAME="$(git show --format=%an old-commit)" \
GIT_AUTHOR_DATE="$(git show --format=%aI old-commit)" \
GIT_COMMITTER_EMAIL="$(git show --format=%ce old-commit)" \
GIT_COMMITTER_NAME="$(git show --format=%cn old-commit)" \
GIT_COMMITTER_DATE="$(git show --format=%cI old-commit)" \
git commit-tree -p the-parent-id -m 'commit message' the-tree-id