I have an untracked config.js
that stores a few development-mode variables in my local repo and a corresponding config.js
on my VPS with the production-mode versions of the same variables. But I want to try out Github Pages. How do I add such an untracked config.js
to my Github repository so that it doesn't get replicated when I do a git pull
on my laptop? Or, if there is no way I can do this, is there any other way I can achieve this? That is, store some variables separately from the repo but use them for Github Pages website?
CodePudding user response:
You can't. There's a technical wording glitch here, but "you can't" is the short answer. See below for details.
Is there any other way I can achieve this? That is, store some variables separately from the repo but use them for Github Pages website?
There are several options; I have no idea which ones might be good on GitHub Pages specifically. This is, in fact, a Frequently Asked Question from the Git FAQ, where it's disguised as "How to I tell Git to ignore tracked files?":
A common reason to ask this question are configuration files which are added to a project as an example or default, but which individual developers may want to change. Keeping uncommitted changes around is undesirable in the long run, because git diff will always show the changes and they will be added for commit if commands such as
commit -a
oradd -u
are used.This problem can be solved in different ways:
- Option a: Rename the configuration file to config.template or something like that, and tell the user to use it as a template. The install script for your system could also copy the template configuration to the appropriate location.
- Option b: Allow configuration options to be overwritten in a seprate file. For example, in Makefiles it is common to add a line
-include config.mak
, which allows users to add custom configuration inconfig.mak
, rather than changing the Makefile itself.- Option c: Make Git ignore changes to the configuration file. There is no automatic centralized way to do that. But on a per-clone basis you can tell Git to do it using either
git update-index --assume-unchanged <file>
or sparse checkout (see the corresponding section in git-read-tree(1) for details). Note that neither feature was designed for that purpose. Use at own risk.
I generally prefer the "template" option (option A). Your software can typically do something like:
if exists("real-config-file"):
load("real-config-file")
else:
print("notice: using template config as fallback")
load("template-config-file")
and now you get a working system even if you haven't configured it, but once you do configure it, you get a working system with your configuration. The "install script" trick can also copy the template into place as the default initial configuration, provided there's an install phase in whatever system you're using.
The gory details
The phrase tracked file, in Git, refers specifically to any file currently in the index. "The index" is a thing that Git calls by three different names: index, staging area, or cache. I'm using "index" here because of the git update-index
command and because the word "index" is relatively meaningless (which means it doesn't drag in too many false assumptions).
The index isn't actually part of any commit, and a repository is primarily a collection of commits. So whether some file exists in some commit or not isn't the right question to ask—at least, not yet. The right question is: How, when, and why do files wind up in the index?
The answer to why is the easiest. A file goes into the index because you would like that file to be in your next commit.
The answer to when and how are trickier, because git add
and git rm
let you manipulate the index one file at a time, and other Git commands manipulate the index en-masse. The big important ones are perhaps these:
- The act of checking out a commit, with
git checkout
orgit switch
, fills in the index (and your working tree) from a commit. - The
git reset
command (in its "entire commit" mode) can reset the index to match a particular commit. It will do so unless you use--soft
. - The
git merge
command uses the index to achieve the merge; the merge result is in the index (and your working tree).
So, if some file is in some particular commit, checking out that commit, or merging with it, will cause that file to wind up in the index. As such, that file will be a tracked file.
But just because some file (index.js
or whatever) is in commit a123456
doesn't mean that that file is in commit b789abc
. If you switch to commit b789abc
and that commit lacks index.js
, the file will be removed from the index, if it's there now (provided that the git switch
or git checkout
succeeds).
The thing is, if it's removed from Git's index, it's also removed from your working tree. That is the effect you wish to avoid. So you must therefore avoid switching away from a commit where the file does exist, and therefore has been put into your index, to a commit where the file does not exist, and therefore is removed from your index and your working tree. Well, except when you want to observe the resulting behavior, which is in fact every time you're using that commit!
So Git is (sort of) doing what you want. It's what you want to want that's not reasonable.