50

WHAT I WANT TO DO

I have a file which contains sensitive datas so I don't want to push content of this file to remote server.

WHAT I DID?

To achieve this, I made a commit when the file was empty and pushed this empty file to server (GitHub). And then fill the file with sensitive datas and applied git update-index --skip-worktree path/to/file . But I didn't made any commit.

Now I'm trying to switch my branch but I'm getting this error :

    error: Your local changes to the following files would be overwritten by checkout:
    path/to/file
    Please, commit your changes or stash them before you can switch branches.
    Aborting

WHY I USE skip-worktree INSTEAD OF assume-unchanged?

I read a few SO questions about this subject, and found Borealid's answer.

--assume-unchanged assumes that a developer shouldn’t change a file. This flag is meant for improving performance for not-changing folders like SDKs.

--skip-worktree is useful when you instruct git not to touch a specific file ever because developers should change it. For example, if the main repository upstream hosts some production-ready configuration files and you don’t want to accidentally commit changes to those files, --skip-worktree is exactly what you want.

After this, I found Jeff's question and VonC's answer. Jeff's problem is almost same with mine, and I followed VonC's solution. However it's not work for me. Maybe because of git version difference. Because that question from 2012. We talked with VonC and he said to ask this as a new question because he couldn't remember answer.

I tried to use --assume-unchanged and --skip-worktree together, and soft reseting worktree. But nothing changed.

SO?

Can you help me about my problem ?

Thank you.

Community
  • 1
  • 1
Eray
  • 7,038
  • 16
  • 70
  • 120
  • 2
    I tried to reproduce the problem, but it worked for me. The only way I could reproduce it was if my private file was different between my branches. Did you double check that `path/to/file` is exactly the same between your branches? Do `git diff branch1..branch2 -- path/to/file`. – Justin Howard Apr 10 '15 at 01:35
  • 5
    @Eray Justin is right. You have to make sure that the file in question (before --skip-worktree) is the same across your branches. – Kise May 30 '17 at 02:49
  • I'll add that this also happens if path/to/file doesn't exist at all in the branch you're trying to check out. Because when you try to checkout, git attempts to delete that file not being present in the checked out branch. It's implied by the statement "You have to make sure that the file in question is the same across your branches", but I think it's better to make it explicit – yfede Oct 21 '21 at 16:24

8 Answers8

8

I haven't been able to find a neat solution to this, so I'm using a .bat file to run --no-skip-worktree on the affected files. I then stash, switch branch, stash apply, and then use another .bat file to run --skip-worktree on the same files.

It's not nice, but it's the simplest and quickest way I've found so far.

Update: I've run into this problem again so have created a PowerShell script instead of the .bat file mentioned above. It will prompt the user whether they want to skip or no-skip the files. Update the two parameters and you're ready to go:

[string]$repoPath = "C:\Fully\Qualified\Path"
[string[]]$filesToSkip = @(
    "Local/Path/To/File.txt",
    "Local/Path/To/OtherFile.txt"
)

$skipText = Read-Host -Prompt 'Skip files? (y/n)'
$skip = $skipText -like "y"
cd $repoPath
foreach ($file in $filesToSkip)
{
    if($skip)
    {
        Write-Host "Skipping" $file
        git update-index --skip-worktree $file
    }
    else
    {
        Write-Host "No-skipping" $file
        git update-index --no-skip-worktree $file
    }
}
stevospinks
  • 137
  • 2
  • 9
  • Could you please share your .bat file? – Woland Dec 18 '17 at 05:22
  • @Woland I'm afraid I don't have a copy of this anymore, but if I remember correctly there was one that only contained `--no-skip-worktree` for each file and another that only contained `--skip-worktree` for the same files. – stevospinks Dec 19 '17 at 12:07
  • 1
    @Woland I've just run into this problem again, so I've updated my answer and added a PowerShell script you can use – stevospinks Nov 19 '19 at 13:43
5

Well, this is a cruddy solution, but seems to work reasonably well (though it is only lightly tested):

Create a file named git-checkoutsw somewhere in your PATH, with the following content:

#!/bin/bash
ignored=( $(git ls-files -v | grep "^S " | cut -c3-) )
for x in "${ignored[@]}"; do
  git update-index --no-skip-worktree -- "$x"
done
git checkout -m $@
for x in "${ignored[@]}"; do
  git update-index --skip-worktree -- "$x"
done

The script captures the list of ignored files, unignores them, checks out the new branch with -m (for merge) and whatever other parameters have been specified, and then ignores them again.

Now you can do git checkoutsw instead of git checkout.

Raman
  • 17,606
  • 5
  • 95
  • 112
2

You can enable sparse checkout and add the file there together with adding the skip-worktree flag (if you add the sparse checkout alone it probably will be deleted).

To exclude a file you should put to sparse-checkout file (see git-read-tree manual):

/*
!unwanted

Then it will not be touched at update (so you don't get content of the other branch there, instead preserving your edits). It might be not what you want.

max630
  • 8,762
  • 3
  • 30
  • 55
2

I got the same problem and it boiled down to be casued by incoming changes on the file I wanted to assume-unchanged on.

So if I just do no-assume-unchanged on the file, stash it, checkout the latest version, pop my stash, and assume-unchanged again.

So now I'm able to switch branches again.

harre
  • 59
  • 7
  • 2
    This was downvoted, but it actually explains the underlying problem. The solutions above solve the problem, but don't explicitly say what is wrong - it was helpful to me. – nitsujri Dec 23 '20 at 13:14
1

Is there a reason to have an empty variant of that file checked in at all? If not

git rm --cached path/to/file
echo 'path/to/file' >> .gitignore
git commit -m'stop tracking path/to/file'

might solve your problem. (Will have to be repeated for each branch that has the file. Or if you don't mind history-rewriting, use git filter-branch to get rid of it in previous commits, too.)

das-g
  • 9,718
  • 4
  • 38
  • 80
  • I pushed empty variant of file, otherwise other files which including this `sensitivefile.php` and those files will produce **file not found** error. So there must be a `sensitivedata.php` file, doesn't matter empty or not. – Eray Apr 09 '15 at 21:44
  • Have you considered simply creating the file as part of your build process? A common pattern is to have a `./configure` script that is expected to be run both, before using the project in some IDE and before full builds. As long as it's as innocuous and (computationally) cheap as just `touch`ing a file, it might be worth calling that script from a [`post-checkout` hook](http://www.git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#Client-Side-Hooks). – das-g Apr 10 '15 at 21:52
1

Just find one line to do --no-skip-worktree on all files

git ls-files -v | grep "^S" | cut -c 3- | xargs -L1 git update-index --no-skip-worktree

You can easily make a little alias.unhideall with that command :)

Mickaël B.
  • 325
  • 4
  • 14
0

So far, --assume-unchanged and --skip-worktree do not work as I found. My git version is : git version 2.8.4 (Apple Git-73) Stash is the only way that works so far.

0

As a comment mentioned (I'm only writing this here as an answer hoping people who don't read comments or might miss them see this), git update-index --skip-worktree path_to_file works but only if the file is the same across all your local (I'm assuming local) branches.

What worked for me in my case was:

  1. git update-index --no-skip-worktree path_to_file to undo skipping first of all.

  2. deleted all my local branches, (You can use: git branch | grep -v "master" | xargs git branch -D), went to master and modified the file with the sensitive information.

  3. git update-index --skip-worktree path_to_file again.

  4. git checkout any__new_local_branch works just fine now.

Credits and thanks to @kise in the comments on the original question.

Yuniac
  • 343
  • 3
  • 11