0

I accidentally reverted my initial commit and deleted the files to my project. I know this should be recoverable, but not sure the proper solution to retrieve that initial commit and then revert back to that.

I ran some commands from answers and when I run git log --oneline. The only commit hash that appears is 287e9ccb (HEAD, master) initial commit, which is my blank directory with the exception of the node_modules folder. Clearly I ran a command that I thought would revert to the first commit and potentially corrupted that.

However, I saw an additional command to run git log --diff-filter=D --summary and this lists out all of the files that were deleted. How can I retain those deleted files like they were in the "initial commit"?

Edit: running git reflog, I get:

287e9ccb (HEAD, master) master@{0}: reset: moving to 287e9ccb
25a289ec master@{1}: revert: Revert "initial commit" 
287e9ccb (HEAD, master) master@{2}: commit (initial): initial commit
torek
  • 448,244
  • 59
  • 642
  • 775
cphill
  • 5,596
  • 16
  • 89
  • 182
  • 1
    It sounds like you did `git reset --hard 287e9ccb` to get into this state. If so, use `git reflog` or `git reflog master` to find the hash ID that the name `master` *used to* select, before you told Git *make the name `master` select commit 287e9ccb*. If not, it would be helpful if you showed what commands you used to accomplish this reversion. – torek Mar 20 '20 at 19:12
  • `git reflog master` retrieves a list of `287e9ccb (HEAD, master) master@{0}: reset: moving to 287e9ccb` , `25a289ec master@{1}: revert: Revert "initial commit"` , `287e9ccb (HEAD, master) master@{2}: commit (initial): initial commit` – cphill Mar 20 '20 at 20:26
  • I originally ran `git reflog` and then `git reset ` for the "initial commit", but I must have selected the wrong hash – cphill Mar 20 '20 at 20:29
  • I've put the output of `git reflog` into the question, as it's a lot easier to read when formatted into lines... – torek Mar 20 '20 at 21:00

1 Answers1

1

The reflog shows that you have just two commits in your repository:

  • 287e9ccb, which is your initial commit
  • 25a289ec, which is probably the result of running git revert 287e9ccb

The initial commit has, as its snapshot, all of the files you committed. The difference between this commit and the previous (non-existent) commit is, if you ask Git for it, add all these files with these contents.

If the second commit is a revert of the first (as its log message implies), it contains the result of undoing the first commit: i.e., removing all the files. So the second commit has an empty source tree.

The only things that Git stores forever / permanently1 are the commits themselves. The files that you work with, that you can see and edit, are temporary. Extracting a commit replaces those files by removing those files2 and putting in the ones from the commit you've chosen.

This means that at least as far as Git goes, all you can get back are these two commits: the first one, and the one that undoes the first one. If you need file contents that were not saved into either of these two commits, you will have to get them from something other than Git.


1Even commits themselves are only as permanent as they are findable by hash ID. Typically you (or Git) will start with a branch or tag name to get one particular hash ID. That lets you find that commit. That commit then contains the hash ID of its previous, or parent, commit, which lets Git find the parent commit. The parent contains the hash ID of another earlier commit, and so on: this goes back to the very first commit, which—being first—doesn't record any earlier commit, and then Git stops.

In other words, Git works backwards. You find the hash ID of the last commit, from a branch name, and then Git works backwards. What git reset does is let you move branch names to any commit hash ID. From there, you can only look backwards.

The reflog holds all the hash IDs that the name held in the past, up to some point anyway. (Reflog entries eventually expire so that the reflogs don't get infinitely long.) So if you reset away a commit, and can't find it, the hash ID of the later commit that the name used to find will be in your reflog.

Unfortunately, git reset --hard means please destroy my temporary copies of files that are in the index and in my work-tree. If you haven't committed them, you cannot get them back.

2Git uses some cleverness to avoid removing a file and then replacing it with the same content. Since most commits mostly re-use some other commits' files, this saves a lot of time when switching from one commit to another. It also helps a lot with some build systems (e.g., make or one of its derivatives).

torek
  • 448,244
  • 59
  • 642
  • 775
  • Thank you for the thorough answer of the situation. Based on what you mentioned the "initial commit" should still contain all my files and contents that I want to recover because that commit was the last I was planning on doing before pushing to a new Github repo. in order to retrieve that initial commit to my master branch what commands should I run? Running `git branch` at the moment retrieves the list with two options, `* (HEAD detached at 287e9ccb)` and `master` – cphill Mar 20 '20 at 22:26
  • The `HEAD detached at ...` tells you that you're in "detached HEAD" mode. In this mode, you have selected a commit to use, and have told Git: "I do not want to be on ANY branch, I just want to use this commit." The commits exist independent of branches, but branch names are how you normally *find* commits. See footnote 1 above. – torek Mar 20 '20 at 23:09
  • To get back to commit `287e9ccb` (which, note, is where you are now) *and* be `on branch master` as `git status` will say, use `git checkout master`. The branch name `master` currently holds commit hash ID `287e9ccb`. That's the commit you're currently using. You're using it without using the branch name. Doing the `git checkout` will tell your Git: *use the commit identified by `master`* (that's `287e9ccb`) *and go back into normal attached-HEAD mode while doing that*. – torek Mar 20 '20 at 23:10
  • When you eventually add a *new* commit, the new commit will have some new hash ID. The new commit's *parent* commit will be `287e9ccb`. Git will write the *new* commit's hash ID into the *name* `master`, so that `master` means the *new* commit, and `287e9ccb` will continue to be a historic commit. It just won't have a *name* attached any more. You will find it by finding whatever the hash ID is in the name `master`, then having Git look up that commit's parent. – torek Mar 20 '20 at 23:12
  • This is what commits, and branch names, in Git are all about. You *add commits* to the repository. Each one has a full snapshot of all of your files, stored as a frozen-forever archive. Each one links back to its previous frozen-forever archive: its parent commit. The *name* holds the hash ID of the *latest* commit, the last one in the branch. – torek Mar 20 '20 at 23:13
  • thank you again for the very detailed answers and continual help. When I `git checkout master` I see individual lines referencing previously existing files represented in the format of `D partials/navigation.hbs` followed by `Switched to branch 'master'` and when I check my directory I still see that no files exist. Does this mean that I have corrupted the previous commit? I'm assuming "D" is shorthand for deleted. Doesn't seem like future commits will contain the initial commit – cphill Mar 21 '20 at 02:49
  • If `git checkout master` prints out `D path/to/file`, this means that `path/to/file` is *currently* missing from the index. Run `git reset` (with no arguments) to tell Git to overwrite the index content with the commit content, without changing the work-tree content (the files you can see) at all; or, run `git reset --hard` to tell Git to overwrite *both* the index *and* work-tree contents with the committed content. Note that the hard reset may remove some files that you can see. – torek Mar 21 '20 at 03:01
  • If you want to know why Git didn't change the index content when you switched from commit `287e9ccb` to commit `287e9ccb` (which didn't actually switch any commits) in the process of switching from detached HEAD to attached HEAD mode, see https://stackoverflow.com/q/22053757/1256452 - but this is more about when you actually *do* switch *commits*. Here, since you're not switching *commits*, it's pretty clear that Git didn't have to touch anything at all. – torek Mar 21 '20 at 03:03
  • To really understand this, see any of my many answers that go into details on the three active copies of each file that Git has available at all times when you're on some commit. There's the *committed* copy of each file in the current commit, which nothing can change; there's a second copy of the file in Git's *index*, which you can't see but is still there; and there's a third copy of each file in your work-tree. That third copy is the only one you can see and work with directly. – torek Mar 21 '20 at 03:06
  • Thank you so much for your help with this. The detailed responses were extremely helpful with allowing me to understand my missteps and resolve my problem. Thank you for all of your time with responding! – cphill Mar 21 '20 at 13:08