+class GitRepository:
+ type = "git"
+
+ def __init__(self, path, options):
+ self.path = path
+ self.options = options
+
+ def name(self):
+ return os.path.basename(self.path)
+
+ def url(self):
+ return self.repo_root()
+
+ def gitweb(self):
+ c = Command("git show | grep commit | awk '{{print $2;}}'", self.options)
+ out = self.__run_in_repo(c.output_of).strip()
+ return "http://git.onelab.eu/?p={}.git;a=commit;h={}".format(self.name(), out)
+
+ def repo_root(self):
+ c = Command("git remote show origin", self.options)
+ out = self.__run_in_repo(c.output_of)
+ for line in out.split('\n'):
+ if line.strip().startswith("Fetch URL:"):
+ return line.split()[2]
+
+ @classmethod
+ def clone(cls, remote, local, options, depth=None):
+ Command("rm -rf {}".format(local), options).run_silent()
+ depth_option = "" if depth is None else " --depth {}".format(depth)
+ Command("git clone{} {} {}".format(depth_option, remote, local), options).run_fatal()
+ return GitRepository(local, options)
+
+ @classmethod
+ def remote_exists(cls, remote, options):
+ return Command("git --no-pager ls-remote {} &> /dev/null".format(remote), options).run()==0
+
+ def tag_exists(self, tagname):
+ command = 'git tag -l | grep "^{}$"'.format(tagname)
+ c = Command(command, self.options)
+ out = self.__run_in_repo(c.output_of, with_stderr=True)
+ return len(out) > 0
+
+ def __run_in_repo(self, fun, *args, **kwargs):
+ cwd = os.getcwd()
+ os.chdir(self.path)
+ ret = fun(*args, **kwargs)
+ os.chdir(cwd)
+ return ret
+
+ def __run_command_in_repo(self, command, ignore_errors=False):
+ c = Command(command, self.options)
+ if ignore_errors:
+ return self.__run_in_repo(c.output_of)
+ else:
+ return self.__run_in_repo(c.run_fatal)
+
+ def __is_commit_id(self, id):
+ c = Command("git show {} | grep commit | awk '{{print $2;}}'".format(id), self.options)
+ ret = self.__run_in_repo(c.output_of, with_stderr=False)
+ if ret.strip() == id:
+ return True
+ return False
+
+ def update(self, subdir=None, recursive=None, branch="master"):
+ if branch == "master":
+ self.__run_command_in_repo("git checkout {}".format(branch))
+ else:
+ self.to_branch(branch, remote=True)
+ self.__run_command_in_repo("git fetch origin --tags", ignore_errors=True)
+ self.__run_command_in_repo("git fetch origin")
+ if not self.__is_commit_id(branch):
+ # we don't need to merge anything for commit ids.
+ self.__run_command_in_repo("git merge --ff origin/{}".format(branch))
+
+ def to_branch(self, branch, remote=True):
+ self.revert()
+ if remote:
+ command = "git branch --track {} origin/{}".format(branch, branch)
+ c = Command(command, self.options)
+ self.__run_in_repo(c.output_of, with_stderr=True)
+ return self.__run_command_in_repo("git checkout {}".format(branch))
+
+ def to_tag(self, tag):
+ self.revert()
+ return self.__run_command_in_repo("git checkout {}".format(tag))
+
+ def tag(self, tagname, logfile):
+ self.__run_command_in_repo("git tag {} -F {}".format(tagname, logfile))
+ self.commit(logfile)
+
+ def diff(self, f=""):
+ c = Command("git diff {}".format(f), self.options)
+ try:
+ return self.__run_in_repo(c.output_of, with_stderr=True)
+ except:
+ return self.__run_in_repo(c.output_of, with_stderr=True, binary=True)
+
+ def diff_with_tag(self, tagname):
+ c = Command("git diff {}".format(tagname), self.options)
+ try:
+ return self.__run_in_repo(c.output_of, with_stderr=True)
+ except:
+ return self.__run_in_repo(c.output_of, with_stderr=True, binary=True)
+
+ def commit(self, logfile, branch="master"):
+ self.__run_command_in_repo("git add .", ignore_errors=True)
+ self.__run_command_in_repo("git add -u", ignore_errors=True)
+ self.__run_command_in_repo("git commit -F {}".format(logfile), ignore_errors=True)
+ if branch == "master" or self.__is_commit_id(branch):
+ self.__run_command_in_repo("git push")
+ else:
+ self.__run_command_in_repo("git push origin {}:{}".format(branch, branch))
+ self.__run_command_in_repo("git push --tags", ignore_errors=True)
+
+ def revert(self, f=""):
+ if f:
+ self.__run_command_in_repo("git checkout {}".format(f))
+ else:
+ # revert all
+ self.__run_command_in_repo("git --no-pager reset --hard")
+ self.__run_command_in_repo("git --no-pager clean -f")
+
+ def is_clean(self):
+ def check_commit():
+ command = "git status"
+ s = "nothing to commit, working directory clean"
+ return Command(command, self.options).output_of(True).find(s) >= 0
+ return self.__run_in_repo(check_commit)
+
+ def is_valid(self):
+ return os.path.exists(os.path.join(self.path, ".git"))
+
+
+class Repository:
+ """
+ Generic repository
+ From old times when we had svn and git
+ """
+ supported_repo_types = [ GitRepository ]
+
+ def __init__(self, path, options):
+ self.path = path
+ self.options = options
+ for repo_class in self.supported_repo_types:
+ self.repo = repo_class(self.path, self.options)
+ if self.repo.is_valid():
+ break
+
+ @classmethod
+ def remote_exists(cls, remote, options):
+ for repo_class in Repository.supported_repo_types:
+ if repo_class.remote_exists(remote, options):
+ return True
+ return False
+
+ def __getattr__(self, attr):
+ return getattr(self.repo, attr)