1

Git Pull Failed: Your local changes would be overwritten by merge.Commit, stash or revert them to proceed. When I click the link to see the files, I see a file named Environment.config. Surprisingly, I have it in my .gitignore file along with Run.config, like..

# General Config files, both originally tracked(but modified for only this project) Run.config Environment.config 

First, I don't know why only Environment.config is pointed out being changed even though both the files have been changed for my local project and entered in to .gitignore file.

Second, I ran this command to remove the file from being tracked.

git update-index --assume-unchanged <file> 

I could able to push with out my local changes being sent to the remote. But after some development, when I try to commit, then pull from remote, I am getting in to this "Git Pull Failed" situation.

When I checked the local changes by 'git status' or IntelliJ tool local changes view, I don't find none. Why this phantom file change is being reported ?

2 Answers 2

2

It’s not “tricky”, it’s normal.

The fact that the file is in .gitignore and is not being tracked is the problem. It means you are not committing this version of the file. So it is an uncommitted file in your work tree. But there is also an earlier version of the same file in the repo from an earlier time when you did commit it. So a simple pull would overwrite the worktree version and git stops you. And git correctly tells you what your choices are for completing this pull. Listen and obey; choose one:

  • Make the version in the index match the version in the work tree, that’s called commit

  • Make the version in the work tree match the version in the index, that’s called revert, or you could use restore

  • Move the version in the work tree aside, that’s called stash

The long term solution is to git rm --cached this file to remove the old version from the index, and commit and push. Now it won’t be present in the remote’s commit the next time you pull, and so there will be no conflict.

Sign up to request clarification or add additional context in comments.

Comments

0

Second, I ran this command to remove the file from being tracked.

git update-index --assume-unchanged <file> 

This does not make the file untracked.

Remember, the definition of a tracked file is a file that exists in Git's index. An untracked file is, by definition, a file that exists in your work-tree but does not exist in Git's index.

Using git update-index --assume-unchanged path marks the file in the index as "assume unchanged". (The similar --skip-worktree marks that same index copy of the file as "skip worktree".) In both cases the file is still in Git's index.

A file that is in Git's index is, by definition, tracked ... so these files are tracked.

When I checked the local changes by 'git status' ...

The git status command works by running two separate comparisons.

  • The first one compares the current commit to what is in Git's index. We can pretty much ignore this comparison for the moment, but if it found any differences, it would print the names of the files, first saying that there are changes staged for commit.

  • The second comparison compares each file in Git's index to the same file in your work-tree. If they're different, Git prints the name of the file, first saying that there are changes not staged for commit.

Setting either of the two flags on the copy of the file that is in Git's index simply tells git status not to look at the work-tree copy. That is, git status should just assume the file is unchanged. That is, after all, why the first flag is named "assume unchanged".

(The second flag is just a slight variant that is meant for the sparse checkout code. The first flag—the assume-unchanged one—is meant for use on slow systems. Using either flag this way is technically an abuse of these two internal mechanisms.)

Because the file is in Git's index, it's tracked. It's just that git status has shut up about it. But git checkout and several other Git commands check on it anyway. In this particular case, your git pull has invoked git merge in such a way that git merge wants to overwrite your work-tree copy while replacing the index copy of each of these two files. Git bypasses the two flags, notices that the work-tree copies of the two files differ from the index copies right now, and—correctly—tells you that if the command were to complete, that would destroy your unsaved work-tree data.1

If you don't want your unsaved work-tree data destroyed, it is your job to save them somewhere. Since Git sees that they're tracked, it suggests that you commit them, even if what you really want is to move them out of your work-tree so that when Git clobbers your work-tree files, it doesn't clobber these files.

If you don't care about the data in these two files, you can remove them yourself. If you do care about the data, don't remove them and don't let Git destroy them—not yet! Save them somewhere, where after Git destroys these copies, you can get them back. Then let Git destroy them, perhaps by clearing the assume-unchanged flag so that you can see what you're doing.

Note: once you've let Git destroy these copies by extracting other committed copies to your work-tree, you're free to destroy the copies Git put into your work-tree, by copying your saved copies into place. Then you can set the assume-unchanged flag again, if you like.


1Remember, all work-tree data—all of your files—are not in Git. Their content is not safely stored for all time.

Git's committed files are safely stored in Git's commits, inside the big Git database in the .git folder. These commits are read-only: frozen for all time, or at least, for as long as the commit itself continues to exist.

Commits contain snapshots. Each commit has a full and complete copy of all of your files—well, all of the files that you told Git to track (and thus had in Git's index) at the time you ran git commit. These files are in a special Git-only format, and de-duplicated, so the fact that there are millions (or hundreds, or whatever) of copies of the same README.md file is not alarming after all: there's really only one copy.

When you git checkout some commit, Git extracts these frozen files—which are in that Git-only format; no other program on your computer can use them—into ordinary everyday files. These everyday files are then yours to fuss with however much you like, in your work-tree. But there's a weird intermediate step: Git first copies2 the files into Git's index, to make things ready for the next commit.

The index is also called the staging area, and its existence is why you have to git add all the time. Suppose a commit you checked out had a frozen-format README.md file in it. That frozen file went into Git's index too, and then Git turned the index copy back into a normal file. That's the README.md you see in your work-tree: the normal file. But if you've changed it, now you need to have Git copy the new README.md back into Git's index / staging-area. That's what git add does: it replaces the existing copy in the index, or puts an all-new-file into the index, as appropriate.

The copies of files that Git puts in Git's index are only half-saved, so they're temporary too. But as long as they're straight out of a commit, that doesn't matter, because the commit saves them forever.

2Technically, the index doesn't have a full copy of a file. Instead, it has the file's name—a full path name complete with forward slashes (even on Windows Git uses forward slashes)—and its mode, 100644 for a non-executable file or 100755 for an executable file, and then a Git blob hash ID. The internal Git blob object is how Git stores (and de-duplicates) file contents. There are also a bunch of fields to make Git go fast, and some flags. But unless you start dumping out the raw index contents, with git ls-files --stage for instance, or updating them with git update-index, you don't need to know this. Even when using the assume-unchanged flag, you still don't really need to know this: you just need to know that Git makes new commits from Git's index, not from your work-tree.

Comments