Home > database >  Got "error: The following untracked working tree files would be overwritten by checkout" w
Got "error: The following untracked working tree files would be overwritten by checkout" w

Time:07-20

Current branch: 20220620_142027_1, there is directory named pages/Auth.

Try to rebase the master branch using the git rebase master command, got the error:

error: The following untracked working tree files would be overwritten by checkout:
        client/modules/activity/pages/auth/components/ActivityForm/index.tsx
        client/modules/activity/pages/auth/components/CampaignProductForm/index.tsx
        client/modules/activity/pages/auth/components/ChannelForm/index.tsx
Please move or remove them before you switch branches.
Aborting
fatal: Could not detach HEAD

The directory name pages/Auth is the lowercase on the master branch, it is pages/auth. Another feature branch 20220621_142346_1 modified all directory names from uppercase to lowercase and merged them into the master branch.

I want the directory with the lowercase names to be applied to the current branch 20220620_142027_1, override them. I have read many similar questions(Q1) but didn't find a clear solution. How can I solve this? Thanks.

CodePudding user response:

No matter how it manifests, the problem here is always the same. What changes from time to time is the shortest correct solution.

Let's state the problem as clearly as possible, given the issues where Git can store files that are named both This/File and this/file, but some OSes can't. The problem goes like this:

  • Commits are full snapshots of every file (plus metadata, but we're concerned with the snapshots here).
  • Switching from one commit to another (as implied by switching from one branch to another, or using git switch --detach or git checkout --detach, for instance) requires removing and/or replacing some files in your working tree, where you do your actual work between commits. This is what git rebase is about to do: git switch --detach master.
  • Some files are tracked, meaning "are in Git's index right now".
  • Some files are untracked, meaning "are not in Git's index right now".

Let's say that some file to be removed—whether or not it will be replaced by some other file with the same name—is named path/to/file.

  • If that file is tracked and not modified, the contents of that file are safely stored in the current commit. It's safe to remove the file!
  • If that file is tracked but modified, the contents of that file are not safely stored in the current commit. It is not safe to remove this file and you will generally get an error, saying that this tracked file has changes you should commit or otherwise save.
  • If that file is untracked, the contents of that file are not safely stored in the current commit. It is not safe to remove this file and you will generally get exactly the error message you see above.

(Note: this does not actually cover every possible case, as a file can be in the current commit, but removed from the index with git rm, but present in the working tree because it was created or was removed with git rm --cached. I'm just going to ignore this special case: it doesn't really affect any of this.)

On a case-insensitive file system (as typically found on Windows and macOS), this applies to a file named path/TO/file or Path/to/file or path/to/FILE or whatever, regardless of the upper and/or lower case-ness of the that that is to be removed. Therefore, for a new file that will be created that has only a name-case difference, such a file will in effect be removed. Note that all we have done here is add a condition:

  • if the current commit lacks a file path/to/file (in any mix of upper and lower case) and the new commit has a file PATH/TO/FILE (with some case that doesn't match the next "and") and you currently have a path/to/file (with some case that doesn't match), we say that path/to/file is to be removed.

    On a case-sensitive file system, the untracked path/to/file won't be removed by creating PATH/TO/FILE or path/to/FILE, so we don't add this to our problem case.

With this problem in mind, the general solution is obvious

Git complains about particular file(s) because Git knows that it is going to destroy their contents, which Git does not have saved in the current commit. So the solution is clear: you must save the contents if they are valuable.

That's it! That's the whole answer—well, almost. There are several remaining issues:

  • What's the shortest / fastest solution? This might not matter too much, really; you should concentrate on a correct solution instead of a fast one here unless you have to do this constantly, and in that case, you should either automate a solution, or reconsider your workflow so that you're not encountering this problem all the time.

  • Where should the contents be saved? This one is pretty important! If the contents aren't valuable, the question goes away, but if they are, it's a pretty big deal.

  • On a case-insensitive but case-preserving file system, what, if anything, should you do about "wrong case" folders? That is, Git knows that PATH/TO/FILE (as stored in Git) requires creating a folder PATH, and a folder TO within PATH, and then the file named FILE. But if you already have an existing path and path/to, or PATH/to, or whatever, Git won't be able to create a new folder that differs only in case, and won't even try, it will just use the "wrong-case" one.

This last problem is likely the stickiest, and there is sometimes no easy answer because you must decide what to do about problem cases. The big problem case is when you want to have both path/TO/fileA and path/to/fileB, because you literally can't have that. You will have to pick one, and you may wish to adjust the names of files in new commits you make.

In my opinion, the way to handle this last problem is to do your Git operations on a case-sensitive file system (that's easy to do on macOS, just make yourself a case-sensitive file system, clone there, and do the job) until you have everything arranged correctly. Then go back to your case-insensitive file system if you like, blow away the existing working tree (you copied those changes to the case-sensitive file system to commit them so they're now committed), and check out the commit so that you get the right folder-case. The problem itself simply ceases to exist while you're working in the case-sensitive file system, so it's easy to set everything the way you want.

This leaves us with the "where to save" question

We have now pinned the problem down to a solveable one: you have some files that need to be saved somewhere. They have valuable data, which you may want back. All you have to do now is choose where to save them. There are only two options, really:

  • you can save them in the repository (in a commit), or
  • you can save them outside the repository.

For tracked files, the former—save them in the repository—seems to be likely to be the best choice. Just add and commit the data! It's now in a commit, and you have it saved as long as that commit continues to exist. Make the commit on a new, temporary branch that you create just now for that purpose, if you want, but if the goal is to do a rebase, I recommend that you add and commit now, then git reset the extra commit away later.

For untracked files the choice is slightly more difficult. Note that these untracked files can be either ignored or not-ignored, though if they are ignored, Git sometimes feels free to destroy them willy-nilly and you might not have gotten this error at all. If they're not ignored, you can easily git add and git commit them. If they are ignored, you can still add them (with git add -f) and commit. So either way you can treat them the same way as you would a tracked file. The tricky-ish part is remembering to un-track them later, although git reset --mixed—which is the default—will actually do all the work for you, so if you reset away an extra commit you've made, there's nothing actually hard here.

Your other option is of course to just copy or move the files out of the repository. Make a parallel directory, outside the working tree, and drop them there. This works for both tracked and untracked files, with the difference being that for a tracked file, you'd copy the working tree version and then use git restore to de-modify it, and for an untracked file you'd just mv (rename) it to its temporary storage area.

Conclusion

You mainly need to save the file data somewhere. In other words, you need to do what Git just told you:

Please move or remove [these untracked files] before you [rebase]

except that you also have the option, in this case, of adding (perhaps with -f / --force) and committing the untracked files. However, in your case you have one last complication, which is that same case-folding issue I said is the hard problem:

The directory name pages/Auth [has become] lowercase ... pages/auth [and] I want the directory with the lowercase names to be applied to the current branch 20220620_142027_1 ...

Here's where building yourself a case-sensitive file system will help a lot.

We use a rebase operation when we have some commits that we mostly (or somewhat) think are fine, but there's something about these commits that we don't like. See also, e.g., my answer to Git interactive rebase HEAD not point at last commit. In your case, you're running a non-interactive:

git rebase master

because the thing you don't like about your existing commits is that they don't come after the last commit in branch master—everything else about these commits, you liked.

But now you've changed your mind. There is one other thing you don't like about these commits, and that is: they have uppercase pages/Auth/... paths in them. You could fix both of these problems in a single interactive rebase by choosing edit instead of pick for each commit (again, see Git interactive rebase HEAD not point at last commit). Then—if you were on a case-sensitive file system!—you could git mv each of the pages/Auth/* files to pages/auth/, to rename them to lowercase, with no issues involved at all, see that the renaming worked, and use git commit --amend and git rebase --continue to move on to the next commit to be edited. When the rebase finishes, all the names would be all-lowercase, exactly as desired.

If you can't make a case-sensitive file system, you can still do the job, it's just painful. See, e.g., Filename capitalization changes ignored in git Git on Windows, Changing capitalization of filenames in Git, and Change case of a file on Windows? A simple git mv is supposed to work, and does in many versions of Git, but doesn't work in all of them. The various answers here provide various workarounds that can help out.

Remember that Git ultimately:

  • stores only files, not folders: files have long names that include forward slashes, e.g., path/to/file;
  • is case sensitive.

Git tries to accommodate case-insensitive file systems on macOS and Windows, and mostly does an OK-ish job, but if you can work in a case-sensitive file system, the whole problem goes away.

(Because every commit holds a full snapshot of every file, and git rebase is copying each commit one at a time, you really will have to do your renaming one commit at a time. You can completely automate it, but unless you're doing this a whole lot, there's no real point.)

  •  Tags:  
  • git
  • Related