X-Git-Url: https://git.distorted.org.uk/~mdw/stgit/blobdiff_plain/1777d8cd458bddb1a5f638e8928726167469154c..774fc1e90859cc68926f4357347b0624d43f746f:/stgit/git.py diff --git a/stgit/git.py b/stgit/git.py index 40d54ef..6b14a74 100644 --- a/stgit/git.py +++ b/stgit/git.py @@ -18,8 +18,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ -import sys, os, popen2 +import sys, os, popen2, re, gitmergeonefile +from stgit import basedir from stgit.utils import * # git exception class @@ -82,14 +83,6 @@ __commits = dict() # Functions # -def get_base_dir(): - """Different start-up variables read from the environment - """ - if 'GIT_DIR' in os.environ: - return os.environ['GIT_DIR'] - else: - return _output_one_line('git-rev-parse --git-dir') - def get_commit(id_hash): """Commit objects factory. Save/look-up them in the __commits dictionary @@ -106,7 +99,7 @@ def get_commit(id_hash): def get_conflicts(): """Return the list of file conflicts """ - conflicts_file = os.path.join(get_base_dir(), 'conflicts') + conflicts_file = os.path.join(basedir.get(), 'conflicts') if os.path.isfile(conflicts_file): f = file(conflicts_file) names = [line.strip() for line in f.readlines()] @@ -182,7 +175,7 @@ def __tree_status(files = None, tree_id = 'HEAD', unknown = False, # unknown files if unknown: - exclude_file = os.path.join(get_base_dir(), 'info', 'exclude') + exclude_file = os.path.join(basedir.get(), 'info', 'exclude') base_exclude = ['--exclude=%s' % s for s in ['*.[ao]', '*.pyc', '.*', '*~', '#*', 'TAGS', 'tags']] base_exclude.append('--exclude-per-directory=.gitignore') @@ -194,8 +187,8 @@ def __tree_status(files = None, tree_id = 'HEAD', unknown = False, if noexclude: extra_exclude = base_exclude = [] - lines = _output_lines(['git-ls-files', '--others'] + base_exclude - + extra_exclude) + lines = _output_lines(['git-ls-files', '--others', '--directory'] + + base_exclude + extra_exclude) cache_files += [('?', line.strip()) for line in lines] # conflicted files @@ -279,9 +272,11 @@ def rev_parse(git_id): def branch_exists(branch): """Existence check for the named branch """ - for line in _output_lines(['git-rev-parse', '--symbolic', '--all']): + for line in _output_lines('git-rev-parse --symbolic --all 2>&1'): if line.strip() == branch: return True + if re.compile('[ |/]'+branch+' ').search(line): + raise GitException, 'Bogus branch: %s' % line return False def create_branch(new_branch, tree_id = None): @@ -299,8 +294,8 @@ def create_branch(new_branch, tree_id = None): if tree_id: switch(tree_id) - if os.path.isfile(os.path.join(get_base_dir(), 'MERGE_HEAD')): - os.remove(os.path.join(get_base_dir(), 'MERGE_HEAD')) + if os.path.isfile(os.path.join(basedir.get(), 'MERGE_HEAD')): + os.remove(os.path.join(basedir.get(), 'MERGE_HEAD')) def switch_branch(name): """Switch to a git branch @@ -319,8 +314,8 @@ def switch_branch(name): __head = tree_id set_head_file(new_head) - if os.path.isfile(os.path.join(get_base_dir(), 'MERGE_HEAD')): - os.remove(os.path.join(get_base_dir(), 'MERGE_HEAD')) + if os.path.isfile(os.path.join(basedir.get(), 'MERGE_HEAD')): + os.remove(os.path.join(basedir.get(), 'MERGE_HEAD')) def delete_branch(name): """Delete a git branch @@ -328,7 +323,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(get_base_dir(), branch_head)) + os.remove(os.path.join(basedir.get(), branch_head)) def rename_branch(from_name, to_name): """Rename a git branch @@ -342,8 +337,8 @@ def rename_branch(from_name, to_name): if get_head_file() == from_name: set_head_file(to_head) - os.rename(os.path.join(get_base_dir(), from_head), \ - os.path.join(get_base_dir(), to_head)) + os.rename(os.path.join(basedir.get(), from_head), \ + os.path.join(basedir.get(), to_head)) def add(names): """Add the files or recursively add the directory contents @@ -485,12 +480,38 @@ def merge(base, head1, head2): 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 - if __run('git-merge-index -o -q gitmergeonefile.py -a') != 0: - raise GitException, 'git-merge-index failed (possible conflicts)' + # check the index for unmerged entries + files = {} + stages_re = re.compile('^([0-7]+) ([0-9a-f]{40}) ([1-3])\t(.*)$', re.S) + + for line in _output('git-ls-files --unmerged --stage -z').split('\0'): + if not line: + continue + + mode, hash, stage, path = stages_re.findall(line)[0] + + if not path in files: + files[path] = {} + files[path]['1'] = ('', '') + files[path]['2'] = ('', '') + files[path]['3'] = ('', '') + + files[path][stage] = (mode, hash) + + # merge the unmerged files + errors = False + for path in files: + stages = files[path] + if gitmergeonefile.merge(stages['1'][1], stages['2'][1], + stages['3'][1], path, stages['1'][0], + stages['2'][0], stages['3'][0]) != 0: + errors = True + + if errors: + raise GitException, 'GIT index merging failed (possible conflicts)' def status(files = None, modified = False, new = False, deleted = False, conflict = False, unknown = False, noexclude = False): @@ -579,13 +600,19 @@ def barefiles(rev1, rev2): return result.rstrip() +def pretty_commit(commit_id = 'HEAD'): + """Return a given commit (log + diff) + """ + return _output(['git-diff-tree', '--cc', '--always', '--pretty', '-r', + commit_id]) + def checkout(files = None, tree_id = None, force = False): """Check out the given or all files """ if not files: files = [] - if tree_id and __run('git-read-tree -m', [tree_id]) != 0: + if tree_id and __run('git-read-tree', [tree_id]) != 0: raise GitException, 'Failed git-read-tree -m %s' % tree_id checkout_cmd = 'git-checkout-index -q -u' @@ -608,14 +635,15 @@ def switch(tree_id): __set_head(tree_id) -def reset(files = None, tree_id = None): +def reset(files = None, tree_id = None, check_out = True): """Revert the tree changes relative to the given tree_id. It removes any local changes """ if not tree_id: tree_id = get_head() - checkout(files, tree_id, True) + if check_out: + checkout(files, tree_id, True) # if the reset refers to the whole tree, switch the HEAD as well if not files: @@ -623,17 +651,17 @@ def reset(files = None, tree_id = None): def pull(repository = 'origin', refspec = None): """Pull changes from the remote repository. At the moment, just - use the 'git pull' command + use the 'git-pull' command """ - # 'git pull' updates the HEAD + # 'git-pull' updates the HEAD __clear_head_cache() args = [repository] if refspec: args.append(refspec) - if __run('git pull', args) != 0: - raise GitException, 'Failed "git pull %s"' % repository + if __run('git-pull', args) != 0: + raise GitException, 'Failed "git-pull %s"' % repository def apply_patch(filename = None, base = None): """Apply a patch onto the current or given index. There must not @@ -667,10 +695,10 @@ def apply_patch(filename = None, base = None): def clone(repository, local_dir): """Clone a remote repository. At the moment, just use the - 'git clone' script + 'git-clone' script """ - if __run('git clone', [repository, local_dir]) != 0: - raise GitException, 'Failed "git clone %s %s"' \ + if __run('git-clone', [repository, local_dir]) != 0: + raise GitException, 'Failed "git-clone %s %s"' \ % (repository, local_dir) def modifying_revs(files, base_rev):