How to Include Certain Files Only in Tagged Commits (with npm)

Certain situations and tools (cough, bower, cough) require that you check-in build artifacts into your git repository, maybe even on the master branch. This is bad practice for a variety of reasons, but sometimes we can’t avoid it.

However, we can hack our way around cluttering the history too much, using a few quick-and-dirty npm scripts.

For this post we assume that we want to check-in the dist folder only on tagged commits, which we create with the npm version command.

First, we add /dist to our .gitignore file, which tells git to ignore the top-level dist folder.

In package.json we add the following scripts:

  "scripts": {
    "preversion": "sed -i '' 's:/dist:#/dist:' .gitignore",
    "version": "npm run build && git add .",
    "postversion": "sed -i '' 's:#/dist:/dist:' .gitignore && git rm --cached -r dist && git add . && git commit -m 'restore pre-version .gitignore'"

The sed command allows us to run a quick regex search-and-replace on a file.1 I used : as a delimiter (instead of /) to make writing regexes that involve file paths easier.

The first regex looks for the dist entry and replaces it with something we can find later (we prepend a # so the file remains a valid .gitignore). The second regex does the reverse.

The execution order is as follows:

  1. npm runs the preversion script. We remove the dist entry from .gitignore.2
  2. npm bumps the version number and runs the version script. We build the project and add the files to git.
  3. npm commits the files and tags the release, then runs the postversion script. We add the dist entry back to .gitignore and remove the build files from git in a follow-up commit.

The git history for every tagged release will look like this:

* ac58cf4 2018-04-06 | restore pre-version .gitignore (HEAD -> master)
* a8989f1 2018-04-06 | 1.2.3 (tag: v1.2.3)

This procedure still adds a lot of clutter to to the git history, but at least it is limited to tagged commits, and we aren’t at risk of checking in (development-) artifacts by accident.

  1. BSD sed requires a file extension after the -i flag because it saves a backup file with the given extension (Source). Since .gitignore doesn’t have one, we provide the empty string. ↩︎

  2. Technically you could also prepend this to the verison script. Using all three version hooks makes it a bit more readable. ↩︎