Esempio n. 1
0
 def run(self, args):
     file = File(args.file)
     if not file.has_xattr(XATTR_BASE):
         print "That file isn't being tracked."
         return
     file.delete_xattr(XATTR_BASE)
     print "%r has been untracked. You should `filer commit` soon." % file.path
Esempio n. 2
0
def detect_working(target=None, silent=False):
    """
    Finds the current working directory by jumping parents until we have no
    more parents, then scanning down until we find one of them that has a
    XATTR_BASE attribute set. We use the topmost one and not the first one we
    encounter while scanning up so that if someone copies a working folder or
    working file into another working folder, it will be seen as an integrated
    file or folder instead of as a separate working copy.
    
    Update: Either XATTR_BASE or XATTR_REPO being present will stop the search
    and produce the relevant file as the result.
    """
    if target is None:
        target = File()
    parents = target.ancestors(True)
    # Reverse so that we've got the topmost folder first
    parents.reverse()
    # Scan through for XATTR_BASE
    for parent in parents:
        if parent.has_xattr(XATTR_BASE) or parent.has_xattr(XATTR_REPO):
            return parent
    # Couldn't find a working copy
    if silent:
        return None
    else:
        raise Exception("Couldn't find a working copy to use. You probably "
                "need to specify --working.")
Esempio n. 3
0
 def run(self, args):
     if args.working is None:
         working_folder = detect_working()
     else:
         working_folder = File(args.working)
     if working_folder is None:
         raise Exception("You're not inside a working folder right now, "
                         "and you didn't specify --working.")
     if working_folder.has_xattr(XATTR_BASE):
         base = json.loads(working_folder.get_xattr(XATTR_BASE))
         for parent in base:
             print parent
Esempio n. 4
0
def init_repository(folder):
    """
    Creates a new repository. The specified folder should point to the .filer
    directory in which the repository should be created.
    """
    # TODO: Consider using a neo4j repository for the prototype. It'd make a
    # lot of stuff simpler and would do away with pretty much all of the
    # maintenance folders (numbers, numbersbyrev, changeparents,
    # changechildren, dirparents, and dirchildren).
    folder = File(folder)
    if folder.exists:
        raise Exception("There's already a filer repository at that location.")
    folder.mkdirs(True)
Esempio n. 5
0
 def run(self, args):
     location = File(args.location)
     if args.plain:
         repository_folder = location
     else:
         location.mkdirs()
         repository_folder = location.child(".filer")
     init_repository(repository_folder)
     if not args.plain:
         repository = Repository(repository_folder)
         working = WorkingCopy(repository, location)
         working.create()
     print "Repository created at %r." % repository_folder.path
Esempio n. 6
0
    def run(self, args):
        if args.repository:
            repository_folder = File(args.repository)
        else:
            repository_folder = detect_repository(File())
            if not repository_folder:
                raise Exception("You're not in a repository (or a working "
                                "folder) right now and you didn't specify "
                                "--repository.")
        if args.working:
            working_file = File(args.working)
        else:
            working_file = detect_working(silent=True)
        repository = Repository(repository_folder)
        revisions = None
        if working_file and working_file.has_xattr(XATTR_BASE) and not args.all:
            # We have a working file and we're not displaying all revisions.
            # Only show revisions which are ancestors of the working file.
            # TODO: Figure out a way to do this without having to reconstruct
            # every revision; probably need to have some sort of cache in the
            # Repository class of parent-child revision relationships. Still
            # thinking a Neo4j database might be useful for this, or maybe some
            # sort of equivalent SQLite database.
            revisions = set()
            base = json.loads(working_file.get_xattr(XATTR_BASE))
            current = set(base)
            while current:
                # Find the revision with the highest number in current
                max_hash = max(current, key=lambda r: int(repository.number_for_rev(r)))
                revisions.add(max_hash)
                current.remove(max_hash)
                current |= set(repository.get_revision(max_hash)["parents"])
        print
        for number, hash, data in repository.revision_iterator():
            if revisions is not None and hash not in revisions:
                continue
            print u"Revision %s:%s:" % (number, hash)
            print u"    date:           %s" % time.ctime(data.get("info", {}).get("date", 0))
            print u"    type:           %s" % data["type"]
            if data.get("current_name"):
                print u"    committed as:   %s" % data["current_name"]
            for cparent in data["parents"]:
                print u"    change parent:  %s:%s" % (repository.number_for_rev(cparent), cparent)
#            for dparent in repository.get_dirparents(hash):
#                print "    dir parent:     %s:%s" % (repository.number_for_rev(dparent), dparent)
            try:
                print u"    message:        %s" % data.get("info", {}).get("message", "")
            except:
                print >>sys.stderr, data
                raise
            print
Esempio n. 7
0
 def run(self, args):
     # This is basically as simple as getting the working file and the
     # repository and committing a new revision on them.
     if args.repository:
         repository_folder = File(args.repository)
     else:
         repository_folder = detect_repository()
     repository = Repository(repository_folder)
     if args.working:
         working_file = File(args.working)
     else:
         working_file = detect_working()
     info = {"date": time.time(), "message": args.message}
     working = WorkingCopy(repository, working_file)
     # Keep track of the old base (which will be None if the working copy
     # is untracked) and compare it later on to see if anything changed
     if working_file.has_xattr(XATTR_BASE):
         base = json.loads(working_file.check_xattr(XATTR_BASE))
     else:
         base = None
     working.commit(info)
     if not working_file.has_xattr(XATTR_BASE):
         # This can happen when the working file itself isn't tracked, which
         # is rare but happens if the user checks out a new, blank working
         # copy but doesn't add it with the add command. In that case, we
         # print a friendly warning.
         print "The working copy isn't being tracked. You probably need to "
         print "`filer add %s` first." % working_file.path
     else:
         new_base = json.loads(working_file.check_xattr(XATTR_BASE))
         hash = new_base[0]
         if new_base != base:
             print "Committed revision %s:%s." % (repository.number_for_rev(hash), hash)
         else:
             print "No changes to be committed."
             print "If you copied new files into the working copy that you "
             print "expected to show up, make sure to `filer add` them first."
Esempio n. 8
0
 def run(self, args):
     if args.exclude_name is None:
         args.exclude_name = []
     file = File(args.file)
     if args.all:
         files = file.recurse(filter=lambda f: (f.name not in args.exclude_name),
                 include_self=True, recurse_skipped=False)
     else:
         files = [file]
     total_tracked = 0
     total_added = 0
     for f in files:
         if f.has_xattr(XATTR_BASE):
             total_tracked += 1
         else:
             if f.is_link:
                 print "Warning: symbolic link %s will be ignored" % f.path
                 continue
             print "Adding %s" % f.path
             f.set_xattr(XATTR_BASE, json.dumps([]))
             total_added += 1
     print "%s files added, %s files already tracked" % (total_added, total_tracked)
     if total_added:
         print "You should `filer commit` soon."
Esempio n. 9
0
 def __init__(self, folder, debug=None):
     if debug is None:
         debug = global_debug
     self.debug = debug
     self.folder = File(folder)
     # Sanity check to make sure we're not using the parent folder
     if self.folder.child(".filer").exists:
         raise Exception("You should specify the .filer directory as the "
                 "repository to work with, not the folder that contains it")
     self.store_folder = self.folder.child("store")
     self.store_folder.mkdirs(True)
     self.store_folder.child("type").write("direct")
     self.store = DirectStore(self.store_folder.child("direct"))
     self.numbers = self.folder.child("numbers")
     self.numbers.mkdirs(True)
     self.numbersbyrev = self.folder.child("numbersbyrev")
     self.numbersbyrev.mkdirs(True)
Esempio n. 10
0
class Repository(object):
    def __init__(self, folder, debug=None):
        if debug is None:
            debug = global_debug
        self.debug = debug
        self.folder = File(folder)
        # Sanity check to make sure we're not using the parent folder
        if self.folder.child(".filer").exists:
            raise Exception("You should specify the .filer directory as the "
                    "repository to work with, not the folder that contains it")
        self.store_folder = self.folder.child("store")
        self.store_folder.mkdirs(True)
        self.store_folder.child("type").write("direct")
        self.store = DirectStore(self.store_folder.child("direct"))
        self.numbers = self.folder.child("numbers")
        self.numbers.mkdirs(True)
        self.numbersbyrev = self.folder.child("numbersbyrev")
        self.numbersbyrev.mkdirs(True)
    
    def get_revision(self, id):
        """
        Gets the revision with the specified revision id, which can either be a
        hex string representing the full sha1 hash or the revision's numeric id.
        An exceptions.NoSuchObject will be thrown if no such revision exists.
        
        The return value is a BEC object corresponding to the revision.
        
        Note that short revision numbers must still be given as strings. Bad
        things (exceptions mainly) will happen if ints are passed in instead.
        """
        if self.numbers.child(id).exists:
            id = self.numbers.child(id).read()
        return self.store.get(id)
    
    def create_revision(self, data):
        """
        Creates a new revision with the specified data, which should be a BEC
        object (not a string). The new revision's hash will be returned.
        
        Entries in changeparents, changechildren, dirparents, and dirchildren
        will be created for this new revision. (Update: those have been
        disabled for now, and will probably be replaced with some sort of graph
        database soon.)
        """
        if not isinstance(data, dict):
            raise Exception("Invalid revision data value: %r (must be a dict)"
                    % data)
        hash = self.store.store(data)
        if self.debug:
            print "Wrote revision %s" % hash
        # TODO: Implement a better algorithm for searching for the next number;
        # perhaps start at 0 and double until an unused number N is hit, then
        # do a binary search from 0 to N for the lowest unused number
        number = 1
        while self.numbers.child(str(number)).exists:
            number += 1
        # Add an entry into numbers for this number
        self.numbers.child(str(number)).write(hash)
        # Add a reverse entry into numbersbyrev
        self.numbersbyrev.child(hash).write(str(number))
        return hash
    
    def revision_iterator(self):
        """
        A generator that returns a (number, hash, data_str, data) tuple for all
        of the revisions in this repository. number will be a string.
        """
        current_number = 1
        while self.numbers.child(str(current_number)).exists:
            # We've still got a revision, so yield it
            hash = self.numbers.child(str(current_number)).read()
            print >>sys.stderr, current_number, hash
            data = self.store.get(hash)
            yield str(current_number), hash, data
            current_number += 1
    
    def number_for_rev(self, hash):
        """
        Returns the short number (as a string) of the specified revision hash.
        """
        return self.numbersbyrev.child(hash).read()
    
    def rev_for_number(self, number):
        if self.numbers.child(number).exists:
            return self.numbers.child(number).read()
        # Assume it's a hash instead of a number and just return it
        return number
        
    def has_revision(self, hash):
        """
        Returns True if the specified revision is present in this repository.
        """
        return self.store.has(hash)
    
    def is_ancestor(self, descendant, ancestor):
        descendant, ancestor = self.rev_for_number(descendant), self.rev_for_number(ancestor)
        if descendant == ancestor:
            return True
        current = set(self.get_revision(descendant)["parents"])
        while current:
            rev = current.pop()
            if rev == ancestor:
                return True
            current |= set(self.get_revision(rev)["parents"])
        return False
Esempio n. 11
0
File: test2.py Progetto: hzmmzl/afn
        g.DVBox.__init__(self)
        self.task_list = task_list
        template = bind.ListTranslator(TaskView, None)
        bind.bind(template.a, task_list)
        bind.bind(self.children, template.b)


def create_task(task_list):
    t = bind.PyDict()
    t["text"] = ""
    t["completed"] = False
    t["children"] = bind.PyList()
    task_list.append(t)


task_file = File(sys.argv[1])
if task_file.exists:
    tasks = bind_utils.json_to_bindable(json.load(task_file.open("r")))
else:
    tasks = bind.PyList()

w = g.make(g.DWindow(), props={"title": task_file.name + " - Task List"})
w.widget.resize(440, 520)

show_all = g.make(g.DCheckButton(), props={"label": "Show all tasks"}, child_props={"expand": True})
add = g.make(g.DButton(), props={"label": "Add"}, child_props={"expand": False})
save = g.make(g.DButton(), props={"label": "Save"}, child_props={"expand": False})


def on_add(*args):
    create_task(tasks)
Esempio n. 12
0
 def run(self, args):
     # The checkout command does something different depending on how it's
     # run:
     # 
     # If the working copy (specified with --working or auto-detected)
     # exists and -r is specified, the working copy is updated to the
     # revision in question.
     # If the working copy exists but is not a working copy, it is turned
     # into one. Then, if -r is specified, it is updated to the specified
     # revision.
     # If the working copy does not exist, -r must be specified (so that we
     # know whether to create a file or a folder), and the working copy
     # will be created and updated to the revision in question.
     # 
     # So, if the working copy does not exist, we require -r and create it.
     # Then, if it's not a working copy, we make it one by setting
     # XATTR_REPO. Then we go update the working copy to the specified
     # revision.
     if args.repository:
         repository_folder = File(args.repository)
     else:
         repository_folder = detect_repository()
     repository = Repository(repository_folder)
     if args.working:
         working_file = File(args.working)
     else:
         working_file = detect_working()
     revision = args.revision
     # Check to see if the soon-to-be working copy already exists as a file
     # or folder
     if not working_file.exists:
         # It doesn't exist. Make sure we've got a --revision (and error out
         # if we don't, since then we don't know whether to create a file or
         # a folder).
         if revision is None:
             raise Exception("When using the checkout command on a working "
                     "copy that doesn't actually exist yet, --revision must "
                     "be specified. This is because Filer doesn't know "
                     "whether to create a folder or a file for it. If you "
                     "want to create a new, blank working copy without "
                     "checking one out from a revision, create a file or "
                     "folder at the relevant location, then use the "
                     "checkout command again. Then it'll work.")
         # We've got a revision, so look at whether it's a file or a folder,
         # and create a file or a folder accordingly.
         working_type = repository.get_revision(revision)["type"]
         if working_type == "file":
             # It's a file, so create a blank file for it.
             working_file.write("")
         else:
             # It's a folder, so create a blank folder for it.
             working_file.mkdir()
     # The working file exists. Now see if it's a working copy.
     working = WorkingCopy(repository, working_file)
     if not working.is_working():
         # It's not a working copy, so make it a working copy.
         working.create()
     # Now update it. TODO: We might want to keep track of whether it
     # already existed before now, and if it did, warn the user that they'll
     # be overwriting their changes.
     if revision:
         if working_file.has_xattr(XATTR_BASE):
             base = json.loads(working_file.get_xattr(XATTR_BASE))
         else:
             base = None
         # If we're already checked out, warn if we have multiple parents
         # as that'll likely steamroller over an impending merge. TODO:
         # might want to recursively walk down and make sure we don't have
         # any children that also have impending merges.
         if base and len(base) > 1:
             if not args.quiet and raw_input("The working copy has multiple parents; this "
                     "usually means a merge is in progress. Do you still "
                     "want to check out a new revision and overwrite "
                     "your changes (y or n)? ").lower()[0] != "y":
                 print "Stopping."
                 return
         # If we're checked out with one parent, see if the new revision is
         # an ancestor of the current revision or vice versa. If not, warn
         # that we're jumping history lines. TODO: Don't warn if they're on
         # the same changeline, even if they're on disjointed branches of
         # the changeline.
         elif base and len(base) == 1:
             base_rev = base[0]
             new_rev = repository.rev_for_number(revision)
             if not (repository.is_ancestor(base_rev, new_rev) or
                     repository.is_ancestor(new_rev, base_rev)):
                 if not args.quiet and raw_input("The revision you're updating to (%s) is not "
                         "an ancestor or a descendant of the working "
                         "copy's current revision (%s). Do you still want "
                         "to continue updating (y or n)? "
                         % (new_rev, base_rev)).lower()[0] != "y":
                     print "Stopping."
                     return
         working.update_to(revision)
         print "Checked out revision %r" % revision
     else:
         print "Resetting working copy to untracked"
         delete_tracked(working_file)