Jak dodać pusty folder w projekcie Mercurial?


44

W moim projekcie korzystam z Mercurial i folderu, w którym użytkownik może przesłać plik. Ale ponieważ użytkownik prześle pliki, folder jest pusty.

Nie wiem, jak mogę dodać ten folder do mojego projektu, nie wkładając do niego żadnego pliku.

Czy wiesz jak mogę to zrobić?

Odpowiedzi:


46

Mercurial śledzi tylko pliki , a nie katalogi .

Jednym z rozwiązań jest dodanie pliku .empty do swojego repozytorium:

$ touch uploads/.empty
$ hg add uploads/.empty

1
Tak, to rzeczywiście właściwe rozwiązanie: Mercurial śledzi tylko pliki , a nie katalogi. Innym rozwiązaniem jest utworzenie pustych katalogów podczas wdrażania oprogramowania.
Martin Geisler

2
Myślę, że nazwanie go .hgemptymoże dać lepszą wskazówkę do czego
Użytkownik

7
Tak lub.hgkeep
Natim,

2
Mógłbym również powiedzieć pełno: .hgkeepifempty :)
Daniel Sokolowski

4

Stworzyłem skrypt Pythona, który automatyzuje proces tworzenia / usuwania tych plików.

Oto źródło skryptu: http://pastebin.com/inbYmMut

#!/usr/bin/python

# 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')
        sys.exit()

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

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

    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):
                removed.append(file)
                cmd += " %s" % fixpath(file)

        if cmd != "rm":
            for f in removed: print f
            os.system(cmd)
        else:
            print "No files named '%s' found" % filename
            sys.exit()

    # Walk directory tree, delete files

    else:

        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
            os.system(cmd)
        else:
            print "No empty directories found"
            sys.exit()


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
            else:
                for x in dirwalk(fullpath):  # recurse into subdir
                    if os.path.isdir(x):
                        if giveDirs:
                            yield x
                    else:
                        yield x
        else:
            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__':
    print
    init_options()
    main()
    print

Link jest martwy.
Natim,

Prawda, zaktualizowany link ...
mendezcode,

2
hostuj go na bitbucket (lub) github, stary pastebin jest stary
Phyo Arkar Lwin

-1, skrypt ten ilustruje wzorce NTI i złe praktyki.
Nikratio

1

Po prostu wykonaj następujące czynności:

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

W tym celu należy wziąć pod uwagę następujące kwestie:

W twoim przypadku możesz przesyłać obrazy w swoim środowisku programistycznym, więc zaleciłbym również dodanie do .hgignorepliku następujących elementów, aby przypadkowo nie zatwierdzić obrazów, których nie zamierzałeś zatwierdzić:

^(images)\/(?!\.hgkeep)

Reguła zignoruje wszystko images/**poza .hgkeepplikiem, do którego należy dodać „pusty” folder do kontroli wersji. Powodem, dla którego ta reguła jest ważna, jest to, że wszystkie pliki w tym folderze (tj. images/test-image.pngBędą wyglądały jak nowy plik nieskonfigurowany w twoim pliku, hg statusjeśli nie zignorujesz tego wzorca.


2
Przeczytaj uważnie pytanie ponownie. Twoja odpowiedź nie odpowiada na pierwotne pytanie, które
brzmiało

1
Masz rację. Zaktualizowałem swoją odpowiedź, aby odpowiedzieć na pytanie. Zmieniłem moją radę i zostawiłem ją, ponieważ ważne jest, aby wiedzieć i 99% czasu pożądane zachowanie.
Paul Redmond

@PaulRedmond co jeśli imageskatalog jest głęboko na ścieżce? Coś takiego ./lectures/chapter_10/images? Jaka jest zatem właściwa składnia?
aaragon,

@aragon wprawdzie minęło trochę czasu, odkąd użyłem Mercurial, ale trzeba by dostosować wyrażenie regularne, aby pasowało do zamierzonych wzorów. Gdy zauważysz ścieżki, które mają być ignorowane, dostosuj wyrażenie regularne w razie potrzeby.
Paul Redmond,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.