tab -> spaces
[mass-build.git] / vcs.py
diff --git a/vcs.py b/vcs.py
index 73ffb10113c2c005aac7f9668b8fd01d2e4131ee..9f53fe257ab586bca5ca5a96eed226057c4f848f 100644 (file)
--- a/vcs.py
+++ b/vcs.py
@@ -1,5 +1,5 @@
 # mass-build - Easily Build Software Involving a Large Amount of Source Repositories
 # mass-build - Easily Build Software Involving a Large Amount of Source Repositories
-# Copyright (C) 2012 Ralf Jung <post@ralfj.de>
+# Copyright (C) 2012-2013 Ralf Jung <post@ralfj.de>
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
 # along with this program; if not, write to the Free Software
 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
-import os, git, subprocess, re
+import os, subprocess, re
 
 '''A VCS must have an "update" method with an optional "mode" parameter taking one of the three values below,
 
 '''A VCS must have an "update" method with an optional "mode" parameter taking one of the three values below,
-   a "version" method returning a version name (or None),
-   and a "newVersions" method which checks for new versions and prints the result to standard output.'''
+a "version" method returning a version name (or None),
+and a "newVersions" method which checks for new versions and prints the result to standard output.'''
 MODE_FETCH = 0
 MODE_REBASE = 1
 MODE_RESET = 2
 
 def natural_sort_key(val):
 MODE_FETCH = 0
 MODE_REBASE = 1
 MODE_RESET = 2
 
 def natural_sort_key(val):
-       return [ (int(c) if c.isdigit() else c) for c in re.split('([0-9]+)', val) ]
+    return [ (int(c) if c.isdigit() else c) for c in re.split('([0-9]+)', val) ]
 
 def get_non_digit_prefix(val):
 
 def get_non_digit_prefix(val):
-       return re.match('[^0-9]*', val).group(0)
+    return re.match('[^0-9]*', val).group(0)
+
+class GitCommand:
+    def __getattr__(self, name):
+        def call(*args, get_stderr = False):
+            cmd = ["git", name.replace('_', '-')] + list(args)
+            output = subprocess.check_output(cmd, stderr=subprocess.STDOUT if get_stderr else None)
+            return output.decode('utf-8').strip('\n')
+        return call
+git = GitCommand()
 
 # Fetch updates from git
 class Git:
 
 # Fetch updates from git
 class Git:
-       def __init__(self, folder, config):
-               self.folder = os.path.abspath(folder)
-               self.url = config['url']
-               self.commit = config['version']
-
-       class _ProgressPrinter(git.remote.RemoteProgress):
-               def update(self, op_code, cur_count, max_count=None, message=''):
-                       print self._cur_line+(" "*30)+"\r",
+    def __init__(self, folder, config):
+        self.folder = os.path.abspath(folder)
+        self.url = config['url']
+        self.commit = config['version']
 
 
-       def update(self, mode = MODE_REBASE):
-               isBranch = (self.commit.startswith('origin/'))
-               if isBranch:
-                       branchname = self.commit[len('origin/'):]
-               else:
-                       branchname = "tag"
-               # get us a git repository, and the "origin" remote
-               if os.path.exists(self.folder):
-                       # load existing repo
-                       repo = git.Repo(self.folder)
-                       origin = repo.remotes.origin
-                       origin.config_writer.set_value("url", self.url) # make sure we use the current URL
-               else:
-                       # create a new one
-                       os.makedirs(self.folder)
-                       repo = git.Repo.init(self.folder)
-                       origin = repo.create_remote('origin', self.url)
-               origin.fetch(progress=Git._ProgressPrinter()) # download new data
-               print " "*80+"\r", # clean the line we are in
-               if mode == MODE_FETCH:
-                       return
-               # create/find correct branch
-               if branchname in repo.heads:
-                       branch = repo.heads[branchname]
-               else:
-                       branch = repo.create_head(branchname, self.commit)
-                       if isBranch: # track remote branch
-                               branch.set_tracking_branch(origin.refs[branchname])
-               # update it to the latest remote commit
-               branch.checkout()
-               if mode == MODE_RESET:
-                       repo.head.reset(self.commit, working_tree=True)
-               else:
-                       repo.git.rebase(self.commit)
-               # update submodules
-               repo.git.submodule("update", "--init", "--recursive", "--rebase")
-               # done
-               print "...done",
-               if repo.head.reference.commit != repo.commit(self.commit):
-                       print "(keeping local patches around)",
-               print
+    def update(self, mode = MODE_REBASE):
+        isBranch = (self.commit.startswith('origin/'))
+        if isBranch:
+            branchname = self.commit[len('origin/'):]
+        else:
+            branchname = "tag"
+        # get us a git repository, and the "origin" remote
+        if os.path.exists(self.folder):
+            # load existing repo
+            os.chdir(self.folder)
+            git.remote("set-url", "origin", self.url) # make sure we use the current URL
+        else:
+            # create a new one
+            os.makedirs(self.folder)
+            os.chdir(self.folder)
+            git.init()
+            git.remote("add", "origin", self.url)
+        git.fetch("origin")
+        if mode == MODE_FETCH:
+            return
+        # create/find correct branch
+        if not git.branch("--list", branchname): # the branch does not yet exit
+            git.branch(branchname, self.commit)
+            if isBranch: # make sure we track the correct remote branch
+                git.branch("-u", self.commit, branchname)
+        # update it to the latest remote commit
+        git.checkout(branchname, get_stderr=True)
+        if mode == MODE_RESET:
+            git.reset("--hard", self.commit)
+        else:
+            git.rebase(self.commit)
+        # update submodules
+        git.submodule("update", "--init", "--recursive", "--rebase")
+        # done
+        print("...done", end=' ')
+        if git.rev_parse("HEAD") != git.rev_parse(self.commit):
+            print("(keeping local patches around)", end=' ')
+        print()
 
 
-       def version(self):
-               repo = git.Repo(self.folder)
-               v = repo.git.describe()
-               return v[len(get_non_digit_prefix(v)):] # remove the non-digit prefix from v (so that it starts with a number)
+    def version(self):
+        v = git.describe()
+        return v[len(get_non_digit_prefix(v)):] # remove the non-digit prefix from v (so that it starts with a number)
 
 
-       def checkVersions(self):
-               self.update(mode = MODE_FETCH)
-               repo = git.Repo(self.folder)
-               currentVersion = repo.git.describe()
-               # get sorted list of tag names with the same non-digit prefix and higher version number
-               tags = map(str, repo.tags)
-               tags = filter(lambda t: get_non_digit_prefix(t) == get_non_digit_prefix(currentVersion) and natural_sort_key(t) > natural_sort_key(currentVersion), tags)
-               if not tags: return
-               tags.sort(key = natural_sort_key)
-               print "Versions newer than "+currentVersion+" available:"
-               print tags
+    def checkVersions(self):
+        self.update(mode = MODE_FETCH)
+        currentVersion = git.describe()
+        # get sorted list of tag names with the same non-digit prefix and higher version number
+        tags = git.tag().split('\n')
+        tags = [t for t in tags if get_non_digit_prefix(t) == get_non_digit_prefix(currentVersion) and natural_sort_key(t) > natural_sort_key(currentVersion)]
+        if not tags: return
+        tags.sort(key = natural_sort_key)
+        print("Versions newer than "+currentVersion+" available:")
+        print(tags)
 
 # Fetch updates via SVN
 class SVN:
 
 # Fetch updates via SVN
 class SVN:
-       def __init__(self, folder, url):
-               self.folder = os.path.abspath(folder)
-               self.url = url
+    def __init__(self, folder, url):
+        self.folder = os.path.abspath(folder)
+        self.url = url
 
 
-       def update(self, mode = MODE_REBASE):
-               if mode == MODE_FETCH: raise Exception("Just fetching is not supported with SVN")
-               if os.path.exists(self.folder):
-                       os.chdir(self.folder) # go into repository
-                       if mode == MODE_RESET: subprocess.check_call(['svn', 'revert', '-R', '.'])
-                       subprocess.check_call(['svn', 'switch', self.url]) # and update to the URL we got
-               else:
-                       os.makedirs(self.folder) # if even the parent folder does not exist, svn fails
-                       subprocess.check_call(['svn', 'co', self.url, self.folder]) # just download it
-       
-       def version(self):
-               return None
-       
-       def checkVersions(self):
-               print "Version checking not supporting with SVN"
+    def update(self, mode = MODE_REBASE):
+        if mode == MODE_FETCH: raise Exception("Just fetching is not supported with SVN")
+        if os.path.exists(self.folder):
+            os.chdir(self.folder) # go into repository
+            if mode == MODE_RESET: subprocess.check_call(['svn', 'revert', '-R', '.'])
+            subprocess.check_call(['svn', 'switch', self.url]) # and update to the URL we got
+        else:
+            os.makedirs(self.folder) # if even the parent folder does not exist, svn fails
+            subprocess.check_call(['svn', 'co', self.url, self.folder]) # just download it
+    
+    def version(self):
+        return None
+    
+    def checkVersions(self):
+        print("Version checking not supporting with SVN")