Commits

Trent Mick committed 6ba96d5

half-baked support for implicitly created Source instances from a path. Basically so
can do "vc changes -d ." in an svn working copy, at least. Probably won't work
for the other repo types.

Comments (0)

Files changed (1)

                           "this to determine where to put its config file")
         return expanduser(join("~", ".vc", "sources"))
 
-    def source_from_path(self, path):
+    def source_from_path(self, path, allow_implicit=True):
         """Determine which VC source the given path belongs to.
 
+        @param path {str} The directory path of the vc working copy.
+        @param allow_implicit {bool} Default true. Whether to implicitly
+            create a `Source` for this dir if it isn't registered in the
+            vc config ("~/.vc/sources").
+        @returns {Source} A source subclass for the particular vc
+            type. Or None if could not create one.
+        
         Returns None if it is not a part of any configured repo source.
         """
         path_bits = tuple(_splitall(normpath(normcase(abspath(path)))))
             if src_path_bits == path_bits[:len(src_path_bits)]:
                 return source
             #log.debug("`%s' is not under `%s'", path, source.local_path)
+        if allow_implicit:
+            return self._implicit_source_from_path(path)
         return None
 
+    def _implicit_source_from_path(self, path):
+        def _find_path_in_parent_dirs(d, subpath):
+            while True:
+                if exists(join(d, subpath)):
+                    return d
+                old_d = d
+                d, b = os.path.split(d)
+                if d == old_d:
+                    return None
+        
+        if exists(join(path, ".svn")):
+            return SVNSource(path, _svn_info(path)["URL"])
+        else:
+            git_root = _find_path_in_parent_dirs(".git")
+            if git_root:
+                #XXX Half baked.
+                return GitSource(git_root)
+            hg_root = _find_path_in_parent_dirs(".hg")
+            if hg_root:
+                #XXX Half baked.
+                return HGSource(hg_root)
+
     def sources_under_path(self, path):
         """Yield all VC sources under the given path."""
         path_bits = tuple(_splitall(normpath(normcase(abspath(path)))))
 
 #---- internal support functions
 
+def _capture_stdout(argv, ignore_retval=False):
+    import subprocess
+    p = subprocess.Popen(argv, stdout=subprocess.PIPE)
+    stdout = p.stdout.read()
+    retval = p.wait()
+    if retval and not ignore_retval:
+        raise OSError("error running '%s'" % ' '.join(argv))
+    return stdout
+
+def _svn_ls(url):
+    output = _capture_stdout(["svn", "ls", url])
+    files = []
+    for line in output.splitlines(0):
+        if not line.strip(): continue
+        files.append(line)
+    return files
+
+def _svn_info(target):
+    stdout = _capture_stdout(["svn", "info", target])
+    if "Last Changed Rev:" not in stdout:  # landmark
+        raise RuntimeError("error getting svn info for `%s' "
+            "(svn info output was %r)" % (target, stdout))
+    info = {}
+    for line in stdout.splitlines(0):
+        if not line.strip(): continue
+        key, value = line.split(":", 1)
+        info[key.strip()] = value.strip()
+    return info
+
 # Recipe: splitall (0.2-)
 def _splitall(path):
     allparts = []