hg site extension
 
(Arne Babenhauserheide)
2013-08-23: merge default into releases for release. releases 0.4

merge default into releases for release.

diff --git a/.bugs/bugs b/.bugs/bugs
--- a/.bugs/bugs
+++ b/.bugs/bugs
@@ -26,6 +26,7 @@ add linenumbers to the src files.       
 commits as commit/<rev>/ for long term viability. .html as suffix is not as long lived as a simple dirname. | owner:Arne Babenhauserheide <bab@draketo.de>, open:True, id:baaad4bdc13d7155048ce6a9dde92dc857b6a1ac, time:1319148414.16
 clone/<pathname>/ → incoming log (commits) + possibly an associated issue in b. | owner:Arne Babenhauserheide <bab@draketo.de>, open:True, id:c58557260a47597ac5057703e26a94df190a2a5d, time:1319147661.8
 only write bug files if their content differs from the content on disk to avoid reuploading them without need | owner:, open:True, id:c7b2cf5fdbc68160530a08e1d651c23a1f49fc01, time:1365865229.49
+incoming commits have no diffstat                            | owner:, open:True, id:cf36e24ae03073dd92827cbd151e3ca0a6d1dd52, time:1365872088.99
 no longer create raw files, since they can’t be served by all webservers and waste bandwidth and space (they are no longer linked anyway). | owner:Arne Babenhauserheide <bab@draketo.de>, open:False, id:d1010e1933648f65af37d969bfb45f8d834fc8bb, time:1319148721.49
 add a footer which links back to hgsite                      | owner:Arne Babenhauserheide <bab@draketo.de>, open:True, id:ed55d92709329e81b8d619c4d2a9aa97361fefd8, time:1365863559.31
 check the hgweb templating for parsing the site.             | owner:Arne Babenhauserheide <bab@draketo.de>, open:True, id:ef17f01dbe8ee58536fa8b345eb18d1efc639f15, time:1319208643.38
diff --git a/.bugs/details/cf36e24ae03073dd92827cbd151e3ca0a6d1dd52.txt b/.bugs/details/cf36e24ae03073dd92827cbd151e3ca0a6d1dd52.txt
new file mode 100644
--- /dev/null
+++ b/.bugs/details/cf36e24ae03073dd92827cbd151e3ca0a6d1dd52.txt
@@ -0,0 +1,30 @@
+# Lines starting with '#' and sections without content
+# are not displayed by a call to 'details'
+#
+[paths]
+# Paths related to this bug.
+# suggested format: REPO_PATH:LINENUMBERS
+
+
+[details]
+# Additional details
+ctxdiffstat(ui, repo, ctx) uses dispatch which likely would have to use the ui object of the remote repo (or 
+something).
+
+It outputs the diffstat to stdout instead of writing it to the file.
+
+
+[expected]
+# The expected result
+
+
+[actual]
+# What happened instead
+
+
+[reproduce]
+# Reproduction steps
+
+
+[comments]
+# Comments and updates - leave your name
diff --git a/staticsite.py b/staticsite.py
--- a/staticsite.py
+++ b/staticsite.py
@@ -11,7 +11,7 @@ serve and full solutions like bitbucket 
 without the interactivity).
 """
 
-__copyright__ = """Copyright 2012 Arne Babenhauserheide
+__copyright__ = """Copyright 2012 till 2013 Arne Babenhauserheide
 
 This software may be used and distributed according to the terms of the
 GNU General Public License version 2 or any later version.
@@ -202,6 +202,7 @@ def overviewlogstring(ui, repo, revs, te
 
 def writeoverview(ui, repo, target, name):
     """Create the overview page"""
+    ui.debug("[staticsite] writeoverview: header\n")
     overview = ""
     # get the title
     overview += templates["head"].replace("{reponame}", name).replace("{title}", name)
@@ -227,20 +228,26 @@ def writeoverview(ui, repo, target, name
         else:
             overview += " <span class=\"bugnumber openbugnumberzero\">0</span>"
         overview += "<span class=\"bugnumber resolvedbugnumber\"><a href=\"bugs#resolved\">" + str(len(resolvedbugs)) + "√</a></span>)</span>"
-        
+    
     # and the forks
+    ui.debug("[staticsite] writeoverview: header: forks\n")
     forks = getforkinfo(ui, target)
     if forks:
         overview += " | " + _("forks: ")
         for forkname, forkuri in forks.items():
+            ui.debug("[staticsite] writeoverview: fork: " + forkname + ": " + forkuri + "\n")
+            ui.debug("[staticsite] writeoverview: forks: getforkdir\n")
             overview += "<a href='" + getforkdir(target, forkname) + "'>" + forkname + "</a> "
+            ui.debug("[staticsite] writeoverview: forks: getincoming\n")
             incoming, fn, localother = getincoming(ui, repo, otheruri=forkuri, othername=forkname)
             overview += "<small>(" + str(len(incoming))
+            ui.debug("[staticsite] writeoverview: forks: getoutgoing\n")
             outgoing, fn, localother = getoutgoing(ui, repo, otheruri=forkuri, othername=forkname)
             overview += "<small>↓↑</small>" + str(len(outgoing)) + ")</small> "
             
     overview += "</p>"
     
+    ui.debug("[staticsite] writeoverview: shortlog\n")
     # now add the 5 most recent log entries
     # divert all following ui output to a string, so we can just use standard functions
     overview += "\n<div id='shortlog'><h2>Changes (<a href='commits'>full changelog</a>)</h2>\n"
@@ -254,6 +261,7 @@ def writeoverview(ui, repo, target, name
     overview += "</div>"
     
     # Add branch, bookmark and tag information, if they exist.
+    ui.debug("[staticsite] writeoverview: branches, tags and bookmarks\n")
     branches = []
     for branch, heads in repo.branchmap().items(): 
         if branch and branch != "default": # not default
@@ -302,11 +310,13 @@ def writeoverview(ui, repo, target, name
                 "XXXXX", "{date|shortdate}"))
         overview += "</div>"
     # add the full readme
+    ui.debug("[staticsite] writeoverview: readme\n")
     overview += "<div id='readme'><h2>"+_("Readme")+"</h2>\n"
     overview += readme
     overview += "</div>"
     
     # finish the overview
+    ui.debug("[staticsite] writeoverview: footer\n")
     overview += templates["foot"]
     indexfile = os.path.join(target, "index.html")
     if not contentequals(indexfile, overview): 
@@ -393,7 +403,7 @@ def getincoming(ui, repo, otheruri, othe
         pass
     # cannot do that for ftp or freenet insertion uris (freenet
     # separates insertion and retrieval by private/public key)
-    isftpuri = otheruri.startswith("ftp://")
+    isftpuri = otheruri.startswith("ftp://") or otheruri.startswith("ftps://")
     isfreenetpriv = _freenetprivkeystring in otheruri
     if isftpuri or isfreenetpriv:
         chlist = []
@@ -423,37 +433,54 @@ def getoutgoing(ui, repo, otheruri, othe
         pass
     # cannot do that for ftp or freenet insertion uris (freenet
     # separates insertion and retrieval by private/public key)
-    isftpuri = otheruri.startswith("ftp://")
+    ui.debug("[staticsite] getoutgoing: checkkeys\n")
+    isftpuri = otheruri.startswith("ftp://") or otheruri.startswith("ftps://")
+
     isfreenetpriv = "AQECAAE/" in otheruri
     if isftpuri or isfreenetpriv:
         chlist = []
         return chlist, cleanupfn, other
     
     if not other: 
+        ui.debug("[staticsite] getoutgoing: findpeer\n")
         other = hg.peer(repo, {}, otheruri)
         
     def outgoingchanges(repo, other):
         from mercurial import discovery
         fco = discovery.findcommonoutgoing
         og = fco(repo, other, force=True)
+        # print dir(og), og.missingheads, og.missing
+        ui.debug("[staticsite] getoutgoing: outgoingchanges: og.missing\n")
         try:
             return og.missing
         except AttributeError: # old client
             common, outheads = og
             o = repo.changelog.findmissing(common=common, heads=outheads)
             return o
-            
+        except Exception as e:
+            ui.warn("Cannot parse parts of your local history. Likely your local repository is broken. You might be able to fix it by getting a fresh clone and pulling from your current repo. The Error was: " + str(e) + "\n")
+    
     other.ui.pushbuffer() # ignore ui events
     
+    ui.debug("[staticsite] getoutgoing: outgoingchanges\n")
     try:
         chlist = outgoingchanges(repo, other)
     except (AttributeError, util.Abort):
         other.ui.popbuffer()
+        ui.debug("[staticsite] getoutgoing: local other\n")
         other = getlocalother(repo, ui, otheruri, othername)
         other.ui.pushbuffer()
-        chlist = outgoingchanges(repo, other)
+        ui.debug("[staticsite] getoutgoing: outgoingchanges, local\n")
+        try:
+            chlist = outgoingchanges(repo, other)
+        except Exception as e: # no changes
+            ui.debug("[staticsite] getoutgoing: outgoingchanges, errer: " + str(e) + "\n")
+            raise
+            chlist = []
         
+    ui.debug("[staticsite] getoutgoing: popbuffer\n")
     other.ui.popbuffer()
+    ui.debug("[staticsite] getoutgoing: done\n")
     return chlist, cleanupfn, other
 
 def getforkinfo(ui, target):
@@ -782,22 +809,28 @@ def parsesite(ui, repo, target, **opts):
         highlightingfile = os.path.join(target, pretty)
         if not os.path.isfile(highlightingfile) or not samefilecontent(highlightingsrc, highlightingfile):
             shutil.copyfile(highlightingsrc, highlightingfile)
-            
+    
+    ui.debug("[staticsite] writeoverview\n")
     # then the overview
     writeoverview(ui, repo, target, name)
     
+    ui.debug("[staticsite] writelog\n")
     # and the log
     writelog(ui, repo, target, name)
     
+    ui.debug("[staticsite] writecommits\n")
     # and all commit files
     writecommits(ui, repo, target, name, force=opts["force"])
     
+    ui.debug("[staticsite] writesourcetree\n")
     # and all file data
     writesourcetree(ui, repo, target, name, force=opts["force"])
     
+    ui.debug("[staticsite] writeforks\n")
     # and all forks
     writeforks(ui, repo, target, name)
     
+    ui.debug("[staticsite] writebugs\n")
     # and all bugs
     writebugs(ui, repo, target, name)
 
@@ -829,7 +862,8 @@ def upload(ui, repo, target, ftpstring, 
     ftppath = "/".join(serverandpath.split("/")[1:])
     timeout = 10
     try:
-        ftp = ftplib.FTP(server, user, password, "", timeout)
+        ftp = ftplib.FTP_TLS(server, user, password, "", timeout=timeout)
+        ftp.prot_p()
     except socket.timeout:
         ui.warn(_("connection to "), server, _(" timed out after "), timeout, _(" seconds.\n"))
         return
@@ -992,6 +1026,7 @@ def ftppush(orig, *args, **opts):
     # first create the site at ._site
     target = "._site"
     ftpstring = path.replace("ftp://", "")
+    ftpstring = path.replace("ftps://", "")
     # fix the options to fit those of the site command
     opts["name"] = opts["sitename"]
     opts["upload"] = ftpstring
@@ -1024,7 +1059,7 @@ class FTPRepository(peerrepository):
         self.create = create
         self.ui = ui
         self.path = path
-        self.capabilities = set(["ftp"])
+        self.capabilities = set(["ftp", "ftps"])
 
     def lock(self):
         """We cannot really lock FTP repos, yet.
@@ -1081,6 +1116,7 @@ class RepoContainer(object):
         return FTPRepository(ui, url, create)
 
 hg.schemes["ftp"] = RepoContainer()
+hg.schemes["ftps"] = RepoContainer()
 
 def test():
     import subprocess as sp