3

As I know that git reset --hard can potentially cause unwanted data loss because it may make changes that cannot be undone, is there a way to check what it would do before I execute it? Basically I'm looking for something that would be logically

git reset --hard --dry-run

or

git reset --hard --verbose --dry-run

which unfortunately do not work with currently existing Git because reset doesn't support --dry-run.

Mikko Rantalainen
  • 14,132
  • 10
  • 74
  • 112
  • so.... you want to know how to do `git diff HEAD`? Because that is what would change. – eftshift0 Feb 17 '23 at 11:36
  • @eftshift0 I guess that's one way to think about it but if you have partially staged files in index, it might be a good idea to warn about losing the contents of index. And for logical dry-run, I think you should use `git diff -R HEAD` to get preview what's going to happen. – Mikko Rantalainen Feb 17 '23 at 11:38
  • I C what you mean. I don' t think it exists.... sounds like a nice patch for git – eftshift0 Feb 17 '23 at 11:48
  • "As I know that git reset --hard will make changes that cannot be undone (potential data loss)" Not necessarily. I do hard reset and then undo it later, quite often and freely, without losing any data. Yes, it's potentially dangerous, but you can make it danger free. – matt Feb 17 '23 at 11:59
  • @matt Good point, I reworded the question to make it more obvious that `git reset --hard` may potentially lose data and that's specifically why I'm asking for `--dry-run` alternative. – Mikko Rantalainen Feb 17 '23 at 12:05

4 Answers4

3

I would dispute the premise of the question. I hard reset nimbly and freely all day long and I don't risk anything. If everything is nicely tucked into bed in a commit, hard reset is perfectly safe and undoable (especially if you make a placeholder branch first). So it's just a matter of making sure that everything is tucked in. git status will tell you that.

Also, consider git switch --det as an alternative. It does much the same thing as a hard reset but it has safety checks built in. You can always switch first and move the branch name later. Again, I do this a lot.

Finally, I should just add that I have an alias that lets me say git snapshot to make assurance double sure:

snapshot = !git stash push --include-untracked --message \"snapshot: $(date)\" && git stash apply \"stash@{0}\" --index

If you snapshot first, the next move is safe, because you can always get back to the commit you were on and apply the stash.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • I guess you could even use `git stash pop --index` here because the "backup" state would still be accessible via reflog (for 30 days by default, IIRC). – Mikko Rantalainen Feb 17 '23 at 14:48
  • This answers describes my day to day perfectly: 1.) "I'm *pretty sure* I want to throwaway what I just did." 2.) `git status` 3.) Hmmm, maybe I want to keep one or more of those staged or unstaged mofications. 4.) `git commit -am "wip: throwaway?"` 5.) `git reset --hard @~1` – TTT Feb 17 '23 at 19:16
1

git reset --hard doesn't have dry run mode but this should get you close to the same information:

#!/bin/bash
echo "Changes that would be executed to working directory files:"
git diff -R HEAD
echo "Staged changes that would be lost:"
git diff --cached --stat

or as a single-liner:

git diff -R HEAD && git diff --cached --stat
Mikko Rantalainen
  • 14,132
  • 10
  • 74
  • 112
1

Since you seem to be looking for an option you can avoid data loss with, let me present you with a different option.

git stash allows you to check out HEAD while saving them to the stash. You can then undo it with git stash pop.

So, instead of git reset --hard, you can do git stash and it will mostly have the same result. If you don't like the changes, you can undo it with git stash pop or if you are fine with it, you can delete the changes from the stash (which is a destructive action) using git stash drop.

If you also want to include untracked files (which git reset --hard doesn't do by default), you can use git stash --include-untracked.

If you don't want to reset to HEAD (i.e. you want to do git reset --hard some_ref), you can first stash the local changes, get the current commit hash using git show-ref and then run the git reset --hard. If in doubt, you can reset to the commit again (uness you run git gc or something like that which cleans up dangling references). However, at that point, you might want to create a branch or a tag as a backup as well.

# Stash your local changes
git stash # add --include-untracked if you want to stash untracked files as well
# get the current commit hash AND SAVE IT somewhere
git show-ref # this will output the commit hash of HEAD
# reset to some ref
git reset --hard some-ref
# In case you want to undo it, reset to the commit hash from git show-ref again and load the local changes from the stash again
git reset --hard YOUR_HASH_FROM_SHOW_REF # replace YOUR_HASH_FROM_SHOW_REF with the hash from git show-ref
git stash pop

In any way, I would recommend you to create a copy of the repository including all changes that are important (just copy the entire directory) in case something goes wrong, especially if you are unsure about the commands.

Also note that git stash allows you to stash your local changes, check out or reset another ref and then apply these changes back on the newly checked out/resetted ref.

dan1st
  • 12,568
  • 8
  • 34
  • 67
  • Yes, assuming the potential changes do not require awfully lot of storage, this would be a nice solution. Are you aware of any special cases that wouldn't be correctly preserved with `git stash && git stash pop`? (I guess one would need to test for stuff where staged content has replaced a file with a directory and working directory has replaced the same file with a symlink to another file or something like that might potentially cause problems.) – Mikko Rantalainen Feb 17 '23 at 12:02
  • 1
    Well, there might be some problems with files that are ignored or untracked in the current ref but not in the one to reset to. But these changes can be `stash`ed as well with `--include-untracked` or `--all` as shown in [this answer](https://stackoverflow.com/a/835561/10871900). – dan1st Feb 17 '23 at 12:05
0

To just see what effects git reset --hard has, you could create a backup of your branch and restore to it should you not be happy:

git branch backup 
git reset --hard <commitid>

If you are not happy at this point, restore the backup:

git reset --hard backup

Otherwise remove the backup

git branch -D backup 
michid
  • 10,536
  • 3
  • 32
  • 59