Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

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 ?

share|improve this question

2 Answers 2

up vote 33 down vote accepted

Mercurial is only keeping track of files, not directories.

One of the solution is to add a .emtpy file in your repository :

$ touch uploads/.empty
$ hg add uploads/.empty
share|improve this answer
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. –  Martin Geisler Dec 21 '09 at 23:23
I'm thinking naming it .hgempty might give a better clue as to what it's for –  User Oct 9 '12 at 20:40
Yes or .hgkeep –  Natim Oct 10 '12 at 7:28
Might as well go for verbose: .hgkeepifempty :) –  Daniel Sokolowski Feb 25 '13 at 19:40

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__':
share|improve this answer
The link is dead. –  Natim Oct 10 '12 at 7:30
True, updated link... –  der Oct 11 '12 at 22:20
host it on bitbucket (or ) github , old pastebin is old –  V3ss0n Dec 10 '12 at 15:28

Your Answer


By posting your answer, you agree to the privacy policy and terms of service.

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