214

My issue is that I have a bunch of WordPress websites in my git repo, of which I want to selectively commit only the content of my themes folders, while ignoring the rest of the redundant files found in WordPress.

I've used .gitignore files to ignore file types before, but can it be used the other way around- that is to ignore everything BUT a certain folder path?

root (git repo)
- / wordpress
- - / (WordPress Site 1)/wp-content/themes
- - / (WordPress Site 2)/wp-content/themes
- - / (WordPress Site 3)/wp-content/themes

Thanks-

UPDATE:

Based on the answers I did the following, but it's not working. Any ideas?

# Ignore everything:
*
# Except for wordpress themes:
!*/wp-content/themes/*

I've also tried the following variations:

!*/wp-content/themes*
!*wp-content/themes/*
!wp-content/themes/*
!/wordpress/*/wp-content/themes*
!wordpress/*/wp-content/themes*

None of these read my themes folders.

0

17 Answers 17

193

Here's how I did it - you essentially have to walk up the paths, you can't wildcard more than one level in any direction:

# Ignore everything:
*

# Except for the themes directories:

!wordpress/
!wordpress/*/
!wordpress/*/wp-content/
!wordpress/*/wp-content/themes/
!wordpress/*/wp-content/themes/*
!wordpress/*/wp-content/themes/*/*
!wordpress/*/wp-content/themes/*/*/*
!wordpress/*/wp-content/themes/*/*/*/*
!wordpress/*/wp-content/themes/*/*/*/*/*

Notice how you have to explicitly allow content for each level you want to include. So if I have subdirectories 5 deep under themes, I still need to spell that out.

This is only how it worked for me. If someone cares to offer a more informed explanation by all means.

Also, these answers helpful:
how-do-negated-patterns-work-in-gitignore
how-do-gitignore-exclusion-rules-actually-work


NOTE: I tried using double-wildcard 'globs' but according to this that functionality is system dependent and it didn't work on my mac:

Did NOT work:

!**/wp-content/themes/
!**/wp-content/themes/**
7
  • 4
    before git 1.8.4 the ** wildcard only workds if you have no slashes in your pattern, see sparethought.wordpress.com/2011/07/19/…
    – user1115652
    Commented Nov 16, 2013 at 21:38
  • This should work: !/wordpress/*/wp-content/themes/**/*
    – that0n3guy
    Commented Oct 30, 2014 at 12:38
  • 1
    Does this syntax work on windows? It does not seem to be working for me...
    – Scorb
    Commented Sep 20, 2016 at 20:32
  • It works wonders on Windows 10. Using it for all kinds of custom projects from Visual Studio Code.
    – klewis
    Commented Jun 5, 2019 at 16:04
  • I used a combination of this answer and the one provided by @吴毅凡 , which worked for me.
    – MikeyE
    Commented Nov 10, 2019 at 6:49
136

I tried the above and they didn't work so well. A better approach is as follows from here: https://gist.github.com/444295

# This is a template .gitignore file for git-managed WordPress projects.
#
# Fact: you don't want WordPress core files, or your server-specific
# configuration files etc., in your project's repository. You just don't.
#
# Solution: stick this file up your repository root (which it assumes is
# also the WordPress root directory) and add exceptions for any plugins,
# themes, and other directories that should be under version control.
#
# See the comments below for more info on how to add exceptions for your
# content. Or see git's documentation for more info on .gitignore files:
# http://kernel.org/pub/software/scm/git/docs/gitignore.html

# Ignore everything in the root except the "wp-content" directory.
/*
!.gitignore
!wp-content/

# Ignore everything in the "wp-content" directory, except the "plugins"
# and "themes" directories.
wp-content/*
!wp-content/plugins/
!wp-content/themes/

# Ignore everything in the "plugins" directory, except the plugins you
# specify (see the commented-out examples for hints on how to do this.)
wp-content/plugins/*
# !wp-content/plugins/my-single-file-plugin.php
# !wp-content/plugins/my-directory-plugin/

# Ignore everything in the "themes" directory, except the themes you
# specify (see the commented-out example for a hint on how to do this.)
wp-content/themes/*
# !wp-content/themes/my-theme/
4
  • 1
    @dwenaus- thanks for sharing this- looks useful for repos rooted in a specific wordpress dir, but won't work on repo w multiple wordpress sites underneath it, which is what I was going for..
    – Yarin
    Commented Jul 17, 2012 at 15:58
  • This structure / model is super helpful. I wanted a single repo for a combined Magento / WP install, and with fewer than 30 lines of .gitignore using this technique, I was able to get it narrowed down to just the Magento theme / skin files and the WP theme files. Thanks! Commented Mar 22, 2013 at 17:06
  • just don't use this setup with auto-deploy because a novice git user could mess up files outside of the indented areas (ie. if they delete all files in the repo by accident and that is auto deployed it'll be messy). Other than that it works well.
    – dwenaus
    Commented Mar 24, 2013 at 0:20
  • You are the man dude! Especially if your version is >=2.1.4. Ref: git-scm.com/docs/gitignore/2.1.4 Commented Jul 1, 2015 at 8:51
54

modify the first line

*

change it to

/*
3
  • 4
    This work for me. My .gitignore file is like this /* !.gitignore !/vendors Commented Apr 5, 2017 at 6:30
  • 1
    Your answer was the only that worked for me. Commented Mar 15, 2023 at 2:51
  • 1
    This works. Best and simple answer.
    – Martin
    Commented Jun 22, 2023 at 14:28
19

Try these answers:

Make .gitignore ignore everything except a few files

# Ignore everything
*

# But not these files...
!.gitignore
!script.pl
!template.latex
# etc...

# ...even if they are in subdirectories
!*/

How do I tell Git to ignore everything except a subdirectory?

This ignores root files & root directories, then un-ignores the root bin directory:

/*
/*/
!/bin/

This way you get all of the bin directory, including subdirectories and their files.

3
  • @irritate- Thanks, did not see these- however, I'm having trouble applying them to my situation. See my edits above...
    – Yarin
    Commented Mar 9, 2011 at 14:39
  • 1
    The second link I posted says that you would need both !*/wp-content/themes/ and !*/wp-content/themes/*. Have you tried that variation? Also, as an alternative, you could just use !*/wp-content/themes/ and in each themes directory have its own .gitignore file with !*
    – irritate
    Commented Mar 9, 2011 at 14:46
  • 1
    @irritate- no that variation doesn't work either, and anyway I don't see how that would be any different from !*/wp-content/themes*
    – Yarin
    Commented Mar 9, 2011 at 15:26
9

If you prefix a pattern with an exclamation point (!) it negates any previous pattern which excluded it. So, presumably, you could ignore everything, then only allow what you want by using this pattern.

3
  • @dappawit- Thanks- concept makes sense, and I got it working for files, but not for my themes folders- see my edits above...
    – Yarin
    Commented Mar 9, 2011 at 14:38
  • I am not an expert on glob patterns, but reading the .gitignore man page, I believe this may work: !wordpress/[write out directories]/wp-content/themes/. Notice the closing slash and no closing asterisk. Also note that * will only match one level, not multiple directories. Here is the man page: kernel.org/pub/software/scm/git/docs/gitignore.html
    – dappawit
    Commented Mar 9, 2011 at 20:07
  • 1
    Helped me realise I had to put the negation after the other rules thanks
    – Axe
    Commented Aug 2, 2012 at 9:03
9

Say you have a directory structure like this:

– sites
– -all
– – – -files
– – – – – -private
– – – – – – – -newspilot
– -default
– – – -files
– – – – – -private
– – – – – – – -newspilot

When you want to only allow sites/all/files/private/newspilot and sites/default/files/private/newspilot, you might, at first, try this:

sites/*/files/*
!sites/*/files/private/newspilot

This is wrong! The tricky thing with gitignore is that you have to first allow the parent ("private") directory to be included, before you can allow its child directory ("newspilot") to be included in commits.

sites/*/files/*
!sites/*/files/private
sites/*/files/private/*
!sites/*/files/private/newspilot

http://www.christianengvall.se/gitignore-exclude-folder-but-include-a-subfolder/

1
  • 2
    "you have to first allow the parent directory to be included, before you can allow its child directory" - Thank you for pointing this out! Was precisely my problem.
    – Jet Blue
    Commented Mar 14, 2020 at 0:42
6

I needed to ignore everything but not one folder with subdirectories.

For me, this works

# Ignore everything
/*

# Not these directories
!folder/

# Not these files
!.gitignore
!.env
!file.txt
5

Yarin's answer worked for me, here's my version (I don't need the /wordpress sub-directory):

*

!.gitignore
!/wp-content/
!/wp-content/themes/
!/wp-content/themes/*
!/wp-content/themes/*/*
!/wp-content/themes/*/*/*
!/wp-content/themes/*/*/*/*
!/wp-content/themes/*/*/*/*/*

# I don't want to track this one, so it's included here, after the negated patterns. 
wp-content/themes/index.php
5

I always get stuck somewhere on this even after coming back to this question numerous times. I've come up with a detailed process of doing it step by step:

First just use git add to add the actual content.

It'll show the relevant files added to the index while all others still untracked. This helps contructing .gitignore step by step.

$ git add wp-content/themes/my-theme/*
$ git status

    Changes to be committed:
        new file:   wp-content/themes/my-theme/index.php
        new file:   wp-content/themes/my-theme/style.css

    Untracked files:
        wp-admin/
        wp-content/plugins/
        wp-content/themes/twentyeleven/
        wp-content/themes/twentytwelve/
        ...
        wp-includes/
        ...

Add a temporary DUMMY.TXT file in your directory:

$ git status

    Changes to be committed:
        new file:   wp-content/themes/my-theme/index.php
        new file:   wp-content/themes/my-theme/style.css

    Untracked files:
        wp-admin/
        wp-content/plugins/
        wp-content/themes/twentyeleven/
        wp-content/themes/twentytwelve/
        ...
        wp-content/themes/my-theme/DUMMY.TXT  <<<
        ...
        wp-includes/
        ...

Our goal now is to construct the rules such that this DUMMY.TXT be the only one still showing up as Untracked when we're done.

Start adding the rules:

.gitignore

/*

First one is just to ignore everything. Untracked files should be all gone, only indexed files should be showing:

$ git status

    Changes to be committed:
        new file:   wp-content/themes/my-theme/index.php
        new file:   wp-content/themes/my-theme/style.css

Add the first dir in the path wp-content

/*
!/wp-content

Now the Untracked files will show up again, but only have wp-content's contents

$ git status

    Changes to be committed:
        new file:   wp-content/themes/my-theme/index.php
        new file:   wp-content/themes/my-theme/style.css

    Untracked files:
        wp-content/plugins/
        wp-content/themes/twentyeleven/
        wp-content/themes/twentytwelve/
        ..

Ignore everything in the first dir /wp-content/* and un-ignore !/wp-content/themes

/*
!/wp-content

/wp-content/*
!/wp-content/themes

Now the Untracked files will further narrow down to only wp-content/themes

$ git status

    Changes to be committed:
        new file:   wp-content/themes/my-theme/index.php
        new file:   wp-content/themes/my-theme/style.css

    Untracked files:
        wp-content/themes/twentyeleven/
        wp-content/themes/twentytwelve/
        ..

Repeat the process till that dummy file is the only one still showing as Untracked:

/*
!/wp-content

/wp-content/*
!/wp-content/themes

/wp-content/themes/*
!/wp-content/themes/my-theme

$ git status

    Changes to be committed:
        new file:   wp-content/themes/my-theme/index.php
        new file:   wp-content/themes/my-theme/style.css

    Untracked files:
        wp-content/themes/my-theme/DUMMY.TXT
5

After having a need for this multiple times I have finally found a solution [1]:

/*/
/*.*
!.git*
!/wordpress

Explanation line by line:

  1. Ignore all directories at the root.
  2. Ignore all files with extensions at the root.
  3. Allow .gitignore itself (and potentially .gitattributes).
  4. Allow the desired directory with all subdirectories

[1] Limitations (that I'm aware of):

  1. It does not ignore files without an extension at the root (and adding /* would break the entire scheme).
  2. The directory that you want to include in line 4 (wordpress in this case) cannot have . in the name (e.g. wordpress.1 would not work). If it does have a ., then remove line 2 and find another way to exclude all files at the root.
  3. Tested only on Windows with git version 2.17.1.windows.2
5

Another easy solution :

You want to ignore all Wordpress files, but not your theme (for example).

.gitignore, content is :

# All wordpress + content of revert ignored directories
wordpress/*
wordpress/wp-content/*
wordpress/wp-content/themes/*

# Revert ignoring directories
!wordpress/
!wordpress/wp-content/
!wordpress/wp-content/themes/
!wordpress/wp-content/themes/my_theme

In this example, you can remove wordpress if .gitignore is in root wordpress directory

You can do exactly the same with every folders and content you want to "exceptionally" keep out of gitignored folders/files

Conclusion :

Be sure to unignore all directories of a path that you want to unignore

BUT

Be sure to ignore all content of unignored directories that you want to ignore

4

For those looking for a cleaner solution, please try the following.

As mentioned in the comments of this answer, you have to use this method recursively.

In this example, you have a website setup at ./ where your .git folder and .gitignore file is located and a WordPress installation setup in ./wordpress. To correctly ignore the everything under the ./wordpress directory apart from the theme directory itself (wordpress/wp-content/themes/my-theme), you will have to recursively ignore and allow each directory up until the directory you wish to allow:

wordpress/*
wordpress/wp-content/*
wordpress/wp-content/themes/*
!wordpress/wp-content
!wordpress/wp-content/themes
!wordpress/wp-content/themes/my-theme

The reason for ignoring with a wildcard and allowing (or, ignoring 'apart from') the directory itself enables Git to look inside the directory first before ignoring everything inside. We then tell Git to ignore everything 'apart from' the directory we have specified. Here's the same syntax but in order of how Git is looking at it:

wordpress/*                            # Ignore everything inside here
!wordpress/wp-content                  # Apart from this directory

wordpress/wp-content/*                 # Ignore everything inside here
!wordpress/wp-content/themes           # Apart from this directory

wordpress/wp-content/themes/*          # Ignore everything inside here
!wordpress/wp-content/themes/my-theme  # Apart from this directory

Hopefully this helps someone better understand the need for the recursive method.

3

late to the party but here's what I use for unknown depth (the accepted solution requires known depth)

/*
!.gitignore
!wp-content/
!*/

this way everything under wp-content is not ignored.

3

Here's my solution, which assumes a checkout into wp-content

# Ignore everything except directories
*
!*/

# except everything in the child theme and its required plugin
!/themes/mytheme-child/**
!/plugins/my-plugin/**

# and this file
!.gitignore

and testing:

git version 2.20.1 (Apple Git-117)
$ git check-ignore -v .foo foo foo/ themes/foo themes/foo/bar themes/mytheme-child \
themes/mytheme-child/foo plugins/foo plugins/my-plugin plugins/my-plugin/foo .gitignore
.gitignore:2:*  .foo
.gitignore:2:*  foo
.gitignore:2:*  foo/
.gitignore:2:*  themes/foo
.gitignore:2:*  themes/foo/bar
.gitignore:2:*  themes/mytheme-child
.gitignore:6:!/themes/mytheme-child/**  themes/mytheme-child/foo
.gitignore:2:*  plugins/foo
.gitignore:2:*  plugins/my-plugin
.gitignore:7:!/plugins/my-plugin/** plugins/my-plugin/foo
.gitignore:10:!.gitignore   .gitignore

so it correctly ignores everything I don't want and nothing I want to keep.

Releasing Code

Gitlab is configured with repository mirrors for protected branches, according to https://docs.gitlab.com/ee/workflow/repository_mirroring.html

When code is pushed to a protected branch, it will be mirrored to the staging and production servers in the /opt/checkout/repo.git/ bare repo. The following post-receive hook (in /opt/checkout/repo.git/hooks/post-receive) will then checkout the code into the working directory.

#!/bin/bash

BRANCH=staging
GIT_DIR=/opt/checkout/repo.git
GIT_WORK_TREE=/opt/bitnami/apps/wordpress/htdocs/wp-content

# on push, checkout the code to the working tree
git checkout -f "${BRANCH}"

# ensure permissions are correct
sudo chown -R bitnami:daemon "${GIT_WORK_TREE}"
sudo chmod -R go-w "${GIT_WORK_TREE}"

For more information, see https://www.digitalocean.com/community/tutorials/how-to-set-up-automatic-deployment-with-git-with-a-vps

2

You can try this:

!**/themes/*

Where:

  • !: negate ignore (include)
  • **: any directory tree (recursion) so you don't have to specify you folder path
  • themes: your folder to include (can be anything)
  • *: any file in that folder, you can include all subfolders as well with ** and instead of including any file (*) you can be specific *.css, *.xy
1

Here's how I did it:

# Ignore everything at root:
/*

# Except for directories:
!wordpress/(WordPress Site 1)/wp-content/themes/
!wordpress/(WordPress Site 1)/wp-content/themes/
!wordpress/(WordPress Site 1)/wp-content/themes/

# Except files:
!README

#Except files of type:
!*.txt

This is what worked for me. Allows you to ignore everything except specific files or folders

macOS sierra

0

I am in the same boat trying to manage a bunch of Wordpress sites under one repo. My directory structure looks like this:

root (git repo)
(WordPress Site 1)/app/public/wp-content/themes
(WordPress Site 2)/app/public/wp-content/themes
(WordPress Site 3)/app/public/wp-content/themes

I want to just track the items inside the app/public folder for each site. I tried the samples in this page as well as some of the suggestions here and ended up trying this:

/(WordPress Site 1)/*
!(WordPress Site 1)/app
(WordPress Site 1)/app/*
!(WordPress Site 1)/app/public

which worked but I would have to ignore the same path for each site which I was trying to avoid.

Then I just replaced the name of the site with * and that did the trick for me. So this is what I ended up using:

/*/*
!*/app
*/app/*
!*/app/public

This effectively ignored everything in the site's folder while capturing everything in the app/public folder for any site that I create in the root.

Note that it will not ignore files in the root, just directories :)

Hope this helps.

Not the answer you're looking for? Browse other questions tagged or ask your own question.