git commit command: message amend, delete, cancel, push, options
This article will cover almost everything related to the git commit
command. We'll cover how to edit, delete and abort commit messages, abort after push, and other useful options.
1. A closer look at the git commit command
After creating a new repo with the git init command and staging the desired changes with the git add command, it's time to commit.
The git commit
command is responsible for registering all the changes that are currently being staged as new history in the current repo.
Since this is such a core part of the Git system, it's worth getting a little more specific.
Let's look at it in more detail, step by step
- When you type the
git commit
command, Git checks to see if there are any staged changes in the repo. If there are no staged changes, it will print the following message.
$ git commit
On branch main
nothing to commit, working tree clean
-
after confirming the existence of the change, Git creates a new commit object and assigns a SHA-1 hash value to that commit. As we saw in the git add post, this object is responsible for storing all the data associated with a single commit.
-
metadata values such as author, commiter, and commit message are added to the commit object.
-
The commit object also stores a reference to a tree object that represents the file system at the time of the commit. The Tree object points to the Blob object, which holds the actual file contents. In this way, the commit object has a known snapshot of the current state.
-
the most recent commit in the current working branch is stored as the parent commit value of the new commit object. This ensures that the newly created commit is registered on the current branch.
-
Finally, the
HEAD
that previously pointed to the most recent commit is changed to point to the newly added commit, making it the last commit in the current branch.
In this way, all changes to the staged state are recorded as part of the history of that repo. It's a simple command, but there's a lot going on under the hood.
Next, we'll see how to change a commit message that has already been committed.
2. How to edit a commit message
Git is a system that places a lot of importance on commits once they're made. This is especially true for commits that have completed a push. This is because a single commit can break synchronization with many team members.
That said, we're all human, and we all make mistakes. If you make a typo in your commit message, you can quickly fix it and notify your team. Let's see how to do that.
First, the command to edit the last commit in the current branch.
$ git commit --amend -m "new commit message"
Let's see the actual execution result
The commit message 2.md created
has been changed to 2.md created!!!
.
But here's something you need to know.
The two commits in the same location have different hash values.
Before it was changed, it was 7d16880
, and after it was changed, it was ff55e3d
.
Although it looked like a fix, Git had internally removed the old commit and registered a new one with the same changes and a new message.
This could potentially cause a conflict with team members who started new work with the old commit. To avoid this, if you change the commit message, you should notify the team the same way you would for a new commit.
2.1. What if you've already committed an existing commit?
If you edit an existing commit locally with the --amend
option, and the commit has already been pushed before the edit, there will be a difference between the local repo and the remote repo.
In this case you will not be able to push the local repo at that time. The idea is to prevent the remote repo from messing up your commits in advance.
In this case, you'll get an error message like the one below.
$ git push origin main
! [rejected] your-branch -> your-branch (non-fast-forward)
error: failed to push some refs to 'your-remote-repository-url'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
There are two ways to resolve this situation.
2.2. force-push solution
The first method is to override the rejection of the remote repo and force the current local repo to push. I If you're running a solo project, or if everyone on your team is aware of the current problem, this can be easy.
It only takes one command
$ git push -f
or
$ git push --force
However, in all other situations, such as when a new commit is registered to a remote repo, or when a team member is working on an existing commit, it has the potential to mess up the commit history, which can be quite annoying.
So use this method only in situations where it's really safe to do so.
2.3. Pull and merge workaround
The second method is somewhat safer. It involves pulling a history of existing commits from a remote repo, and then merging or rebasing it with your local commit history. You can then push back, just as you would with a normal merge.
This method is secure, but it does require some extra work. If you're still collaborating, the second method is the way to go.
For more information on the pull, merge, and rebase commands, see the corresponding articles.
3. How to undo a commit
Here's how to undo a commit, which will make the existing commit disappear, but the changes registered in the existing commit will come back and be modified and unstaged.
The command is as follows
$ git reset HEAD~1
Let's see it in action
First we replaced the contents of 2.md
with new text, followed by add
, then commit
.
After completing the commit, I checked with the git status
command and there were no more changes (nothing to commit
).
I then ran git reset HEAD~1
to undo the commit, and the changes in the 2.md
file reverted to the unstaged state. (unstaged changes after reset
)
You can also see that the 2.md
file is in the unstaged state in the status checked after undoing the commit.
Finally, run git log{:shell}:
and you will see that the 1ac689b
commit we logged above is gone.
This is how to undo a commit without losing all your changes.
4. How to delete a commit
Here's how to delete a commit, which deletes the existing commit along with the changes registered against it. It can be used instead of the cancel method in Section 3 if the changes are not needed.
The command is as follows
$ git reset --hard HEAD~1
Let's see it in action
If you check with git status
, there are currently no staged changes. Stage them using the git add
command, then complete the commit. If you check the logs, you'll see that the commit of the new hash c2072b8
has been registered.
Now issue git reset ---hard HEAD~1
to remove the last commit. No changes came back, and looking at the history, the c2072b8
commit was successfully deleted.
5. Undoing a push-completed commit
We saw this before in Section 2.1, but it is not easy to undo or delete a commit you pushed to a remote repo using the git reset
command.
This is because the commit history of the local repo and the remote repo are different, and the remote repo will reject the push.
One way to work around this is the force-push method in Section 2.2, but I mentioned that this is not recommended. Instead of this method, Git provides a command to explicitly undo the old commit and register a new one.
The commands are
$ git revert HEAD --no-edit
This command creates a new commit that reverts all changes from the previous commit. This could be done manually by the developer, but developers are human, and there is a chance they might miss a single line. Therefore, we recommend using the above command to do it for you.
Let's take a look at the actual execution
This is the status of the current new commit.
Let's register a new commit that explicitly reverts the one we just registered.
A new commit has been created, and a message has automatically been written to say that we have reverted an existing commit. If you don't specify the --no-edit
option,
it will open a plain text editor like `vi' where you can write the commit message you want.
6. Other options to make development easier
The -m
and --amend
options of the git commit
command have been covered in the example code so far. Here are some other options to consider.
6.1. The -a option without the git add -u command
The git commit -a
option will automatically stage and commit all changes without using the git add
command.
Note that this excludes files in the untracked state. Use it like this
$ git commit -a -m "commit without add command"
6.2. The --allow-empty option for empty commits
Sometimes you want to leave a bunch of commits with no new changes. This option allows you to leave commits when no changes have been made.
Use it like this
$ git commit --allow-empty -m "No changes"
6.3. Using the --no-verify option to override pre-commit and commit-msg hooks
Git hooks are event triggers that execute a defined script when a certain event occurs. The pre-commit hook
and commit-msg hook
are triggered when a new commit is registered.
The --no-verify
option is a way to ignore these hooks and create a new commit. Use it as follows
$ git commit --no-verify -m "This Commit ignore hooks"
7. Conclusion
In this article we've covered just about everything there is to know about the git commit
command.
I believe that knowing how to commit is just as important as knowing how to modify and delete commits.
I'll be writing more in-depth posts on the git reset
and git revert
commands we covered in this post.
