I have the following commit history:
HEAD
HEAD~
HEAD~2
HEAD~3
git commit --amend
modifies the current HEAD
commit. But how do I modify HEAD~3
?
Use git rebase
. For example, to modify commit bbc643cd
, run:
git rebase --interactive bbc643cd~
Please note the tilde ~
at the end of the command, because you need to reapply commits on top of the previous commit of bbc643cd
(i.e. bbc643cd~
).
In the default editor, modify pick
to edit
in the line mentioning bbc643cd
.
Save the file and exit. git will interpret and automatically execute the commands in the file. You will find yourself in the previous situation in which you just had created commit bbc643cd
.
At this point, bbc643cd
is your last commit and you can easily amend it. Make your changes and then commit them with the command:
git commit --all --amend --no-edit
After that, return back to the previous HEAD commit using:
git rebase --continue
WARNING: Note that this will change the SHA-1 of that commit as well as all children -- in other words, this rewrites the history from that point forward. You can break repos doing this if you push using the command git push --force
.
Answered 2023-09-20 20:20:25
reword
action in git rebase -i
instead of edit
(it automatically opens the editor and continues with the rest of the rebase steps; this obviates the use of git commit --ammend
and git rebase --continue
when you only need to change the commit message and not the content). - anyone git stash
before git rebase
and git stash pop
afterwards, if you have pending changes. - anyone git commit --all --amend --no-edit
here. All I had to do after git rebase -i ...
was to git commit --amend
normally then git rebase --continue
. - anyone git rebase -i @~9 # Show the last 9 commits in a text editor
Find the commit you want, change pick
to e
(edit
), and save and close the file. Git will rewind to that commit, allowing you to either:
git commit --amend
to make changes, orgit reset @~
to discard the last commit, but not the changes to the files (i.e. take you to the point you were at when you'd edited the files, but hadn't committed yet).The latter is useful for doing more complex stuff like splitting into multiple commits.
Then, run git rebase --continue
, and Git will replay the subsequent changes on top of your modified commit. You may be asked to fix some merge conflicts.
Note: @
is shorthand for HEAD
, and ~
is the commit before the specified commit.
Read more about rewriting history in the Git docs.
ProTip™: Don't be afraid to experiment with "dangerous" commands that rewrite history* — Git doesn't delete your commits for 90 days by default; you can find them in the reflog:
$ git reset @~3 # go back 3 commits
$ git reflog
c4f708b HEAD@{0}: reset: moving to @~3
2c52489 HEAD@{1}: commit: more changes
4a5246d HEAD@{2}: commit: make important changes
e8571e4 HEAD@{3}: commit: make some changes
... earlier commits ...
$ git reset 2c52489
... and you're back where you started
* Watch out for options like --hard
and --force
though — they can discard data.
* Also, don't rewrite history on any branches you're collaborating on.
On many systems, git rebase -i
will open up Vim by default. Vim doesn't work like most modern text editors, so take a look at how to rebase using Vim. If you'd rather use a different editor, change it with git config --global core.editor your-favorite-text-editor
.
Answered 2023-09-20 20:20:25
git reset @~
exactly what I wanted to do after choosing commit with git rebase ...
. You're my hero) - anyone git rebase -i --root
. - anyone git reset @~
to make a few updates (mostly a git checkout file-i-didn't-mean-to-change
), is there an easy way to put add that commit back with the original message? I copied the original message from the blurb output by git status
, and then ran git rebase --continue
, but I wondered if there is an easier way? Perhaps if there isn't an easier way, you could also mention this briefly in your answer? - anyone Interactive rebase with --autosquash
is something I frequently use when I need to fixup previous commits deeper in the history. It essentially speeds up the process that ZelluX's answer illustrates, and is especially handy when you have more than one commit you need to edit.
From the documentation:
--autosquash
When the commit log message begins with "squash! …" (or "fixup! …"), and there is a commit whose title begins with the same …, automatically modify the todo list of rebase -i so that the commit marked for squashing comes right after the commit to be modified
Assume you have a history that looks like this:
$ git log --graph --oneline
* b42d293 Commit3
* e8adec4 Commit2
* faaf19f Commit1
and you have changes that you want to amend to Commit2 then commit your changes using
$ git commit -m "fixup! Commit2"
alternatively you can use the commit-sha instead of the commit message, so "fixup! e8adec4
or even just a prefix of the commit message.
Then initiate an interactive rebase on the commit before
$ git rebase e8adec4^ -i --autosquash
your editor will open with the commits already correctly ordered
pick e8adec4 Commit2
fixup 54e1a99 fixup! Commit2
pick b42d293 Commit3
all you need to do is save and exit
Answered 2023-09-20 20:20:25
git commit --fixup=@~
instead of git commit -m "fixup! Commit2"
. This is especially useful when your commit messages are longer and it would be a pain to type out the whole thing. - anyone fixup = "!fn() { git commit --fixup ${1} && GIT_EDITOR=true git rebase --autosquash -i ${1}^; }; fn
-> git fixup <commitId>
amends all staged changes to the given commit - anyone "
. - anyone fixup = "!fn() { git commit -m \"fixup! ${1}\" && GIT_EDITOR=true git rebase --autosquash -i ${1}^; }; fn"
- anyone HEAD
, HEAD^
, HEAD~7
etc: fixup = "!fn() { _FIXUP_COMMIT=`git rev-parse ${1}` && git commit -m \"fixup! ${_FIXUP_COMMIT}\" && GIT_EDITOR=true git rebase --autosquash -i ${_FIXUP_COMMIT}^; }; fn"
- anyone Based on Documentation
Amending the message of older or multiple commit messages
git rebase -i HEAD~3
The above displays a list of the last 3 commits on the current branch, change 3 to something else if you want more. The list will look similar to the following:
pick e499d89 Delete CNAME
pick 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.
Replace pick with reword before each commit message you want to change. Let say you change the second commit in the list, your file will look like the following:
pick e499d89 Delete CNAME
reword 0c39034 Better README
pick f7fde4a Change the commit message but push the same commit.
Save and close the commit list file, this will pop up a new editor for you to change your commit message, change the commit message and save.
Finally, force-push the amended commits.
git push --force
Answered 2023-09-20 20:20:25
Run:
$ git rebase --interactive commit_hash^
each ^
indicates how many commits back you want to edit, if it's only one (the commit hash that you specified), then you just add one ^
.
Using Vim you change the words pick
to reword
for the commits you want to change, save and quit(:wq
). Then git will prompt you with each commit that you marked as reword so you can change the commit message.
Each commit message you have to save and quit(:wq
) to go to the next commit message
If you want to exit without applying the changes, press :q!
EDIT: to navigate in vim
you use j
to go up, k
to go down, h
to go left, and l
to go right( all this in NORMAL
mode, press ESC
to go to NORMAL
mode ).
To edit a text, press i
so that you enter the INSERT
mode, where you insert text.
Press ESC
to go back to NORMAL
mode :)
UPDATE: Here's a great link from github listing How to undo (almost) anything with git
Answered 2023-09-20 20:20:25
git push --force
? - anyone git push --force
does is overwrite the remotes commits with your local commits. That's not the case of this topic :) - anyone I just thought I'd share an alias that I'm using for this. It's based on non-interactive interactive rebase. To add it to your git, run this command (explanation given below):
git config --global alias.amend-to '!f() { SHA=`git rev-parse "$1"`; git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"; }; f'
Or, a version that can also handle unstaged files (by stashing and then un-stashing them):
git config --global alias.amend-to '!f() { SHA=`git rev-parse "$1"`; git stash -k && git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^" && git stash pop; }; f'
The biggest advantage of this command is the fact that it's no-vim.
(1)given that there are no conflicts during rebase, of course
git amend-to <REV> # e.g.
git amend-to HEAD~1
git amend-to aaaa1111
The name amend-to
seems appropriate IMHO. Compare the flow with --amend
:
git add . && git commit --amend --no-edit
# vs
git add . && git amend-to <REV>
git config --global alias.<NAME> '!<COMMAND>'
- creates a global git alias named <NAME>
that will execute non-git command <COMMAND>
f() { <BODY> }; f
- an "anonymous" bash function.SHA=`git rev-parse "$1"`;
- converts the argument to git revision, and assigns the result to variable SHA
git commit --fixup "$SHA"
- fixup-commit for SHA
. See git-commit
docsGIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^"
git rebase --interactive "$SHA^"
part has been covered by other answers.--autosquash
is what's used in conjunction with git commit --fixup
, see git-rebase
docs for more infoGIT_SEQUENCE_EDITOR=true
is what makes the whole thing non-interactive. This hack I learned from this blog post.Answered 2023-09-20 20:20:25
amend-to
handle unstaged files: git config --global alias.amend-to '!f() { SHA=
git rev-parse "$1"; git stash -k && git commit --fixup "$SHA" && GIT_SEQUENCE_EDITOR=true git rebase --interactive --autosquash "$SHA^" && git stash pop; }; f'
- anyone If for some reason you don't like interactive editors, you can use git rebase --onto
.
Say you want to modify Commit1
. First, branch from before Commit1
:
git checkout -b amending [commit before Commit1]
Second, grab Commit1
with cherry-pick
:
git cherry-pick Commit1
Now, amend your changes, creating Commit1'
:
git add ...
git commit --amend -m "new message for Commit1"
And finally, after having stashed any other changes, transplant the rest of your commits up to master
on top of your
new commit:
git rebase --onto amending Commit1 master
Read: "rebase, onto the branch amending
, all commits between Commit1
(non-inclusive) and master
(inclusive)". That is, Commit2 and Commit3, cutting the old Commit1 out entirely. You could just cherry-pick them, but this way is easier.
Remember to clean up your branches!
git branch -d amending
Answered 2023-09-20 20:20:25
git checkout -b amending Commit1~1
to get the prior commit - anyone git checkout -b amending Commit1
? - anyone git stash
+ rebase
automation
For when I need to modify an old commit a lot of times for Gerrit reviews, I've been doing:
git-amend-old() (
# Stash, apply to past commit, and rebase the current branch on to of the result.
current_branch="$(git rev-parse --abbrev-ref HEAD)"
apply_to="$1"
git stash
git checkout "$apply_to"
git stash apply
git add -u
git commit --amend --no-edit
new_sha="$(git log --format="%H" -n 1)"
git checkout "$current_branch"
git rebase --onto "$new_sha" "$apply_to"
)
Usage:
git add
if already in repogit-amend-old $old_sha
I like this over --autosquash
as it does not squash other unrelated fixups.
Answered 2023-09-20 20:20:25
git amend
to apply changes to a specific commit with the using the current stash, very clever! - anyone The best option is to use "Interactive rebase command".
The
git rebase
command is incredibly powerful. It allows you to edit commit messages, combine commits, reorder them ...etc.Every time you rebase a commit a new SHA will be created for each commit regardless of the content will be changed or not! You should be careful when to use this command cause it may have drastic implications especially if you work in collaboration with other developers. They may start working with your commit while you're rebasing some. After you force to push the commits they will be out of sync and you may find out later in a messy situation. So be careful!
It's recommended to create a
backup
branch before rebasing so whenever you find things out of control you can return back to the previous state.
git rebase -i <base>
-i
stand for "interactive". Note that you can perform a rebase in non-interactive mode. ex:
#interactivly rebase the n commits from the current position, n is a given number(2,3 ...etc)
git rebase -i HEAD~n
HEAD
indicates your current location(can be also branch name or commit SHA). The ~n
means "n beforeé, so HEAD~n
will be the list of "n" commits before the one you are currently on.
git rebase
has different command like:
p
or pick
to keep commit as it is.r
or reword
: to keep the commit's content but alter the commit message. s
or squash
: to combine this commit's changes into the previous commit(the commit above it in the list).... etc.
Note: It's better to get Git working with your code editor to make things simpler. Like for example if you use visual code you can add like this git config --global core.editor "code --wait"
. Or you can search in Google how to associate you preferred your code editor with GIT.
git rebase
I wanted to change the last 2 commits I did so I process like this:
#This to show all the commits on one line
$git log --oneline
4f3d0c8 (HEAD -> documentation) docs: Add project description and included files"
4d95e08 docs: Add created date and project title"
eaf7978 (origin/master , origin/HEAD, master) Inital commit
46a5819 Create README.md
Now I use git rebase
to change the 2 last commits messages:
$git rebase -i HEAD~2
It opens the code editor and show this:
pick 4d95e08 docs: Add created date and project title
pick 4f3d0c8 docs: Add project description and included files
# Rebase eaf7978..4f3d0c8 onto eaf7978 (2 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
...
Since I want to change the commit message for this 2 commits. So I will type r
or reword
in place of pick
. Then Save the file and close the tab.
Note that rebase
is executed in a multi-step process so the next step is to update the messages. Note also that the commits are displayed in reverse chronological order so the last commit is displayed in that one and the first commit in the first line and so forth.
Update the messages: Update the first message:
docs: Add created date and project title to the documentation "README.md"
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
...
save and close Edit the second message
docs: Add project description and included files to the documentation "README.md"
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
...
save and close.
You will get a message like this by the end of the rebase: Successfully rebased and updated refs/heads/documentation
which means that you succeed. You can display the changes:
5dff827 (HEAD -> documentation) docs: Add project description and included files to the documentation "README.md"
4585c68 docs: Add created date and project title to the documentation "README.md"
eaf7978 (origin/master, origin/HEAD, master) Inital commit
46a5819 Create README.md
I wish that may help the new users :).
Answered 2023-09-20 20:20:25
I found myself fixing a past commit frequently enough that I wrote a script for it.
Here's the workflow:
git commit-edit <commit-hash>
This will drop you at the commit you want to edit.
Fix and stage the commit as you wish it had been in the first place.
(You may want to use git stash save
to keep any files you're not committing)
Redo the commit with --amend
, eg:
git commit --amend
Complete the rebase:
git rebase --continue
For the above to work, put the below script into an executable file called git-commit-edit
somewhere in your $PATH
:
#!/bin/bash
set -euo pipefail
script_name=${0##*/}
warn () { printf '%s: %s\n' "$script_name" "$*" >&2; }
die () { warn "$@"; exit 1; }
[[ $# -ge 2 ]] && die "Expected single commit to edit. Defaults to HEAD~"
# Default to editing the parent of the most recent commit
# The most recent commit can be edited with `git commit --amend`
commit=$(git rev-parse --short "${1:-HEAD~}")
message=$(git log -1 --format='%h %s' "$commit")
if [[ $OSTYPE =~ ^darwin ]]; then
sed_inplace=(sed -Ei "")
else
sed_inplace=(sed -Ei)
fi
export GIT_SEQUENCE_EDITOR="${sed_inplace[*]} "' "s/^pick ('"$commit"' .*)/edit \\1/"'
git rebase --quiet --interactive --autostash --autosquash "$commit"~
git reset --quiet @~ "$(git rev-parse --show-toplevel)" # Reset the cache of the toplevel directory to the previous commit
git commit --quiet --amend --no-edit --allow-empty # Commit an empty commit so that that cache diffs are un-reversed
echo
echo "Editing commit: $message" >&2
echo
Answered 2023-09-20 20:20:25
Changing the Last Commit:
git commit --amend
// or
git commit --amend -m "an updated commit message"
Don’t amend public commits Amended commits are actually entirely new commits and the previous commit will no longer be on your current branch.
For example, if you want to change the last three commit messages, or any of the commit messages in that group, you supply as an argument to git rebase -i the parent of the last commit you want to edit, which is HEAD~2^ or HEAD~3. It may be easier to remember the ~3 because you’re trying to edit the last three commits, but keep in mind that you’re actually designating four commits ago, the parent of the last commit you want to edit:
$ git rebase -i HEAD~3
Answered 2023-09-20 20:20:25
Came to this approach (and it is probably exactly the same as using interactive rebase) but for me it's kind of straightforward.
Note: I present this approach for the sake of illustration of what you can do rather than an everyday alternative. Since it has many steps (and possibly some caveats.)
Say you want to change commit 0
and you are currently on feature-branch
some-commit---0---1---2---(feature-branch)HEAD
Checkout to this commit and create a quick-branch
. You can also clone your feature branch as a recovery point (before starting).
?(git checkout -b feature-branch-backup)
git checkout 0
git checkout -b quick-branch
You will now have something like this:
0(quick-branch)HEAD---1---2---(feature-branch)
Stage changes, stash everything else.
git add ./example.txt
git stash
Commit changes and checkout back to feature-branch
git commit --amend
git checkout feature-branch
You will now have something like this:
some-commit---0---1---2---(feature-branch)HEAD
\
---0'(quick-branch)
Rebase feature-branch
onto quick-branch
(resolve any conflicts along the way). Apply stash and remove quick-branch
.
git rebase quick-branch
git stash pop
git branch -D quick-branch
And you end up with:
some-commit---0'---1'---2'---HEAD(feature-branch)
Git will not duplicate (although I can't really say to what extent) the 0 commit when rebasing.
Note: all commit hashes are changed starting from the commit we originally intended to change.
Answered 2023-09-20 20:20:25
To get a non-interactive command, put a script with this content in your PATH:
#!/bin/sh
#
# git-fixup
# Use staged changes to modify a specified commit
set -e
cmt=$(git rev-parse $1)
git commit --fixup="$cmt"
GIT_EDITOR=true git rebase -i --autosquash "$cmt~1"
Use it by staging your changes (with git add
) and then run git fixup <commit-to-modify>
. Of course, it will still be interactive if you get conflicts.
Answered 2023-09-20 20:20:25
I solved this,
1) by creating new commit with changes i want..
r8gs4r commit 0
2) i know which commit i need to merge with it. which is commit 3.
so, git rebase -i HEAD~4
# 4 represents recent 4 commit (here commit 3 is in 4th place)
3) in interactive rebase recent commit will located at bottom. it will looks alike,
pick q6ade6 commit 3
pick vr43de commit 2
pick ac123d commit 1
pick r8gs4r commit 0
4) here we need to rearrange commit if you want to merge with specific one. it should be like,
parent
|_child
pick q6ade6 commit 3
f r8gs4r commit 0
pick vr43de commit 2
pick ac123d commit 1
after rearrange you need to replace p
pick
with f
(fixup will merge without commit message) or s
(squash merge with commit message can change in run time)
and then save your tree.
now merge done with existing commit.
Note: Its not preferable method unless you're maintain on your own. if you have big team size its not a acceptable method to rewrite git tree will end up in conflicts which you know other wont. if you want to maintain you tree clean with less commits can try this and if its small team otherwise its not preferable.....
Answered 2023-09-20 20:20:25
I do the following, including changing the date and time of local commits:
git rebase -i HEAD~6
~6
is the amount of commit history to display.
pick
to edit
the commits to be edited.Ctrl+O
to save and Ctrl+X
to exit)git commit --amend --date="2022-09-02T19:10:04" -m "NEW_MSG"
git rebase --continue
If there are more commits to edit, it is repeated from point 1
Finally I validate the changes and if everything is ok, I do the push
Answered 2023-09-20 20:20:25
For me it was for removing some credentials from a repo. I tried rebasing and ran into a ton of seemingly unrelated conflicts along the way when trying to rebase --continue. Don't bother attempting to rebase yourself, use the tool called BFG (brew install bfg) on mac.
Answered 2023-09-20 20:20:25
If you haven't already pushed the commits then you can go back to a previous commit using git reset HEAD^[1,2,3,4...]
For example
git commit <file1> -m "Updated files 1 and 2"
git commit <file3> -m "Updated file 3"
Oops, forgot to add file2 to the first commit...
git reset HEAD^1 // because I only need to go back 1 commit
git add <file2>
This will add file2 to the first commit.
Answered 2023-09-20 20:20:25
Well, this solution might sound very silly, but can save you in certain conditions.
A friend of mine just ran into accidentally committing very some huge files (four auto-generated files ranging between 3GB to 5GB each) and then made some additional code commits on top of that before realizing the problem that git push
wasn't working any longer!
The files had been listed in .gitignore
but after renaming the container folder, they got exposed and committed! And now there were a few more commits of the code on top of that, but push
was running forever (trying to upload GB of data!) and finally would fail due to Github's file size limits.
The problem with interactive rebase or anything similar was that they would deal with poking around these huge files and would take forever to do anything. Nevertheless, after spending almost an hour in the CLI, we weren't sure if the files (and deltas) are actually removed from the history or simply not included in the current commits. The push wasn't working either and my friend was really stuck.
So, the solution I came up with was:
~/Project-old
.~/Project
). cp -r
the files from ~/Project-old
folder to ~/Project
. mv
ed, and included in .gitignore
properly. .git
folder in the recently-cloned ~/Project
by the old one. That's where the logs of the problematic history lives!push
'ed.The biggest problem with this solution is, it deals with manual copying some files, and also it merges all the recent commits into one (obviously with a new commit-hash.) B
The big benefits are that, it is very clear in every step, it works great for huge files (as well as sensitive ones), and it doesn't leave any trace in history behind!
Answered 2023-09-20 20:20:25
I had same problem and this this:
x
,My rider git log:
git checkout -b x 77164a510f1c17ed650b87c2ebf0f7762ac6b2a2 --
git reset --hard 0d038b5e3e3e2adef4bd6aab7653f922c3fdc63f
git add --ignore-errors -A -f -- src/Mores.Warehouse.Core.Api.ClientSdk/Mores.Warehouse.Core.Api.ClientSdk.csproj
git commit -F C:\Users\Hassan\AppData\Local\Temp\git-commit-msg-.txt --amend --
git cherry-pick 77164a510f1c17ed650b87c2ebf0f7762ac6b2a2
git checkout feature/MOR-2947 --
git reset --hard 0d038b5e3e3e2adef4bd6aab7653f922c3fdc63f
git rebase x
Answered 2023-09-20 20:20:25