0

I'm on Windows using Git version 2.12.2.windows.2.

I have a Git history originally converted from Subversion. I recently realized to that had a file foo.txt~1~ in the repository. (This must have been from an editor in eons past.) I realized this because every time I would clone the repository, Git would immediately tell me that foo.txt had been modified. Apparently something about the foo.txt~1~ file made Git think that foo.txt had been modified.

So I figured I'd solve the problem by deleting foo.txt~1~ and making a commit with the log message, "Removed back up text file." Let's call this commit 01234

Then a few commits later, I decided just to get rid of the text file altogether, so I did:

git filter-branch --tree-filter "rm -f foo.txt~1~" --prune-empty HEAD

So now I think I have foo.txt~1~ out of the history. Oddly, the diff for 01234 says that I removed all the lines of foo.txt and added back all the lines of foo.txt.

In any case, I want to remove commit 01234 from the history, because there's no point with having a do-nothing commit saying that I removed a backup text file that no longer exists, so I did a git rebase -i 01234~ and selected drop to get rid of the commit.

Unfortunately Git suddenly stops and says:

Cannot rebase: You have unstaged changes.

Git shows that foo.txt is modified; it shows that all of the lines of the file have been removed and replaced... with the same lines.

Git says that I can use git checkout -- foo.txt to discard my changes, but after doing that git status still shows that foo.txt is modified.

Git also says that I can use git add foo.txt to update what will be committed. So I use git add foo.txt to add the file. It tells me that I have staged changes. So I use git commit -m "Added non-modified text file." to commit the "changes". Then I did git rebase --continue again.

That succeeded --- but of course now I have an "Added non-modified text file" commit in the history. So I tried again to git rebase -i 01234~, then drop that commit... and the whole cycle starts up again:

Cannot rebase: You have unstaged changes.

Sometimes I want to get rid of Git.

How do I fix this?

P.S. I have a single copy of the original repository before I tried to remove foo.txt~1~ from the history with git filter-branch, if that helps. But I don't have a copy of the repository before I added single commit removing foo.txt~1~ (commit 01234), and that commit is not the last commit.

P.P.S. I thought this might have something to do with Windows and ~ in the filename, so I tried this on Linux using Git 2.12.2. Exactly the same thing happened.

Garret Wilson
  • 18,219
  • 30
  • 144
  • 272
  • Windows *does* have issues with names containing tildes (and other names). It's not clear to me what's going on, especially if you are doing this *on* Windows with its various "issues" (not that Linux is issue-free :-) but at least there, one usually has a workaround available). But it should be possible to clear it all up on Linux. The specific diff for the one file sounds like LF vs CRLF line ending issues (another Windows bugaboo, but one that you can import *to* Linux by accident, via `.gitattributes`). – torek May 25 '17 at 07:38
  • See the P.P.S. above --- I _did_ try this on Linux, and the same thing happened. And you're missing the point that Git reports no modifications _before_ the rebase, but then stops in the middle of the rebase saying the file is modified, and there's no way to get around it. Anything I do to get around it leaves the problem in the history. – Garret Wilson May 25 '17 at 13:28
  • No, I'm not missing that point: that's the kind of behavior you see when there are end-of-line issues. It occurs because cherry-pick copies commits, and during the copying, end of line conversion can get in the way. I'd need to have the repository in question to fully diagnose the problem, though. – torek May 25 '17 at 15:18
  • @torek, I wasn't sure you realized that this occurred in the middle of rebasing, but now I see you did understand this. I wasn't expecting Git to barf on something that was already in the repository. See my answer. – Garret Wilson May 25 '17 at 16:14
  • Yes, the problem—well, one of several, really—is that rebase (and various other actions) *copy* commits and can apply new or different EOL and/or gitattribute settings as they go. On Unix-y systems where you don't normally have things mess with your data, it's kind of shocking when Git messes with your data. :-) – torek May 25 '17 at 18:15

2 Answers2

1

It turns out that this had nothing to do with a tilde ~ character in the filename; that such a file (foo.txt~1~) appeared as a "sidecar" to the file Git thought was modified (foo.txt) was merely a coincidence.

The problem indeed came down to line endings. I have long since resolved the line ending issue with a .gitattributes file, but when I converted this repository from Subversion I had a script that converted files from CRLF to LF. This script came with a long list of files with endings to convert. I had not included this particular file extension (it was actually jsp and not txt, but I'll contine to use txt for consistency), which apparently meant that all my .txt files got stuck with CRLF endings in the Git repository.

I suppose because of my snazzy new .gitattributes file (which in fact does include .txt), when doing the rebase Git checked out the foo.txt file using CRLF endings but in the index it used LF as it should, which differed from the CRLF in the repository itself. (Or something to that effect; Git is complicated.)

So my objective was first to normalize the newlines and then deal with the foo.txt~1~ file. Here's how I finally went about it. I switched to a Linux machine (simply because it had dos2unix handy) and started with the repository before I had changed anything (which still had the commit in the middle where I erased foo.txt~1~, thinking that was the problem):

#normalize EOLs on `*.txt` and `*.txt~1~`
git filter-branch --tree-filter '/path/to/normalize-eol.sh' -- --all

#drop the commit which deleted the `.txt~1~` file
git rebase -i 01234~ #and specify "drop"

#then do the magic from https://stackoverflow.com/a/43961960/421049
#  to rejoin some tail branches

#now delete the `*.txt~1~` files throughout history
git filter-branch --index-filter 'git rm --cached --ignore-unmatch -r *.*~*' HEAD

The normalize-eol.sh script looks something like this:

find . -type f -iregex ".*\(\.txt\|\.txt~1~\)" -not -path "./git/*" | while read file; do dos2unix "$file"; done

(That code was gleaned from scouring Stack Overflow years ago when I first did this conversion.)

This may be my worst Git nightmare yet, and (yet again) from such a silly little thing as line endings.

Garret Wilson
  • 18,219
  • 30
  • 144
  • 272
-1

This means that your commit DOES actually have changes. It may be something like line endings that you can't see.

Don't get rid of git. Don't hate the player, hate the game.

Adam Dymitruk
  • 124,556
  • 26
  • 146
  • 141
  • You're not getting it. I do a `git status` before the `git rebase`, and there are no modifications of anything. The rebase starts fine, but when it gets to that commit, it stops and says that a file suddenly has a modification, during the rebase. If there were no modified files at the start of the rebase, how can there be a modified file during rebasing if I don't touch anything? – Garret Wilson May 25 '17 at 02:38
  • When using `git rebase`, you're actually copying commits—and the copy is done by a `git checkout` and then `git cherry-pick`. The cherry pick step performs a merge. If you have CR/LF issues, the merge may not go well. There are control knobs for this (`merge.renormalize`) that interact with the control knobs in `.gitattributes` and `core.eol` and so on, and the result is potentially extremely confusing. – torek May 25 '17 at 07:41
  • Thanks for the explanation @torek. And Adam Dymitruk, yes it was line endings, but I thought you were saying that I had made an explicit modification. It surprised me that Git would be stumped by code in the repository already. See the explanation in my separate answer. Adam, Stack Overflow won't let me modify my downvote unless you modify your answer, so please make some changes and I'll change the downvote to an upvote. I probably won't accept it as an answer, though, because it really didn't give me much direction on how to _solve_ the problem. – Garret Wilson May 25 '17 at 16:18