hg-importfs /

# Copyright 2010 Lantiq Beteiligungs- GmbH & Co. KG
# This software may be used and distributed according to the terms of
# the GNU General Public License version 2 or any later version.
"""import a set of files from a file-system into a repository"""
import os
import shutil

from mercurial import commands, hg, util
from mercurial.error import Abort, RepoError
from mercurial.i18n import _

__version__ = '0.9.0'

def get_repo(ui, path):
    """Initialize or create a repository."""
        repo = hg.repository(ui, path)
    except RepoError:
        repo = hg.repository(ui, path, True)
        ui.status(_('created repository %s\n' % repo.root))
    return repo

def update_repo(ui, repo, rev, branch=None):
    """Update the repository.

    Creates a new branch if necessary.
    if branch:
        if branch not in repo.branchmap():
            # Create a named branch.
            commands.update(ui, repo, rev=rev)
            commands.branch(ui, repo, label=branch)
            # Update to the named branch.
            commands.update(ui, repo, rev=branch)
    elif rev:
        # Create an anonymous branch.
        commands.update(ui, repo, rev=rev)
        commands.branch(ui, repo)
    elif repo.dirstate.branch() != 'default':
        # Update to the default branch.
        commands.update(ui, repo, rev='default')
        commands.update(ui, repo)

def onerror(func, path, exc_info=tuple()):
    """Error handler for shutil.rmtree and methods of the os package.

    If the error is due to an access error (read only file)
    it attempts to add write permission and then retries.

    If the error is for another reason it re-raises the error.
    import stat
    if not os.access(path, os.W_OK):
        os.chmod(path, stat.S_IWUSR)

def importfs(ui, repo, source, *pats, **opts):
    """Import a set of files from a file-system into a repository.

    Imports a set of files from a given file-system into a Mercurial
    repository as a changeset. If anything fails, the program exits, leaving
    the repository as it is.

    The specified repository is created if it doesn't exist. It is updated to
    the specified parent revision or null.

    If the repository is updated to a revision other than "tip" the new
    revision is created on a new branch. If the branch option is used the
    branch has the given name. Otherwise the name "default" is used.

    The files in the working directory are deleted and the files from the
    source directories are copied into the working directory. If a file exists
    in more than one source directory the file in the rightmost directory wins.
    "hg addremove" with the specified similarity is executed. "hg commit" with
    the specified message is executed. If a tag is specified, "hg tag" with the
    name is executed.
    sources = []
    for node in (source,) + pats:
        path = util.expandpath(node)
        if not os.path.exists(path):
            raise Abort(_('directory %s does not exist') % path)
    repo = get_repo(ui, repo)
    update_repo(ui, repo, opts.get('rev'), opts.get('branch'))
    # Empty the repository except the metadata.
    for node in os.listdir(repo.root):
        if node.startswith('.hg'):
        path = os.path.join(repo.root, node)
        if os.path.isdir(path):
            shutil.rmtree(path, onerror=onerror)
            except WindowsError:
                onerror(os.remove, path)
    # Copy all files into the repository.
    for sourcepath in sources:
        for node in os.listdir(sourcepath):
            src = os.path.join(sourcepath, node)
            dst = os.path.join(repo.root, node)
            util.copyfiles(src, dst, False)
    commands.addremove(ui, repo, similarity=opts.get('similarity'))
    message = opts.get('message') or 'importfs commit.'
    commands.commit(ui, repo, message=message)
    tag = opts.get('tag')
    if tag:
        commands.tag(ui, repo, tag)

cmdtable = {'importfs':
    [('r', 'rev', '', _('The revision to use as the immediate predecessor of '
        'the new revision. If omitted the revision null is used.'), _('REV')),
    ('b', 'branch', '', _("The name of a branch for the new revision. If it "
        "doesn't exist it is created. If omitted the default branch is used. "
        "This option is required if a revision other than tip is specified."),
    ('s', 'similarity', 100, _('A number to pass as the value of the '
        'similarity option to "hg addremove" for guessing file renames. (See '
        'hg help for an explanation.) If omitted the value "100" is used.'),
    ('m', 'message', '', _('The commit message to be used. If omitted the '
        'tag string is used.'), _('TEXT')),
    ('t', 'tag', '', _('The tag for the resulting revision. If omitted the '
        'revision is not tagged.'), _('NAME'))],
    '[OPTION]... REPO SOURCE...')
commands.norepo += ' importfs'