+def branch_exists(branch):
+ return ref_exists('refs/heads/%s' % branch)
+
+def create_branch(new_branch, tree_id = None):
+ """Create a new branch in the git repository
+ """
+ if branch_exists(new_branch):
+ raise GitException, 'Branch "%s" already exists' % new_branch
+
+ current_head_file = get_head_file()
+ current_head = get_head()
+ set_head_file(new_branch)
+ __set_head(current_head)
+
+ # a checkout isn't needed if new branch points to the current head
+ if tree_id:
+ try:
+ switch(tree_id)
+ except GitException:
+ # Tree switching failed. Revert the head file
+ set_head_file(current_head_file)
+ delete_branch(new_branch)
+ raise
+
+ if os.path.isfile(os.path.join(basedir.get(), 'MERGE_HEAD')):
+ os.remove(os.path.join(basedir.get(), 'MERGE_HEAD'))
+
+def switch_branch(new_branch):
+ """Switch to a git branch
+ """
+ global __head
+
+ if not branch_exists(new_branch):
+ raise GitException, 'Branch "%s" does not exist' % new_branch
+
+ tree_id = rev_parse('refs/heads/%s^{commit}' % new_branch)
+ if tree_id != get_head():
+ refresh_index()
+ try:
+ GRun('read-tree', '-u', '-m', get_head(), tree_id).run()
+ except GitRunException:
+ raise GitException, 'read-tree failed (local changes maybe?)'
+ __head = tree_id
+ set_head_file(new_branch)
+
+ if os.path.isfile(os.path.join(basedir.get(), 'MERGE_HEAD')):
+ os.remove(os.path.join(basedir.get(), 'MERGE_HEAD'))
+
+def delete_ref(ref):
+ if not ref_exists(ref):
+ raise GitException, '%s does not exist' % ref
+ sha1 = GRun('show-ref', '-s', ref).output_one_line()
+ try:
+ GRun('update-ref', '-d', ref, sha1).run()
+ except GitRunException:
+ raise GitException, 'Failed to delete ref %s' % ref
+
+def delete_branch(name):
+ delete_ref('refs/heads/%s' % name)
+
+def rename_ref(from_ref, to_ref):
+ if not ref_exists(from_ref):
+ raise GitException, '"%s" does not exist' % from_ref
+ if ref_exists(to_ref):
+ raise GitException, '"%s" already exists' % to_ref
+
+ sha1 = GRun('show-ref', '-s', from_ref).output_one_line()
+ try:
+ GRun('update-ref', to_ref, sha1, '0'*40).run()
+ except GitRunException:
+ raise GitException, 'Failed to create new ref %s' % to_ref
+ try:
+ GRun('update-ref', '-d', from_ref, sha1).run()
+ except GitRunException:
+ raise GitException, 'Failed to delete ref %s' % from_ref
+
+def rename_branch(from_name, to_name):
+ """Rename a git branch."""
+ rename_ref('refs/heads/%s' % from_name, 'refs/heads/%s' % to_name)
+ try:
+ if get_head_file() == from_name:
+ set_head_file(to_name)
+ except DetachedHeadException:
+ pass # detached HEAD, so the renamee can't be the current branch
+ reflog_dir = os.path.join(basedir.get(), 'logs', 'refs', 'heads')
+ if os.path.exists(reflog_dir) \
+ and os.path.exists(os.path.join(reflog_dir, from_name)):
+ rename(reflog_dir, from_name, to_name)
+
+# Persons caching
+__user = None
+__author = None
+__committer = None
+
+def user():
+ """Return the user information.
+ """
+ global __user
+ if not __user:
+ name=config.get('user.name')
+ email=config.get('user.email')
+ __user = Person(name, email)
+ return __user;
+
+def author():
+ """Return the author information.
+ """
+ global __author
+ if not __author:
+ try:
+ # the environment variables take priority over config
+ try:
+ date = os.environ['GIT_AUTHOR_DATE']
+ except KeyError:
+ date = ''
+ __author = Person(os.environ['GIT_AUTHOR_NAME'],
+ os.environ['GIT_AUTHOR_EMAIL'],
+ date)
+ except KeyError:
+ __author = user()
+ return __author
+
+def committer():
+ """Return the author information.
+ """
+ global __committer
+ if not __committer:
+ try:
+ # the environment variables take priority over config
+ try:
+ date = os.environ['GIT_COMMITTER_DATE']
+ except KeyError:
+ date = ''
+ __committer = Person(os.environ['GIT_COMMITTER_NAME'],
+ os.environ['GIT_COMMITTER_EMAIL'],
+ date)
+ except KeyError:
+ __committer = user()
+ return __committer
+
+def update_cache(files = None, force = False):