4

enter image description here

I mistakenly replace all and save lines in my documents that are all tracked by Git. I have done some work before and forgot to commit as a checkpoint before doing that, so reverting is not desirable as it would revert all of my work. I would like to revert only the replace all operation using help from Git.

I know all the lines that I need to revert because they all have the same new line. From GUI tools like SourceTree I can click Discard Hunk one by one, but there are many such location that I would like to discard. All the places I would like to revert has new line that says

m_Sprite: {fileID: 21300164, guid: 791de2a5646d94a15a09bb1d7e79a0e6, type: 3}

But old line is not necessary the same. (In fact there are 2 kinds of old lines that I changed into this same new ones) Are there any scripted way to match the pattern of changed lines so I can selectively revert them all? Thank you.

Using git add -p interactively to add everything except the hunk I don't want going forward is very tedious, since it ask you one by one but there are so many works to add and at the same time the hunks I don't want are mixed in almost every file. (So I must be very careful on every question git asks) The same goes for git checkout -p to remove the hunks I don't want going backward.

5argon
  • 3,683
  • 3
  • 31
  • 57
  • 2
    `and forgot to commit as a checkpoint before doing that` ... I think that Git won't help you here. Git's lingua franca are commits, which represent versions of files. The earlier work you did which was wiped by the replace all was never seen by Git. Your best option might be to just use undo from your IDE. – Tim Biegeleisen Sep 14 '17 at 10:01
  • How did you "replace all" in your project? Whatever tool you used to do that should have an undo operation or atleast some way to do a reverse transform. – Noufal Ibrahim Sep 14 '17 at 10:04
  • @NoufalIbrahim The operation is Find and Replace All of Visual Studio Code. After that I have saved all the files and have already close the editor so I could not undo them. (I save all files by trying to close the program, and when the warning dialog comes up I click Save All) – 5argon Sep 14 '17 at 10:06
  • @TimBiegeleisen The new replace was never seen by Git, ok. So if I know the old ones can I recover them? I have only 2 such cases and I know exactly what they looks like. – 5argon Sep 14 '17 at 10:07
  • If you did not commit that work (or maybe stash it) _and_ you closed Visual Studio, thereby erasing the undo history, then you may be out of luck. – Tim Biegeleisen Sep 14 '17 at 10:11

3 Answers3

6

Ok, I managed to fix the problem using a patch.

First, I commit this messed up state as a checkpoint. Then I can produce a patch of this state (the one with replaced lines I want to revert plus all the work I don't want to lose) and the previous commit (the one with correct lines remembered) using

git diff --binary HEAD^ HEAD > ~/Desktop/patch.diff

Note that the previous commit comes first in the command.

With the patch saved somewhere, I reset hard into the previous commit and git clean -f. Now, if I git apply patch.diff I would arrive at the same point (but still keeping head on a previous commit) The idea is to directly edit the patch surgically before applying.

The patch is composed of hunks, so to disable a certain hunk I don't want the approach is to replace the + lines with the same thing as the preceeding - ones. I can use regex searching in VSCode to search multi-lines with \n (Don't forget to escape the + character as \+ so it does not count as regex) and replace them all.

This way is better than removing the hunks, since there's no chance to corrupt the format of a patch. The patch does not know if it is changing into the same line, doing nothing.

enter image description here

After editing the patch and apply, I got all the changes minus the hunks that we edit earlier. And so it equals the action of "reverting all the hunks I don't want".

5argon
  • 3,683
  • 3
  • 31
  • 57
0

Using the patch like you did, it lead to corrupt patch when i want to replace creation only (no modifications). I have a solution for that case but i'm aware it's not perfect, because it would also remove any other addition or removal in the same blob of lines (if there is one too much close that you would like to keep).

Here is the regex :

@@.*(?:(?!@@.*)[\s\S\n])*?(patternToRemove)((?!@@)[\s\S\n])* and replace with nothing
TT M
  • 1
0

Analogous to this answer, you could use grepdiff from patchutils.

Check the matches first:

git diff -U0  | grepdiff 'schemaVersion' --output-matching=hunk 

Then use apply --reverse to undo the matching changes.

git diff -U0  | grepdiff 'schemaVersion' --output-matching=hunk | git apply --reverse  --unidiff-zero

The same logic can be used with git apply --cached to stage matching lines.

Joshua Goldberg
  • 5,059
  • 2
  • 34
  • 39