Reputation: 33046
As I understand it calling "git reset --hard" should undo any changes on my computer. However after running it, I have 1,459 files on my system Git says I need to add or merge to the branch I am working on.
How can I have it throw away all changes on my computer (I do want to keep the changes I have pushed up already)?
If it's relevant, this occurred because Visual Studio ran out of memory half way through switching to another branch. So I think it left files from the one branch on my system but marked itself as on the switched to branch.
Upvotes: 1
Views: 3862
Reputation: 490068
Try removing the index (remove the file .git/index
), then running git reset --hard origin/support-15.2
. This isn't a normal thing to do, but your situation is not normal either.
It sure sounds like Visual Studio goofed up the "disk full" error-handling.
As another recent Q&A noted, when we work in Git (without some IDE in the way), we have three main parts of interest:
The repository itself, usually hidden inside a directory named .git
(but not when using a --bare
repository).
The repository stores objects (it's a sort of key/value database), plus some ancillary data, most significantly a mapping from names—which Git normally presents as "branch B" or "tag T", but the general format is reference names—to object hash IDs, such as the big ugly commit hashes. Since the hashes appear random, Git (and we) need a way to turn a consistent, sensible name like master
into the hash ID of the commit to treat as the most recent commit on a branch. (This mapping changes over time, as we add more and more commits.)
Objects inside the repository are stored in a special Git-only format. They are also read-only, once created: an object is either there, containing the same value it had when it was saved into the key/value store initially and forever; or it's not there at all. If one runs out of disk space, this database must maintain this same property (objects must still be all-or-nothing).
The name-to-object-ID mappings are read/write, since they do change. Running out of disk space could cause you to lose some mappings, which is significant since it's these mappings themselves that make objects reachable.
The work-tree (or working-tree, or working directory, or various additional names, all for "the place where you do your work").
Files in the work-tree have their normal everyday form, and are read/write, like normal everyday files.
The index, also called the staging area and the cache.
The index is, in a sense, entirely unnecessary, but it's how Git gets much of its speed. It also enables a bunch of tricky items like git add -p
. The actual implementation of the index is as a file, .git/index
, but the format and contents of that file are nontrivial (and now there's something called a split index where the index is in more than one file; and when you use git worktree add
, you get one index per work-tree, so you should not assume .git/index
anyway).
The index, like the work-tree, is necessarily read/write. It has several roles: it not only lets you stage items for commit (via git add <file>
), it also caches information about all the work-tree files. This is why it has the names staging area and cache. The cached information lets git status
go very fast—but assumes that the index has been correctly adjusted based on the contents of the work-tree, and vice versa.
A normal git checkout
and series of git add
s keeps this cache data up to date, so that the index and work-tree match each other. I suspect that after running out of space, VS and/or the underlying Git code got the two out of sync. If they got them sufficiently badly de-synchronized, it's possible that even an ordinary git reset --hard
is not getting them back in sync.
In this case, as long as you have no staged modifications, no in-progress merges, and the like, it's safe enough to remove the index entirely. This loses all the cached information about the current commit and about the current work-tree contents, which forces Git to rebuild it all from scratch. A subsequent git reset
with either --mixed
or --hard
will do this rebuilding. With --mixed
, Git will make index entries for files in the target commit of the git reset
,1 looking at what's in the work-tree, without touching the work-tree. With --hard
, Git will make these index entries and at the same time re-set the work-tree files to match the target commit. If some work-tree files are left over from a half-conversion from some other commit hash ID, making them all match the hash ID to which origin/support-15.2
resolves is probably the right thing to do.
(As always, if you're not quite sure, you can save the work-tree contents somewhere out of the reach of Git itself, then use whatever tools you like to compare the saved tree with what Git extracts.)
1The target is what I call the commit argument to git reset
:
git reset [--type] target
(where type is one of soft, mixed, or hard)
The Git documentation calls this a <commit>
, but there are lots of commits. If you specify a raw hash ID, this must be the hash ID of a commit, and Git will change the current branch name—whatever branch name that is—so that it names that specific hash ID as its current commit. If you specify a name, such as origin/support-15.2
, Git just resolves that name to a commit hash ID, and then proceeds as if you'd given it a commit hash ID.
With --soft
, Git changes the current branch to point to the target commit, and stops. With --mixed
, Git goes on to modify the index as well, and then stops. With --hard
, Git updates the work-tree while modifying the index.
Upvotes: 2