In my project, I am using Mercurial and a folder in when the user can upload file. But since the user will upload files, the folder is empty.

I don't know how I can add this folder to my project without putting any file inside.

Do you know how I can do ?

3 Answers 3


Mercurial only keeps track of files, not directories.

One solution is to add a .empty file to your repository:

$ touch uploads/.empty
$ hg add uploads/.empty
  • 1
    Yes, that is indeed the correct solution: Mercurial is only keeping track of files, not directories. Another solution is to create the empty directories when you deploy your software. Commented Dec 21, 2009 at 23:23
  • 2
    I'm thinking naming it .hgempty might give a better clue as to what it's for
    – User
    Commented Oct 9, 2012 at 20:40
  • 9
    Yes or .hgkeep
    – Natim
    Commented Oct 10, 2012 at 7:28
  • 2
    Might as well go for verbose: .hgkeepifempty :) Commented Feb 25, 2013 at 19:40
  • I would be really happy if I could understand why a folder can't be treated as a file. Everything is a file. Commented Dec 8, 2022 at 7:38

I have created a python script that automates the process of creating/deleting those files.

Here's the script's source: http://pastebin.com/inbYmMut


# Copyright (c) 2011 Ernesto Mendez (der-design.com)
# Dual licensed under the MIT and GPL licenses:
# http://www.opensource.org/licenses/mit-license.php
# http://www.gnu.org/licenses/gpl.html

# Version 1.0.0
# - Initial Release

from __future__ import generators
import sys
from optparse import OptionParser
import os

def main():
    # Process arguments

    if len(args) > 1:
        parser.error('Too many arguments')

    elif len(args) == 0:
        parser.error('Missing filename')

    if not os.path.exists(options.directory):
        parser.error("%s: No such directory" % options.directory)

    filename = args[0]

    # Create generator

    filetree = dirwalk(os.path.abspath(options.directory))

    # Walk directory tree, create files

    if options.remove == True:

        removed = ['Removing the following files: \n']
        cmd = "rm"

        for file in filetree:
            if (os.path.basename(file) == filename):
                cmd += " %s" % fixpath(file)

        if cmd != "rm":
            for f in removed: print f
            print "No files named '%s' found" % filename

    # Walk directory tree, delete files


        created = ["Creating the following files:\n"]
        cmd = "touch"

        for file in filetree:
            if (os.path.isdir(file)):
                created.append("%s%s" % (file, filename))
                cmd += " " + fixpath("%s%s" % (file, filename))

        if cmd != "touch":
            for f in created: print f
            print "No empty directories found"

def dirwalk(dir, giveDirs=1):
    # http://code.activestate.com/recipes/105873-walk-a-directory-tree-using-a-generator/
    for f in os.listdir(dir):
        fullpath = os.path.join(dir, f)
        if os.path.isdir(fullpath) and not os.path.islink(fullpath):
            if not len(os.listdir(fullpath)):
                yield fullpath + os.sep
                for x in dirwalk(fullpath):  # recurse into subdir
                    if os.path.isdir(x):
                        if giveDirs:
                            yield x
                        yield x
            yield fullpath

def wrap(text, width):
    return reduce(lambda line, word, width=width: '%s%s%s' % (line, ' \n'[(len(line)-line.rfind('\n')-1 + len(word.split('\n', 1)[0] ) >= width)], word), text.split(' ') )

def fixpath(p):
    return shellquote(os.path.normpath(p))

def shellquote(s):
    return "'" + s.replace("'", "'\\''") + "'"

def init_options():
    global parser, options, args
    parser = OptionParser(usage="usage: %prog [options] filename", description="Add or Remove placeholder files for SCM (Source Control Management) tools that do not support empty directories.")
    parser.add_option("-p", "--path", dest="directory", help="search within PATH", metavar="PATH")
    parser.add_option("-r", "--remove", dest="remove", action="store_true", help="remove FILE from PATH, if it's the only file on PATH")

    (options, args) = parser.parse_args()

if __name__ == '__main__':
  • The link is dead.
    – Natim
    Commented Oct 10, 2012 at 7:30
  • True, updated link...
    – mendezcode
    Commented Oct 11, 2012 at 22:20
  • 2
    host it on bitbucket (or ) github , old pastebin is old Commented Dec 10, 2012 at 15:28
  • -1, that script exemplifies nti-patterns and bad practices.
    – Nikratio
    Commented Oct 5, 2015 at 18:32

You simply do the following:

mkdir images && touch images/.hgkeep
hg add images/.hgkeep
hg commit -m"Add the images folder as an empty folder"

Note the following as a consideration when you do this:

In your case you might be uploading images in your development environment, so I would also recommend adding the following to your .hgignore file so you don't accidentally commit images you did not intend to commit:


The rule will ignore everything on images/** except the .hgkeep file you need to add an "empty" folder to version control. The reason why this rule is important, is that any files in that folder (ie. images/test-image.png will look like a new non-versioned file in your hg status if you don't ignore that pattern.

  • 2
    Please read the question again carefully. Your answer does not answer the original question, which asked "how to add an empty folder" not "How to ignore a folder"
    – DavidPostill
    Commented Jun 26, 2015 at 22:18
  • 1
    You're right. I've updated my answer to actually answer the question. I've altered my advice and left it because it's important to know and 99% of the time a desired behavior. Commented Jul 1, 2015 at 17:46
  • @PaulRedmond what if images is a directory deep in the path? Something like ./lectures/chapter_10/images? What is then the right syntax?
    – aaragon
    Commented Jun 20, 2018 at 16:07
  • @aaragon admittedly it has been a while since I used Mercurial, but you would need to adjust the regex to match patterns you intend. As you notice paths that you expect to be ignored, adjust the regex as needed. Commented Jun 20, 2018 at 23:09

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .