kawnah
kawnah

Reputation: 3414

gitignore - ignore generated files in a folder, but not the folder

I have a gulp task that pipes assets into a directory. I would like to include the directory in my project, but exclude the outputted files through a .gitignore file. My gulp task looks like this:

gulp.task('styles', function() {
    return sass('app/scss/styles.scss')
    .pipe(cleanCSS())
    .pipe(gulp.dest('public/stylesheets'))
    .pipe(livereload());
});

gulp.task('scripts', function() {
    gulp.src('app/js/script.js')
    .pipe(webpack(require('./webpack.config.js')))
        .pipe(uglify())
    .pipe(gulp.dest('public/javascripts'))
    .pipe(livereload());
});

Folder structure:

app
---js
---scss
public(NEED TO KEEP THIS)
---javascripts(NEED TO KEEP THIS)
-----scrpits.min.js**(NEED TO IGNORE THIS)**
---stylesheets(NEED TO KEEP THIS)
-----styles.css**(NEED TO IGNORE THIS)**

So in .gitignore if I go:

/public

That ignores everything, which is not what I want.

I found this question: How can I add an empty directory to a Git repository? and someone suggested a "workaround" with 54 upvotes:

"Andy Lester is right, but if your directory just needs to be empty, and not empty empty, you can put an empty .gitignore file in there as a workaround..."

So now I go:

app
---js
---scss
public(NEED TO KEEP THIS)
---javascripts(NEED TO KEEP THIS)
-----scrpits.min.js**(NEED TO IGNORE THIS)**
-----.gitignore
---stylesheets(NEED TO KEEP THIS)
-----styles.css**(NEED TO IGNORE THIS)**
-----.gitignore

And took a suggestion from the accepted answer and did:

/public/!.gitignore

But still is not getting the desired result.

How can I commit an output folder to a repository for a gulp task?

Upvotes: 2

Views: 4102

Answers (3)

kawnah
kawnah

Reputation: 3414

Ok I figured it out:

/public/**/*.js
/public/**/*.css

Essentially, any file in any folder below one level in /public should be ignored.

Upvotes: 4

torek
torek

Reputation: 489678

Git doesn't store directories (aka folders) at all, it only stores files within a commit. When Git goes to check out a commit, if some file within that commit requires that some parent directory exist, Git makes that parent directory at that point. So that's why you need to commit the file .gitignore, or anyway some file, within the directory: so that Git has some file it's extracting, that forces it to create the directory.

Now, the problem here is that when Git is doing its "run as fast as possible even if that confuses everyone" :-) tricks, it may discover that a .gitignore lists a directory name and that it—specifically, Git's index—has no files already within that directory, and it will then not even bother to look inside the directory for files. This is how git add skips right over the !public/.gitignore directive: while it was looking around, it found public as a directory but had no files in it to be committed yet, then found public in .gitignore, and decided not to look inside public at all.

If, instead of listing public in your .gitignore, you list public/*, Git has to look inside public to find all its files and sub-directories to see if they can be skipped. Having looked inside, it will check public/.gitignore against the .gitignore contents. The public/* entry will say "skip it" but the later !public/.gitignore entry will say "don't skip it". The later entry then overrides, and public/.gitignore gets stuffed into Git's index by git add --all or git commit --all.

Once public/.gitignore is in the index, it goes into the next commit. Once that's happened, it's actually safe to list public rather than public/* in the .gitignore file, because now that Git has a file in the directory, Git is forced to scan the directory. The entry for public/.gitignore will only be removed from the index again by an explicit request to remove that entry (git rm public/.gitignore or git rm --cached public/.gitignore), or by moving to (git checkout-ing) an existing commit in which there is no public/.gitignore file. Once the index entry is removed, it's no longer safe to list public rather than public/*, because Git can go back to its trick of noticing that public is a directory and is ignored and Git has nothing forcing Git to look inside public, so that it doesn't look inside and doesn't find public/.gitignore and hence doesn't check for the exception.


There are of course other ways to do the same thing. My preference is not to list the directory at all in the top level: instead, inside the public directory, create a .gitignore file with the contents:

*
!.gitignore

Git will then have to scan the public directory for files, since it's not ignored; that directory then has a .gitignore file, and that .gitignore file says: ignore everything here, except for .gitignore here. Since Git has already gone inside the directory at this point, and has found a list of all files and sub-directories within that directory, it will check each such file and sub-directory individually against this .gitignore and ignore all of them except for .gitignore itself.

Upvotes: 2

Shakes
Shakes

Reputation: 521

To exclude files in a sub-directory, append the following to a .gitignore in your base directory:

/public/scrpits.min.js
/public/styles.css
/sub-directory/file.whatever
etc...

If that's the only item in the folder and you need to keep that folder active, maybe place a blank text file in it

Upvotes: 0

Related Questions