X-Git-Url: https://git.distorted.org.uk/~mdw/stgit/blobdiff_plain/fcc1ad70be5ff89996358b6d770cd229fec2a3ba..a622d90bbeccf29319dc2c3142a93ae1010648fe:/stgit/git.py diff --git a/stgit/git.py b/stgit/git.py index abdd2fc..8306c85 100644 --- a/stgit/git.py +++ b/stgit/git.py @@ -27,12 +27,6 @@ class GitException(Exception): pass -# Different start-up variables read from the environment -if 'GIT_DIR' in os.environ: - base_dir = os.environ['GIT_DIR'] -else: - base_dir = '.git' - # # Classes @@ -87,6 +81,23 @@ __commits = dict() # # Functions # + +# GIT_DIR value cached +__base_dir = None + +def get_base_dir(): + """Different start-up variables read from the environment + """ + global __base_dir + + if not __base_dir: + if 'GIT_DIR' in os.environ: + __base_dir = os.environ['GIT_DIR'] + else: + __base_dir = _output_one_line('git-rev-parse --git-dir') + + return __base_dir + def get_commit(id_hash): """Commit objects factory. Save/look-up them in the __commits dictionary @@ -103,7 +114,7 @@ def get_commit(id_hash): def get_conflicts(): """Return the list of file conflicts """ - conflicts_file = os.path.join(base_dir, 'conflicts') + conflicts_file = os.path.join(get_base_dir(), 'conflicts') if os.path.isfile(conflicts_file): f = file(conflicts_file) names = [line.strip() for line in f.readlines()] @@ -167,9 +178,6 @@ def __run(cmd, args=None): return r return 0 -def __check_base_dir(): - return os.path.isdir(base_dir) - def __tree_status(files = None, tree_id = 'HEAD', unknown = False, noexclude = True): """Returns a list of pairs - [status, filename] @@ -182,7 +190,7 @@ def __tree_status(files = None, tree_id = 'HEAD', unknown = False, # unknown files if unknown: - exclude_file = os.path.join(base_dir, 'info', 'exclude') + exclude_file = os.path.join(get_base_dir(), 'info', 'exclude') base_exclude = ['--exclude=%s' % s for s in ['*.[ao]', '*.pyc', '.*', '*~', '#*', 'TAGS', 'tags']] base_exclude.append('--exclude-per-directory=.gitignore') @@ -205,7 +213,7 @@ def __tree_status(files = None, tree_id = 'HEAD', unknown = False, cache_files += [('C', filename) for filename in conflicts] # the rest - for line in _output_lines(['git-diff-index', '-r', tree_id] + files): + for line in _output_lines(['git-diff-index', tree_id] + files): fs = tuple(line.rstrip().split(' ',4)[-1].split('\t',1)) if fs[1] not in conflicts: cache_files.append(fs) @@ -253,6 +261,9 @@ def __set_head(val): raise GitException, 'Could not update HEAD to "%s".' % val __head = val + # only allow SHA1 hashes + assert(len(__head) == 40) + def __clear_head_cache(): """Sets the __head to None so that a re-read is forced """ @@ -274,7 +285,7 @@ def rev_parse(git_id): raise GitException, 'Unknown revision: %s' % git_id def branch_exists(branch): - """Existance check for the named branch + """Existence check for the named branch """ for line in _output_lines(['git-rev-parse', '--symbolic', '--all']): if line.strip() == branch: @@ -296,8 +307,8 @@ def create_branch(new_branch, tree_id = None): if tree_id: switch(tree_id) - if os.path.isfile(os.path.join(base_dir, 'MERGE_HEAD')): - os.remove(os.path.join(base_dir, 'MERGE_HEAD')) + if os.path.isfile(os.path.join(get_base_dir(), 'MERGE_HEAD')): + os.remove(os.path.join(get_base_dir(), 'MERGE_HEAD')) def switch_branch(name): """Switch to a git branch @@ -308,7 +319,7 @@ def switch_branch(name): if not branch_exists(new_head): raise GitException, 'Branch "%s" does not exist' % name - tree_id = rev_parse(new_head + '^0') + tree_id = rev_parse(new_head + '^{commit}') if tree_id != get_head(): refresh_index() if __run('git-read-tree -u -m', [get_head(), tree_id]) != 0: @@ -316,8 +327,8 @@ def switch_branch(name): __head = tree_id set_head_file(new_head) - if os.path.isfile(os.path.join(base_dir, 'MERGE_HEAD')): - os.remove(os.path.join(base_dir, 'MERGE_HEAD')) + if os.path.isfile(os.path.join(get_base_dir(), 'MERGE_HEAD')): + os.remove(os.path.join(get_base_dir(), 'MERGE_HEAD')) def delete_branch(name): """Delete a git branch @@ -325,7 +336,7 @@ def delete_branch(name): branch_head = os.path.join('refs', 'heads', name) if not branch_exists(branch_head): raise GitException, 'Branch "%s" does not exist' % name - os.remove(os.path.join(base_dir, branch_head)) + os.remove(os.path.join(get_base_dir(), branch_head)) def rename_branch(from_name, to_name): """Rename a git branch @@ -339,7 +350,8 @@ def rename_branch(from_name, to_name): if get_head_file() == from_name: set_head_file(to_head) - os.rename(os.path.join(base_dir, from_head), os.path.join(base_dir, to_head)) + os.rename(os.path.join(get_base_dir(), from_head), \ + os.path.join(get_base_dir(), to_head)) def add(names): """Add the files or recursively add the directory contents @@ -461,21 +473,27 @@ def commit(message, files = None, parents = None, allowempty = False, return commit_id -def apply_diff(rev1, rev2): +def apply_diff(rev1, rev2, check_index = True): """Apply the diff between rev1 and rev2 onto the current index. This function doesn't need to raise an exception since it is only used for fast-pushing a patch. If this operation fails, the pushing would fall back to the three-way merge. """ - return os.system('git-diff-tree -p %s %s | git-apply --index 2> /dev/null' - % (rev1, rev2)) == 0 + if check_index: + index_opt = '--index' + else: + index_opt = '' + cmd = 'git-diff-tree -p %s %s | git-apply %s 2> /dev/null' \ + % (rev1, rev2, index_opt) + + return os.system(cmd) == 0 def merge(base, head1, head2): """Perform a 3-way merge between base, head1 and head2 into the local tree """ refresh_index() - if __run('git-read-tree -u -m', [base, head1, head2]) != 0: + if __run('git-read-tree -u -m --aggressive', [base, head1, head2]) != 0: raise GitException, 'git-read-tree failed (local changes maybe?)' # this can fail if there are conflicts @@ -598,21 +616,18 @@ def switch(tree_id): __set_head(tree_id) -def reset(tree_id = None): +def reset(files = None, tree_id = None): """Revert the tree changes relative to the given tree_id. It removes any local changes """ if not tree_id: tree_id = get_head() - cache_files = __tree_status(tree_id = tree_id) - rm_files = [x[1] for x in cache_files if x[0] in ['D']] - - checkout(tree_id = tree_id, force = True) - __set_head(tree_id) + checkout(files, tree_id, True) - # checkout doesn't remove files - map(os.remove, rm_files) + # if the reset refers to the whole tree, switch the HEAD as well + if not files: + __set_head(tree_id) def pull(repository = 'origin', refspec = None): """Pull changes from the remote repository. At the moment, just