aheinecke@321: # Copyright (C) 2010 by Intevation GmbHi aheinecke@321: # Authors: aheinecke@321: # Andre Heinecke aheinecke@321: # aheinecke@321: # This program is free software under the GPL (>=v2) aheinecke@321: # Read the file COPYING coming with the software for details. aheinecke@321: aheinecke@321: """Collection of Git utility code""" aheinecke@321: aheinecke@321: import os aheinecke@321: import shutil aheinecke@321: import re aheinecke@321: import StringIO aheinecke@321: aheinecke@321: import run aheinecke@321: from cmdexpand import cmdexpand aheinecke@321: from util import extract_value_for_key aheinecke@321: aheinecke@321: aheinecke@321: class GitError(Exception): aheinecke@321: aheinecke@321: """Base class for Git specific errors raised by TreePKG""" aheinecke@321: aheinecke@321: def checkout(url, localdir, branch=None): aheinecke@321: """Clones the repository at url into the localdir""" aheinecke@321: run.call(cmdexpand("git clone $url $localdir", **locals())) aheinecke@321: if branch: aheinecke@323: run.call(cmdexpand("git checkout --track -b local $branch", aheinecke@323: **locals()), cwd=localdir) aheinecke@321: else: aheinecke@323: run.call(cmdexpand("git checkout --track -b local master"), aheinecke@323: cwd=localdir) aheinecke@321: aheinecke@321: def update(localdir, revision=None): aheinecke@321: """Runs git pull on the localdir.""" aheinecke@323: run.call(cmdexpand("git pull -q"), cwd=localdir) aheinecke@321: aheinecke@321: def export(src, dest): aheinecke@321: """Exports the local branch from src to dest""" aheinecke@446: dest = dest + os.sep aheinecke@323: run.call(cmdexpand("git checkout-index -a -f --prefix=$dest", **locals()), aheinecke@323: cwd=src) aheinecke@321: aheinecke@321: def last_changed_revision(git_working_copy): aheinecke@328: """Return the SHA1 sum of the latest commit""" aheinecke@328: output = run.capture_output(cmdexpand("git rev-parse HEAD"), aheinecke@328: cwd=git_working_copy) aheinecke@321: if output is None: aheinecke@321: raise GitError("Cannot determine last changed revision for %r" aheinecke@328: % git_working_copy) aheinecke@328: return output.strip() aheinecke@321: aheinecke@321: class GitRepository(object): aheinecke@321: aheinecke@321: """Describes a git repository""" aheinecke@321: aheinecke@321: def __init__(self, url, branch=None): aheinecke@321: """Initialize the git repository description aheinecke@321: Parameters: aheinecke@321: url -- The url of the repository aheinecke@321: aheinecke@321: branch -- The name of the remote Branch to track aheinecke@321: defaults to master aheinecke@321: """ aheinecke@321: self.url = url aheinecke@321: self.branch = branch aheinecke@321: aheinecke@321: def checkout(self, localdir): aheinecke@321: """Checks out the repository into localdir.""" aheinecke@321: checkout(self.url , localdir, self.branch) aheinecke@321: update(localdir) aheinecke@321: aheinecke@321: def export(self, localdir, destdir): aheinecke@321: """Exports the working copy in localdir to destdir""" aheinecke@321: export(localdir, destdir) aheinecke@321: aheinecke@321: def last_changed_revision(self, localdir): aheinecke@321: """Returns the last changed revision of the working copy in localdir""" aheinecke@321: return last_changed_revision(localdir) aheinecke@321: aheinecke@321: def check_working_copy(self, localdir): aheinecke@321: """ aheinecke@321: FIXME STUB: Not implemented for git aheinecke@321: """ aheinecke@321: return None aheinecke@321: aheinecke@321: class GitWorkingCopy(object): aheinecke@321: aheinecke@321: """Represents a checkout of a git repository""" aheinecke@321: aheinecke@321: def __init__(self, repository, localdir, logger=None): aheinecke@321: """ aheinecke@321: Initialize the working copy. aheinecke@321: Parameters: aheinecke@321: repository -- The GitRepository instance describing the aheinecke@321: repository aheinecke@321: localdir -- The directory for the working copy aheinecke@321: logger -- logging object to use for some info/debug messages aheinecke@321: """ aheinecke@321: self.repository = repository aheinecke@321: self.localdir = localdir aheinecke@321: self.logger = logger aheinecke@321: aheinecke@321: def log_info(self, *args): aheinecke@321: if self.logger is not None: aheinecke@321: self.logger.info(*args) aheinecke@321: aheinecke@321: def update_or_checkout(self, revision=0): aheinecke@321: """Updates the working copy or creates by checking out the repository. aheinecke@321: Revision number included for compatibility aheinecke@321: """ aheinecke@321: if os.path.exists(self.localdir): aheinecke@321: self.log_info("Updating the working copy in %r", self.localdir) aheinecke@321: update(self.localdir, self.repository.branch) aheinecke@321: else: aheinecke@321: self.log_info("The working copy in %r doesn't exist yet." aheinecke@321: " Checking out from %r", aheinecke@321: self.localdir, self.repository.url) aheinecke@321: self.repository.checkout(self.localdir) aheinecke@321: aheinecke@321: def export(self, destdir): aheinecke@321: """Exports the working copy to destdir""" aheinecke@321: self.repository.export(self.localdir, destdir) aheinecke@321: aheinecke@321: def last_changed_revision(self): aheinecke@321: """Returns the last changed rev of the working copy""" aheinecke@321: return self.repository.last_changed_revision(self.localdir)