24

I'm in a situation where I want to open source my project, however there's a single source file that I want to release a "clean" version of, but use a separate version locally. Does git have a feature where I can just commit a file once, and it stops looking for changes on that file from now on?

I've tried adding the file to .gitignore, but after the first time when I do a git add -f and git commit on the file, and I proceed to edit it again, git status shows the file as changed. The ideal behavior would be for git to not show this file as changed from now on, even though I've edited it.

I'd also be interested in how others have dealt with "scrubbing" their codebases of private code/data before pushing to an open source repo, especially on Git.

Wim Coenen
  • 66,094
  • 13
  • 157
  • 251
Suan
  • 34,563
  • 13
  • 47
  • 61
  • There are git internal reasons for this, but FYI, the reason why your `.gitignore` trick didn't work is that the matches by `.gitignore` only apply when git is searching for unversioned files to add. This means that it will (1) show up in `git status` and (2) be added when you `git add .`. – Steven Jan 17 '11 at 05:52
  • Workflow solution: Have a separate repository directory for your important branch. You can now copy files between them with your file explorer. – nathanfranke Oct 21 '22 at 04:33

5 Answers5

22

You can do this with Git's sparse checkout feature. Run the following commands in a repo where you want a special, untracked local.txt file:

git config core.sparsecheckout true
echo '*' >.git/info/sparse-checkout
echo '!local.txt' >>.git/info/sparse-checkout
git read-tree --reset -u HEAD

This will first delete the existing local.txt file. But now it's ignored from Git's perspective, so you can put a machine-specific one there and Git won't bother you about it.

You can then add and manage the published local.txt file from some other repository. Git will be happy to track this file and keep it in the repository, but on a machine with the above sparse checkout configuration, Git will ignore the local local.txt file in that working directory, even if it is different from what's in the repository.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 3
    @foo: sure, edit the `.git/info/sparse-checkout` file, remove the `!local.txt` line, and rerun the `git read-tree` command. – Greg Hewgill Apr 11 '11 at 19:01
  • 2
    I've found that this method is very robust. More than other other methods I've seen that use `git update-index --assume-unchanged` or `git update-index --skip-worktree`. Cheers! – TheAmpersand Jan 17 '13 at 17:25
16

use update-index, here are the docs.

Here is a basic example freezing a file foo.rb:

git update-index --assume-unchanged foo.rb

Then, to get it back:

git update-index --no-assume-unchanged foo.rb

Or for simplicity, add it to .gitconfig

freeze = update-index --assume-unchanged
thaw = update-index --no-assume-unchanged
...
git freeze foo.rb
git thaw foo.rb

Note: This answer was originally provided by @TheAmpersand in comments above. I thought it was important enough to formalize.

ynkr
  • 25,946
  • 4
  • 32
  • 30
6

Greg's approach very adequately and precisely answers your question.

Let me make an addition that takes into account what I infer to be your high-level objectives:

  • Don't release local data to the wild
  • Release a clean version into the wild

The simplest (and in my experience, best attested) way to accomplish this is to use the following structure:

Versioned

./application.code
./config.code
./config.local.code.sample
./.gitignore

application.code

include('./config.code')
if is_file('./config.local.code')
  include('./config.local.code')
end

config.code

username = 'root'
password = 'password'

config.local.code.sample

username = 'replace with your local username and rename file to config.local.code'
password = 'replace with your local password and rename file to config.local.code'

.gitignore

config.local.code

Unversioned (optionally)

./config.local.code # will not be picked up by git due to .gitignore

config.local.code

username = 'Suan'
password = 'myownpass'

Result

As you can see, you avoid releasing config.local.code, which actually contains your sensitive information, into the wild. The default configuration (cloned straight from a remote) will work for a reasonable, if rare, case where username root and a password password is valid. And the aptly named config.local.code.sample provides local customization instructions.

Steven
  • 17,796
  • 13
  • 66
  • 118
0

I've solved a similar situation like this for config files in Django by using symbolic links. It seems you're looking for versioning capabilities instead of config file management, but I'm sure this little strategy could help. Here are the steps I usually take:

Say the file you want to update locally (and not to be shared) is named 'local.file' and the file that you want released (in the wild) is 'release.file'... You need to create a symbolic link file, let's call this symbolic link file 'actual.file'. The symbolic link file will either point to local.file or release.file at any given time (or be missing if you just cloned the repo).

  1. gitignore local.file and actual.file so they aren't tracked since you don't want these to be kept track of or released. actual.file is always pointing to the file that you want to work with.
  2. Make the symbolic link point to local.file when you're working on your 'non-frozen' version-ed file.
  3. Make the symbolic link (actual.file) point to 'frozen' release.file when you want to make manual updates on it or simply copy changes from local.file to release.file.
rpq
  • 1,287
  • 2
  • 11
  • 9
0

One way is to have your changes to that file in a local branch. Do your work in that branch, then rebase ignoring your changes back into the actual branch that development is happening in.

Yuliy
  • 17,381
  • 6
  • 41
  • 47