Reputation: 5534
When trying to revert to a previous commit (for instance 123abc) via git reset
git reset --hard 123abc
git commit -a -m "revert to 123abc"
I cannot push this (I need to pull before and pulling moves me forward). I have come with this few lines:
for i in `git diff --name-only 123abc`; do git checkout 123abc $i; done
git commit -a -m "revert to 123abc"
Which works since now
git diff --name-only 123abc
is empty
I was wondering if this is an hack or the git way to do it. In case it is not, how to accomplish this correctly?
Upvotes: 4
Views: 30764
Reputation: 487755
When trying to revert to a previous commit (for instance 123abc) ...
The emphasis I added here on the preposition "to", as in revert to, is crucial. The accepted answer at your linked question (How do I revert a Git repository to a previous commit?) calls this out:
This depends a lot on what you mean by "revert".
Running git revert
on a single commit may not suffice. See the answers there about reverting multiple commits, if needed.
Running git reset --hard
may suffice, but introduces the very problem you've encountered. The things to understand here are:
A branch name simply lets Git find one particular commit. We say that the branch name points to the commit. Using git reset
, we can change the one particular commit to which some branch name points. Nothing else happens in the repository yet, although depending on the kind of git reset
you run, something else might happen before git reset
finishes: the first step of this git reset
is just to change one of your own branch names. Since you're considering (or have used) --hard
, some more things will change in your repository, but they're less important at the moment.
Every Git repository has its own branch names. Moving one of your branch names has no effect on any other repository.
What you need to do is to get some other Git repository to change one of its branch names. That's where you run into this problem:
I cannot push this (I need to pull before and pulling moves me forward).
The git push
command is how you ask—or, using --force
, command—some other Git repository to change or create or delete some of its names (branch names, tag names, and other such names). But each Git repository is set up to easily accept new incoming commits, yet at the same time, resist the suggestion to throw away any existing commits.
When you use git reset
to move one of your branch names "backwards", so as to revert to (not revert
as in add-a-commit-that-backs-out) some previous commit, you're deliberately throwing away some existing commit(s). Since you control your own Git repository, you can definitely do this to your own repository. Since the git reset
command is meant to do this kind of throwing-away,1 it does so without complaint. But git push
isn't, and their Git complains.
As soon as you use git pull
, their repository has you put back all the commits you snipped out of your own repository. Git is, after all, designed to add commits as easily as possible! That leaves you with the situation you're in.
You now have a choice:
Force the other Git repository to discard some commits. This requires sufficient permissions. It also means that everyone else who is using that other Git repository need to take action with any clones they have made, because their clones will enthusiastically put back all the commits you are attempting to prune. So this is kind of mean to those other people.
Or, use git revert
or some other command to add some commit(s) to your repository that, in the end, result in putting the files back, but don't remove any old commits. The new commits simply add on to the old ones. The old ones are still there for anyone who would like to ask about them (git log
) or use them (git switch --detach hash-id
, for instance).
Adding new commit(s) is what Git is designed to do, so the latter is the way to go unless there's some really strong reason to ditch the old commits.
As the question you linked notes, all of this is a lot easier with unpublished commits: a commit that only you have, in your own private repository, is simply not in any other Git repository. If you shave those commits off your own branches, nobody will ever know. They won't be able to object that a git push
shaves those commits off their branches, because those commits aren't on their branches. (Again, each branch name is local to each Git repository. One repository can show another one the commit hash ID stored in its branch name, but each repository takes responsibility in keeping its own hash IDs in its own branch names. The commits get shared; the branch names do not.)
Since the commits you're looking at are published you cannot use this short-cut.
1This makes git reset
a very-high-powered tool, like some sort of flame-throwing chainsaw, or industrial steel-cutting laser, or something. This over-powered-ness is part of the reason that Git 2.23 now has git restore
: some of the things you can do, that used to require using git reset
, can now be done with the rather gentler git restore
. Both commands will throw away in-progress work if you ask them, but git restore
is more like a hand-saw, or bolt cutters, or something along these lines.
Upvotes: 6
Reputation: 4171
It is a hack. Use
git revert <commit hash>
but beware, you are reverting the changes applied by the <commit hash>
, so to revert to previous commit, use
git revert HEAD
This will create another commit that is reverting a change caused by the last commit. Check this answer: https://stackoverflow.com/a/22683231/12118546
This above is a proper way how to do this with putting the reverting itself to the history also. If you are the only one working in the repo, you can also force push the branch with the latest commit removed: https://stackoverflow.com/a/31937298/12118546
Upvotes: 1
Reputation: 598
You should try using git revert <commit hash>
. This will revert the selected commit and you can then push the changes
Upvotes: 1