This is my situation:
I am trying to place an exact copy of an earlier commit on top of my latest commit. However,
- I have already pushed to remote, so do not want to simply
git reset --hard
(or, I would be happy to do so if I could somehow then add the earlier commit back on top of the latest commit) - in between my latest commit and the desired commit, I created and merged a branch, and this seems to give me problems/conflicts (otherwise would do a revert)
- any solutions I have tried seem to retain additional changes from the other branch, which I do not want (I want my copy to look exactly like a checkout of the earlier commit)
I know this is probably a simple problem, but after much searching/trial and error I still cannot find a solution. Any help would be greatly appreciated.
In case you're wondering why I want to do something like this, the original motivation was in order to easily use git-latexdiff
to compare two versions separated by a merge.
To reproduce this scenario/see an example:
In order to make this scenario reproducible, the commands below will make a folder called tryrevert
with the necessary structure:
mkdir tryrevert; cd tryrevert; git init; echo 'line 1 commit 1' > file1.txt; git add .; git commit -m 'commit 1'; echo 'line 2 commit 2 only want these two lines' >> file1.txt; git add .; git commit -m 'commit 2 - WANT THIS ONE'; git branch sidebranch; git checkout sidebranch; echo 'another file - only in sidebranch' > sidefile.txt; git add .; git commit -m 'commit 3 - sidebranch work'; git checkout master; echo 'extra work' >> file1.txt; git add .; git commit -m 'commit 4 - some more work on master'; git merge sidebranch -m 'commit 5 - the merge'; echo 'final work after the merge' >> file1.txt; git add .; git commit -m 'commit 6 - latest commit '
git log --graph --all --decorate --oneline
and will output the following commit tree (excluding origin/master
):
* 7c4c7a1 (HEAD, origin/master, master) commit 6 - latest commit
* 5b0077c commit 5 - the merge
|\
| * 20a1164 (sidebranch) commit 3 - sidebranch work
* | 3a5a24f commit 4 - some more work on master
|/
* 925228b commit 2 - WANT THIS ONE
* 52af39e commit 1
(I left sidebranch
where it is rather than moving it up to master
for clarity.)
So what I want is:
* abcdefg (HEAD, master) COPY OF commit 6
* hijklmn COPY OF commit 2
* 7c4c7a1 (origin/master) commit 6 - latest commit
* 5b0077c commit 5 - the merge
|\
| * 20a1164 (sidebranch) commit 3 - sidebranch work
* | 3a5a24f commit 4 - some more work on master
|/
* 925228b commit 2 - WANT THIS ONE
* 52af39e commit 1
Notes:
i) sidebranch
creates sidefile.txt
, and all efforts I have at reverting to commit 2 end up containing sidefile.txt
(and I do not want COPY of commit 2 to contain sidefile.txt
). I would also like to avoid having to deal with merge conflicts in file1.txt
.
ii) I've tried rebase -i
and cherry-pick
, but without success (perhaps I am doing it incorrectly) - I keep running into conflicts.
iii) If I did not have the merge, something like:
git revert --no-commit master...HEAD~3; git add .; git commit -m 'reverting to commit 2'
would work for me, but the merge seems stop this exact line from working.
iv) I have spent time looking at many posts on stackoverflow, but couldn't find one that dealt with this specific scenario...
Edit:
(apologies in advance for the long edit, but want to clarify the state of the two commits)
In response to @Juan's question, and to make it clear, commit 6 contains
file1.txt
:
line 1 commit 1
line 2 commit 2 only want these two lines
extra work
final work after the merge
sidefile.txt
another file - only in sidebranch
On the other hand, in commit 2 file1.txt
contains only the first two lines:
line 1 commit 1
line 2 commit 2 only want these two lines
and there is no sidefile.txt
in commit 2.
@Juan's answer below works well for reverting/cherry-picking file1.txt
, but the created commit still contains sidefile.txt
(which I don't want).
After playing around with "theirs", "ours" and other flags, the following post provided a solution:
git checkout master~1^1~1
git checkout -b commit2branch
git merge -s ours master -m 'revert to commit2'
git checkout master
git merge commit2branch
git branch -D commit2branch
which works, in terms of getting the files to be in the correct state (correct file1.txt
, no sidefile.txt
but then the commit history contains an extra branch:
* 4236c61 (HEAD, master) revert to commit2
|\
| * 7c4c7a1 (origin/master) commit 6 - latest commit
| * 5b0077c commit 5 - the merge
| |\
| | * 20a1164 commit 3 - sidebranch work
| |/
|/|
| * 3a5a24f commit 4 - some more work on master
|/
* 925228b commit 2 - WANT THIS ONE
* 52af39e commit 1
while I do not want this extra branch...
Final edit
I have an answer, which I have posted below, and is based on @Juan's second solution. However, the solution uses several reverts, while I would really like a one-line solution.