2

I am on my local my-feature branch

git status reports nothing to commit, working tree clean

I want to switch to develop branch and do git fetch and git merge there (I prefer it over git pull)

However, doing so produces error below

Here I first check status and it shows that all is clean

mymbp:MyProj username$ git status
On branch my-feature
nothing to commit, working tree clean

Next I try to checkout my develop branch which is an existing local branch

On branch my-feature
nothing to commit, working tree clean
mymbp:MyProj username$ git checkout develop
error: Your local changes to the following files would be overwritten by checkout:
    MyProj.sln
Please commit your changes or stash them before you switch branches.
Aborting

It complains that myProj.sln has been changed even though git status says nothing has changed.

Issuing git status again, confirms that nothing has changed

mymbp:MyProj username$ git status
On branch my-feature
nothing to commit, working tree clean

UPDATE 1

Doing git ls-files --stage --debug MyProj.sln shows like below and I dont see any 4000 or 8000 (--skip-worktree or --assume-unchanged flags):

mymbp:MyProj username$ git ls-files --stage --debug MyProj.sln
100644 40c3593ed572beb2139c189455274f8900a1340c 0   MyProj.sln
  ctime: 1541703970:521058155
  mtime: 1541637062:121492660
  dev: 16777220 ino: 8470003
  uid: 501  gid: 20
  size: 55684   flags: 0
mymbp:MyProj username$ 

Issuing git show develop:MyProj.sln shows me number of project files and their GUIDs in the solution, Global sections for pre and post solution but the output is very long showing just Release, Debug configurations and some GUIDS. Not sure what to do with that yet.

UPDATE 2

So, it seams as if MyProj.sln file is in work-tree but not in index and commit (HEAD). Based on @torek explanation, issuing git add MyProj.sln should add this file to index but that is not true since nothing is added and git status returns nothing before I did git add and after I did it. Meanwhile git checkout still complains that MyProj.sln has changed. git diff also returns nothing

UPDATE 3

I also found someone suggest issuing these 2 command to get hash of commit HEAD and to then see what changed in it. I see lots of files duplicate, while some do not. Those that do not appear to be files I added in my current feature branch. Those that are duplicate appear to be files from remote

mymbp:MyProj username$ git rev-parse HEAD
1ca8d8a7c5eff0f2a03eb185f1b25aff27c1d2fd
mymbp:MyProj username$ git ls-tree -r 1ca8d8a7c5eff0f2a03eb185f1b25aff27c1d2fd

And here is the output of it

enter image description here

UPDATE 4

My config is:

mymbp:MyProj username$ git config --list
credential.helper=osxkeychain
core.excludesfile=/Users/username/.gitignore_global
core.autocrlf=input
difftool.sourcetree.cmd=opendiff "$LOCAL" "$REMOTE"
difftool.sourcetree.path=
mergetool.sourcetree.cmd=/Applications/Sourcetree.app/Contents/Resources/opendiff-w.sh "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED"
mergetool.sourcetree.trustexitcode=true
user.name=User Name
user.email=username@somesystems.com
color.ui=true
color.status.changed=blue normal
color.status.untracked=red normal
color.status.added=magenta normal
color.status.updated=green normal
color.status.branch=yellow normal bold
color.status.header=white normal bold
commit.template=/Users/username/.stCommitMsg
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
core.precomposeunicode=true
remote.origin.url=https://github.com/SomeSystems/MyProj.git
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
branch.develop.remote=origin
branch.develop.merge=refs/heads/develop
branch.feat-1.remote=origin
branch.feat-1.merge=refs/heads/feat/feat-1
branch.1234-refactoring.remote=origin
branch.1234-refactoring.merge=refs/heads/1234-refactoring
mymbp:MyProj username$ 
cd491415
  • 823
  • 2
  • 14
  • 33
  • Has the file `MyProj.sln` actually been added to the current branch? – larsks Nov 08 '18 at 18:40
  • What does another git status say – Christoph Nov 08 '18 at 18:43
  • it was there for months but regardless, even if it was added, git status should see it as untracked. Another git status? Every time I do git status in this branch, It says working tree is clean, I rebooted and try few more times, each time same result as above – cd491415 Nov 08 '18 at 18:58
  • If you have the file as untracked and you are getting that message it's probably because it has been added to the branch (by someone else?). – eftshift0 Nov 08 '18 at 19:24
  • no, this is my local feature branch, no one touched it. Plus, the file is not showing as untracked. Git status shows that nothing has changed, git commit shows it has as explained above. I updated to clerify – cd491415 Nov 08 '18 at 19:34

1 Answers1

3

Edit per updates: Something is definitely odd here, though it's hard to say for sure what (the captured output is images so it's not possible to check for weird Unicode issues, for instance). No file should ever be listed twice in git ls-tree -r output on a commit-tree. I have never seen this kind of behavior from Git. There are issues with uppercase vs lowercase, especially on Windows and MacOS, that can cause this kind of behavior, but that doesn't match what you're showing here.

Original answer below

First, a quick note: your tags mention MacOS. MacOS file systems are by default case-insensitive, so that if you have a file named README.TXT and ask the system to view the file named readme.txt, it shows you README.TXT. If you ask the system to add a new file named readme.txt it will instead overwite the existing README.TXT and keep the uppercase name. So watch out for files whose name differs only in case: the Git commit might have a myproj.sln file that will overwrite your MyProj.sln file.


There are at least two possibilities, but let's take the most likely first:

  • This means there is a file that is not in the current index and commit, but is in the current work-tree, named MyProj.sln. This same file is in the commit you've asked Git to git checkout. So Git will overwrite the work-tree file if you do successfully git checkout that other commit. Git is warning you that you will lose the current contents of that file.

  • Or, there is a file that is in the current index and work-tree, named MyProj.sln. The work-tree copy does not match the index copy. Normally, git status would tell you that the file is modified, but you've set one of the two index flag bits that tell Git: don't look for changes to the file, and/or don't tell me about changes if you accidentally find some, just keep the index copy around the way it is. These two flag bits are --assume-unchanged and --skip-worktree.

In both cases, if you do successfully check out the commit you're asking Git to check out, that will overwrite the work-tree copy of MyProj.sln. If that is OK, just go ahead and remove the file now, and the git checkout will proceed.


To see which is the case:

git ls-files --stage --debug MyProj.sln

If you get no output, the file is not in the index (and hence also not in the current commit, based on the git status output, or lack of output). That in turn means that it's just an untracked work-tree file at the moment.

If you do get output, it should resemble this output that I get here for a different file:

$ git ls-files --stage --debug Makefile
100644 b08d5ea258c69a78745dfa73fe698c11d021858a 0       Makefile
  ctime: <number>:<number>
  mtime: <number>:<number>
  dev: <number>  ino: <number>
  uid: <number>  gid: <number>
  size: <number> flags: <number>

The flags: <number> shows the assume-unchanged and skip-worktree bits, although not in a human-friendly format: skip-worktree is 4000 and the other is 8000 (if both are set you'll get c000). Setting the skip-worktree bit, I actually get:

size: 96311   flags: 40004000

while setting the assume-unchanged bit gives me:

size: 96311   flags: 8000

The commit you're asking to switch to (the tip of develop) has a committed version of the file, which you can see (without having it overwrite the current copy) with:

git show develop:MyProj.sln

Note that once you git checkout develop, that file will be in all three active places: the current commit, the index, and the work-tree. If the tip commit of my-feature does not have the file, switching back from develop to my-feature will remove the file from the work-tree. The way to remember most of this is:

  • Commits are made from the index.
  • Commits are therefore extracted into the index first (and then on into the work-tree).
  • The index keeps track of what's in the work-tree that should be committed. The index copy is the one that goes into the commit. git status compares the index to the work-tree to tell you what you should copy into the index.
  • When switching commits, work-tree files that aren't in the index, but need to be based on the new commit, get created; files that are in the index, but need to not be there based on the new commit, get removed.
  • Files in the work-tree that aren't in the index are untracked, and are left alone.

After that point, the assume-unchanged and skip-worktree bits, if you set them, are just minor tweaks to the otherwise largely self-consistent rules. The .gitignore rules start to make sense as well: a file listed in .gitignore is one that git status won't complain about as being untracked. (However, an important side effect, that .gitignore makes Git feel free to clobber such untracked files in some cases, is tougher to remember, or explain for that matter.)

torek
  • 448,244
  • 59
  • 642
  • 775
  • Thanks torek but would you mind elaborating? First, I dont recall setting any flag bits but regardless, how can I check their values and if they were changed? Are there default values? Which values should these flags have? Second, I am fine with overwriting this file (I think at least) but how to remove it? I guess you are saying to just find that file in finder and manually delete it? Thanks again – cd491415 Nov 08 '18 at 20:13
  • Much appreciated. I did all you suggested, your explanation is very helpful understanding what might be going on but I still have question how to get that MyProj.sln file not show as modified. Basically, how to put it in commit since I assume that should work fine since I can build and run my project from my current feature branch? – cd491415 Nov 08 '18 at 21:33
  • 1
    If the file *isn't* in the index, `git add` will copy it into the index, and now it *is* in the index and will be in the next commit you make. If you want it to get committed (saved forever, or at least as long as the commit itself continues to exist), do that. – torek Nov 08 '18 at 22:01
  • Hi Torek, I am not sure if you saw my UPDATE 1 and UPDATE 2 above. But I tried issuing git add as explained above. UPDATE 1 shows all output I get when following your awesome explanation. UPDATE 2 shows that nothing change if I do git add on my feature branch – cd491415 Nov 08 '18 at 22:32
  • ... also added UPDATE 3 which might reveal something interesting I havent noticed before – cd491415 Nov 08 '18 at 22:51
  • 1
    Had not seen the updates, but they are peculiar. No file should be listed twice under the same name; here many are listed twice (with the same blob hash ID as well). These don't *appear* to be tricky names: there's no obvious upper- vs lower-case issues for instance. It's possible there are UTF-8 encoded names but one usually sees this issue with files named, e.g., `schön` and there are no accents I can see here either. – torek Nov 08 '18 at 22:58
  • correct, and there are many more of them of all types, xml, xaml, png, cs, csproj, ... One thing I though was that it might be the fact that while I work on Mac, most of my team uses Windows. I thought it could be line endings but dont know how to confirm that. My config has setting core.autocrlf=input if that tells you anything. I added UPDATE 4 with listing of git config --list – cd491415 Nov 08 '18 at 23:20
  • 1
    Line endings don't affect internal Git data structures. You could use `git ls-tree -r HEAD | vis -ctl` to show tabs and newlines as `\t` and `\$`, perhaps, but I'm suspicious that someone's been using a broken version of Git and generating bad tree objects. – torek Nov 08 '18 at 23:42
  • That shows \$ at but at the end of the each line I showed to you in screenshot under UPDATE 3 above, not inside the files themselves. – cd491415 Nov 09 '18 at 15:51
  • 1
    Well, yes, the point of the `\$` is to make any *trailing* white-space visible, in case one file is named X and another named X, for instance. The `vis` command would also encode various control and escape sequences so that they become visible; `-t` encodes tab (as `\011` or, with `-c`, as `\t`). – torek Nov 09 '18 at 16:01
  • OK, so I do see \t and \$ at the end of each line in that screenshot duplicated line or the one not duplicated. So, I guess line endings are not an issue either here. Have you had a chance to look at my config listing in UPDATE 4. Is there anything you find strange there? – cd491415 Nov 09 '18 at 16:07
  • I did, and nothing stands out in the config. I think someone is creating bogus tree objects. The only way to tell for sure would be to examine the raw Git repository itself, though, and that's difficult (not to mention sensitive depending on whether it's open source). – torek Nov 09 '18 at 16:34
  • I know that following suggestion wont resolve the issue in that case but it would make me confident that the problem is not on my machine. I am thinking to: (a) backup my project to another directory, (b) nuke my real project with its .git, (c) uninstall git and SourceTree from my mac, (d) then install and set git and SourceTree again, (e) get fresh clone of MyProj project, (f) manually apply my changes one-by-one from the backed up copy. Crazy but I see no other way than going this radical. Any better suggestion? Much appreciated – cd491415 Nov 09 '18 at 17:01
  • You can export your commits in a format Git can digest using `git format-patch`. Probably a good idea to do other backups as well first, though. (But I'm pretty sure it's not your installation.) – torek Nov 09 '18 at 18:14
  • how do I export all my commits in my local feature branch (no one touched it other than me) using git format-patch? I tried googling but nothing for this specific case. Branch is mine, local and no one touched it. Also, is there a simple way to confirm problem is not on my machine? I tried getting fresh copy from remote and the files were not duplicated like in my case. – cd491415 Nov 09 '18 at 18:22
  • Find the last commit that's *not* yours (by hash ID or branch name), and run `git format-patch --stdout ..my-feature > ` (replace and with appropriate parts). View the resulting file to make sure it looks right: it should be a mailbox-formatted file with one "mail message" per commit. On any other clone, you can `git checkout -b my-branch; git am ` to re-insert all those same patches. Note that this cannot preserve merges, only non-merge commits. – torek Nov 09 '18 at 18:26
  • hmmm, that is the reason I stopped looking into patch yesterday. I worked on my branch for about 2 - 3 weeks and merged with remote cpl times. TBH at this stage, I dont even care if I loose the history of my commits, anyways it never went to remote. I am thinking my idea to reinstall git, and then copy manually all changes one-by-one would be better. That way I will not carry over anything that might have led to this issue. I will loose commit history yes, but not the code. – cd491415 Nov 09 '18 at 18:31
  • You shouldn't have to do anything manually. Run `git diff A B > mydiff.diff` to get the difference between commit A and commit B. Then you can `git apply mydiff.diff` to apply it somewhere else. Merging does this under-the-hood, but when doing it manually you can inspect the diff and fix any issues you see before applying it. – Nicholas Pipitone Nov 12 '18 at 18:14
  • @torek Does this SO post gives you any clue what could be wrong? https://stackoverflow.com/questions/53326516/git-switching-to-a-local-develop-then-back-to-feature-branch-resets-the-statu?noredirect=1#comment93565062_53326516 – cd491415 Nov 19 '18 at 21:53
  • 1
    Nothing obvious, though I'm now wondering if you have any clean and/or smudge filters that are doing something non-obvious. But that wouldn't mess with the *tree* objects, and the config you listed has no filters defined. – torek Nov 19 '18 at 21:58
  • @torek Thanks. Is this giving you any hints? https://stackoverflow.com/questions/54001982/git-add-wont-stage-files-git-cache-confused?noredirect=1#comment94858387_54001982 – cd491415 Jan 03 '19 at 16:55
  • @cd491415 no, but it sure looks like it's the same issue. – torek Jan 03 '19 at 18:04
  • @torek anything you can add to this https://stackoverflow.com/questions/54083919/git-fatal-the-remote-hung-up-unexpectedly-but-not-rpc-or-permission-or-broken ? Thanks – cd491415 Jan 08 '19 at 15:45
  • No, but it looks like it's a symptom of commits made while the duplicate entries were in your index. Git just packaged up the index contents, assuming they were OK, when they weren't, and hence made bad tree objects. – torek Jan 08 '19 at 15:55