Just a comment:
git revert aCommit
does revert the all commit (as in "all the files part of the commit" ):
it computes a reverse patch, applies it on HEAD and commit.
So two problems here (the first one is easily solved):
- it does always commit, so you may want to add
-no-commit
option: "git revert --no-commit aCommit
": this is useful when reverting more than one commits' effect to your index in a row.
- it does not apply for a specific file (what if you a.py was part of a commit with 1000 other changes that you may not want to revert) ?
For that, if you want to extract specific files as they were in another commit, you should see git-checkout
, specifically the git checkout <commit> <filename>
syntax (that is not exactly what you need in this case though)
Easy Git (Elijah Newren) tried to bring a more "complete revert" to the Git Mailing list; but without much success:
People occasionally want to "revert changes".
Now, this may be:
- the changes between 32 and 29 revisions ago,
- it might be all changes since the last commit,
- it could be the changes since 3 commits ago, or
- it could be just one specific commit.
- The user may want to subset such reversions to just specific files,
(eg revert
is documented here, but I am not sure it is part of the current distribution of eg though)
but it all boils down to "reverting changes" in the end.
eg revert --since HEAD~3 # Undo all changes since HEAD~3
eg revert --in HEAD~8 # much like git revert HEAD~8, but nocommit by default
eg revert --since HEAD foo.py # Undo changes to foo.py since last commit
eg revert foo.py # Same as above
eg revert --in trial~7 bar.c baz. # Undo changes made in trial~7 to bar.[ch]
Are these kinds of "reverting data" really so different that there should need to be different commands, or that some of these operations shouldn't be supported by the simple revert command?
Sure, most users most of the time will probably use the "eg revert FILE1 FILE2...
"
form, but I didn't see the harm in supporting the extra capabilities.
Also...is there anything fundamental that would keep core git from adopting such behavior?
Elijah
Note: commits by default don't make sense for the generalized revert
command, and "git revert REVISION
" would error out with instructions (telling the user to add the --in flag).
Lets say you have, out of 50 committed, 20 files you realize that old commit X introduced changes that should not have taken place.
A little plumbing is in order.
What you need is a way to list all the specific files you need to revert
(as in "to cancel changes made in commit X while keeping all subsequent changes"),
and then, for each of them:
git-merge-file -p a.py X X^
The issue here is to recover the lost function without obliterating all subsequent changes in a.py you might want to keep.
That technique is sometime called "negative merging".
Since git merge-file <current-file> <base-file> <other-file>
means:
incorporates all changes that lead from the <base-file>
to <other-file>
into <current-file>
, you can restore the deleted function by saying you want to incorporate all changes.)
- from: X (where the function has been deleted)
- to: X^ (the previous commit before X, where the function was still there)
Note: the '-p
' argument which allows you to review first the changes without doing anything on the current file. When you are sure, remove that option.
Note: the git merge-file
is not that simple: you can not reference previous versions of the file just like that.
(you would have over and over the frustrating message: error: Could not stat X
)
You have to:
git cat-file blob a.py > tmp/ori # current file before any modification
git cat-file blob HEAD~2:a.py > tmp/X # file with the function deleted
git cat-file blob HEAD~3:a.py > tmp/F # file with the function which was still there
git merge-file a.py tmp/X tmp/F # basically a RCS-style merge
# note the inversed commit order: X as based, then F
# that is why is is a "negative merge"
diff -u a.py tmp/ori # eyeball the merge result
git add a.py
git commit -m "function restored" # and any other changes made from X are preserved!
If this is to be done for a large number of files within a previous commit... some scripting is in order ;)