infocalypse

(Arne Babenhauserheide)
2012-06-26: now pushing and pulling works with keys in the .hg/hgrc (prefixed freenet-scheme

now pushing and pulling works with keys in the .hg/hgrc (prefixed with freenet://)

diff --git a/infocalypse/__init__.py b/infocalypse/__init__.py
--- a/infocalypse/__init__.py
+++ b/infocalypse/__init__.py
@@ -356,7 +356,7 @@ import os
 
 from commands import *
 
-from mercurial import commands, extensions, util, hg, dispatch
+from mercurial import commands, extensions, util, hg, dispatch, discovery
 from mercurial.i18n import _
 
 import freenetrepo
@@ -518,47 +518,83 @@ commands.norepo += ' fn-archive'
 
 
 ## Wrap core commands for use with freenet keys.
+## Explicitely wrap functions to change local commands in case the remote repo is an FTP repo. See mercurial.extensions for more information.
+# Get the module which holds the functions to wrap
+# the new function: gets the original function as first argument and the originals args and kwds.
+def findcommonoutgoing(orig, *args, **opts):
+    repo = args[0]
+    remoterepo = args[1]
+    capable = getattr(remoterepo, 'capable', lambda x: False)
+    if capable('infocalypse'):
+        class fakeoutgoing(object):
+            def __init__(self):
+                self.excluded = []
+                self.missing = repo.heads()
+                self.missingheads = []
+                self.commonheads = []
+        return fakeoutgoing()
+    else:
+        return orig(*args, **opts)
+# really wrap the functions
+extensions.wrapfunction(discovery, 'findcommonoutgoing', findcommonoutgoing)
+
+# wrap the commands
 
 def freenetpathtouri(path):
+    path = path.replace("%7E", "~").replace("%2C", ",")
     if path.startswith("freenet://"):
         return path[len("freenet://"):]
     if path.startswith("freenet:"):
         return path[len("freenet:"):]
     return path
 
-def isfreenetpath(path):
-    if path and path.startswith("freenet:") or path.startswith("USK@"):
-        return True
-    return False
-
-def parsepushargs(ui, repo, path=None):
-    return ui, repo, path
-
 def freenetpull(orig, *args, **opts):
+    def parsepushargs(ui, repo, path=None):
+        return ui, repo, path
+    def isfreenetpath(path):
+        if path and path.startswith("freenet:") or path.startswith("USK@"):
+            return True
+        return False
     ui, repo, path = parsepushargs(*args)
     if not path:
         path = ui.expandpath('default', 'default-push')
+    else:
+        path = ui.expandpath(path)
     # only act differently, if the target is an infocalypse repo.
     if not isfreenetpath(path):
         return orig(*args, **opts)
     uri = freenetpathtouri(path)
     opts["uri"] = uri
     opts["aggressive"] = True # always search for the latest revision.
-    infocalypse_pull(ui, repo, **opts)
+    return infocalypse_pull(ui, repo, **opts)
 
 def freenetpush(orig, *args, **opts):
+    def parsepushargs(ui, repo, path=None):
+        return ui, repo, path
+    def isfreenetpath(path):
+        if path and path.startswith("freenet:") or path.startswith("USK@"):
+            return True
+        return False
     ui, repo, path = parsepushargs(*args)
     if not path:
         path = ui.expandpath('default-push', 'default')
+    else:
+        path = ui.expandpath(path)
     # only act differently, if the target is an infocalypse repo.
     if not isfreenetpath(path):
         return orig(*args, **opts)
     uri = freenetpathtouri(path)
     opts["uri"] = uri
     opts["aggressive"] = True # always search for the latest revision.
-    infocalypse_push(ui, repo, **opts)
+    return infocalypse_push(ui, repo, **opts)
 
 def freenetclone(orig, *args, **opts):
+    def parsepushargs(ui, repo, path=None):
+        return ui, repo, path
+    def isfreenetpath(path):
+        if path and path.startswith("freenet:") or path.startswith("USK@"):
+            return True
+        return False
     ui, source, dest = parsepushargs(*args)
     # only act differently, if dest or source is an infocalypse repo.
     if not isfreenetpath(source) and not isfreenetpath(dest):
@@ -592,12 +628,11 @@ def freenetclone(orig, *args, **opts):
         raise util.Abort("""Can't clone without source and target. This message should not be reached. If you see it, this is a bug.""")
 
     if action == "copy":
-        opts["requesturi"] = pulluri
-        opts["inserturi"] = pushuri
-        return infocalypse_copy(ui, repo, **opts)
+        raise util.Abort("""Cloning without intermediate local repo not yet supported in the simplified commands. Use fn-copy directly.""")
     
     if action == "create":
         opts["uri"] = pushuri
+        repo = hg.repository(ui, ui.expandpath(source))
         return infocalypse_create(ui, repo, **opts)
 
     if action == "pull":
@@ -614,7 +649,12 @@ def freenetclone(orig, *args, **opts):
         # store the request uri for future updates
         with destrepo.opener("hgrc", "a", text=True) as f:
             f.write("""[paths]
-default = freenet://""" + pulluri + "\n")
+default = freenet://""" + pulluri + """
+
+[ui]
+username = anonymous
+""" )
+        ui.warn("As basic protection, infocalypse automatically set the username 'anonymous' for commits in this repo. To change this, edit " + str(os.path.join(destrepo.root, ".hg", "hgrc")))
         # and update the repo
         return hg.update(destrepo, None)
 
@@ -627,3 +667,76 @@ entry[1].extend(PULL_OPTS)
 entry[1].extend(FCP_OPTS)
 entry = extensions.wrapcommand(commands.table, "clone", freenetclone)
 entry[1].extend(FCP_OPTS)
+
+
+# Starting an FTP repo. Not yet used, except for throwing errors for missing commands and faking the lock.
+
+from mercurial import repo, util
+try:
+    from mercurial.error import RepoError
+except ImportError:
+    from mercurial.repo import RepoError
+
+class InfocalypseRepository(repo.repository):
+    def __init__(self, ui, path, create):
+        self.create = create
+        self.ui = ui
+        self.path = path
+        self.capabilities = set(["infocalypse"])
+        self.branchmap = {}
+
+    def lock(self):
+        """We cannot really lock Infocalypse repos, yet.
+
+        TODO: Implement as locking the repo in the static site folder."""
+        class DummyLock:
+            def release(self):
+                pass
+        l = DummyLock()
+        return l
+
+    def url(self):
+        return self.path
+
+    def lookup(self, key):
+        return key
+
+    def cancopy(self):
+        return False
+
+    def heads(self, *args, **opts):
+        """
+        Whenever this function is hit, we abort. The traceback is useful for
+        figuring out where to intercept the functionality.
+        """
+        raise util.Abort('command heads unavailable for Infocalypse repositories')
+
+    def pushkey(self, namespace, key, old, new):
+        return False
+
+    def listkeys(self, namespace):
+        return {}
+
+    def push(self, remote, force=False, revs=None, newbranch=None):
+        raise util.Abort('command push unavailable for Infocalypse repositories')
+    
+    def pull(self, remote, heads=[], force=False):
+        raise util.Abort('command pull unavailable for Infocalypse repositories')
+    
+    def findoutgoing(self, remote, base=None, heads=None, force=False):
+        raise util.Abort('command findoutgoing unavailable for Infocalypse repositories')
+
+
+class RepoContainer(object):
+    def __init__(self):
+        pass
+
+    def __repr__(self):
+        return '<InfocalypseRepository>'
+
+    def instance(self, ui, url, create):
+        # Should this use urlmod.url(), or is manual parsing better?
+        #context = {}
+        return InfocalypseRepository(ui, url, create)
+
+hg.schemes["freenet"] = RepoContainer()