diff --git a/README.md b/README.md index 101f53d..37091ca 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,19 @@ gitpylib ========= +[![PyPI version](https://badge.fury.io/py/gitpylib.svg)]( + http://badge.fury.io/py/gitpylib) + Python library for Git. -Has methods for the most frequently used Git features; abstracts the user of the -library from the burden of having to know which is the correct command to use to -perform some Git operation. +gitpylib is a lightweight wrapper of the `git` command. + +**As of 02/2015 gitpylib has been phased out and merged into +[Gitless](https://github.com/sdg-mit/gitless "Gitless").** + +Install +------- + +Via `pip` (the Python Package Manager): + + $> pip install gitless diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index cc40a4f..b9e623f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -2,6 +2,12 @@ Gitpylib's Release Notes ======================== +10th Feb 2015 - 0.7 +------------------- + +* Bug fixes. + + 25th Mar 2014 - 0.6 ------------------- diff --git a/gitpylib/branch.py b/gitpylib/branch.py index 9aa323f..96bf7cd 100644 --- a/gitpylib/branch.py +++ b/gitpylib/branch.py @@ -4,11 +4,15 @@ """Module for dealing with Git branches.""" +import collections import re from . import common +BranchStatus = collections.namedtuple( + 'BranchStatus', ['name', 'is_current', 'tracks']) + SUCCESS = 1 UNFETCHED_OBJECT = 2 INVALID_NAME = 3 @@ -26,7 +30,7 @@ def checkout(name): Returns: SUCCESS or NONEXISTENT_BRANCH """ - ok, _, _ = common.git_call('checkout %s' % name) + ok, _, _ = common.git_call('checkout {0}'.format(name)) if not ok: return NONEXISTENT_BRANCH return SUCCESS @@ -63,7 +67,7 @@ def force_delete(name): Returns: SUCCESS or NONEXISTENT_BRANCH """ - ok, _, _ = common.git_call('branch -D %s' % name) + ok, _, _ = common.git_call('branch -D {0}'.format(name)) if not ok: return NONEXISTENT_BRANCH return SUCCESS @@ -83,27 +87,26 @@ def status(name): name: the name of the branch to status. Returns: - a tuple (exists, is_current, tracks) where exists and is_current are - boolean values and tracks is a string representing the remote branch it - tracks (in the format 'remote_name/remote_branch') or None if it is a local - branch. + None if the branch doesn't exist or a namedtuple (name, is_current, tracks) + where is_current is a boolean value and tracks is a string representing the + remote branch it tracks (in the format 'remote_name/remote_branch') or None + if it is a local branch. """ - out, _ = common.safe_git_call('branch --list -vv %s' % name) + out, _ = common.safe_git_call('branch --list -vv {0}'.format(name)) if not out: - return (False, False, None) + return None - _, is_current, tracks = _parse_output(out) - return (True, is_current, tracks) + return _parse_output(out) def status_all(): """Get the status of all existing branches. Yields: - tuples of the form (name, is_current, tracks) where is_current is a boolean - value and tracks is a string representing the remote branch it tracks (in - the format 'remote_name/remote_branch') or None if it is a local branch. - name could be equal to '(no branch)' if the user is in no branch. + namedtuples of the form (name, is_current, tracks) where is_current is a + boolean value and tracks is a string representing the remote branch it + tracks (in the format 'remote_name/remote_branch') or None if it is a local + branch. name could be equal to '(no branch)' if the user is in no branch. """ out, _ = common.safe_git_call('branch --list -vv') for b in out.splitlines(): @@ -118,7 +121,7 @@ def set_upstream(branch, upstream_branch): upstream_branch: the upstream branch. """ ok, _, _ = common.git_call( - 'branch --set-upstream %s %s' % (branch, upstream_branch)) + 'branch --set-upstream {0} {1}'.format(branch, upstream_branch)) if not ok: return UNFETCHED_OBJECT @@ -132,7 +135,7 @@ def unset_upstream(branch): Args: branch: the branch to unset its upstream. """ - common.git_call('branch --unset-upstream %s' % branch) + common.git_call('branch --unset-upstream {0}'.format(branch)) return SUCCESS @@ -151,7 +154,7 @@ def _parse_output(out): # the branch followed by the sha1, optionally followed by some remote tracking # info (between brackets) and finally the message of the last commit. if out.startswith('* (no branch)'): - return ('(no branch)', True, None) + return BranchStatus('(no branch)', True, None) pattern = r'([\*| ]) ([^\s]+)[ ]+\w+ (.+)' result = re.match(pattern, out) @@ -166,4 +169,4 @@ def _parse_output(out): tracks = track_info.split(':')[0] else: tracks = track_info - return (result.group(2), result.group(1) == '*', tracks) + return BranchStatus(result.group(2), result.group(1) == '*', tracks) diff --git a/gitpylib/common.py b/gitpylib/common.py index 65167c8..eba4f1a 100644 --- a/gitpylib/common.py +++ b/gitpylib/common.py @@ -37,12 +37,12 @@ def safe_git_call(cmd): ok, out, err = git_call(cmd) if ok: return out, err - raise Exception('%s failed: out is %s, err is %s' % (cmd, out, err)) + raise Exception('{0} failed: out is {1}, err is {2}'.format(cmd, out, err)) def git_call(cmd): p = subprocess.Popen( - shlex.split('git %s' % cmd), + shlex.split('git {0}'.format(cmd)), stdout=subprocess.PIPE, stderr=subprocess.PIPE) out, err = p.communicate() # Python 2/3 compatibility. @@ -76,10 +76,8 @@ def real_case(fp): found = True break if not found: - # TODO(sperezde): fix this hack - # raise Exception("Invalid file %s: the file doesn't exist" % fp) - # Temp hack until I figure out how to deal with filenames with special - # characters. + # TODO(sperezde): fix this hack (deal with filenames with special + # characters). return fp return os.path.join(*ret) @@ -116,17 +114,15 @@ def remove_dups(list, key): list: the list to read from. key: a function that receives an element from list and returns its key. - Returns: - a new list without duplicates. + Yields: + unique elements of the given list """ keys = set() - ret = [] for a in list: k_a = key(a) if k_a not in keys: keys.add(k_a) - ret.append(a) - return ret + yield a def get_all_fps_under_cwd(): @@ -140,3 +136,11 @@ def get_all_fps_under_cwd(): dirnames.remove('.git') for fp in filenames: yield os.path.relpath(os.path.join(dirpath, fp)) + + +def items(dic): + """Py 2/3 compatible way of getting the items of a dictionary.""" + try: + return dic.iteritems() + except AttributeError: + return iter(dic.items()) diff --git a/gitpylib/config.py b/gitpylib/config.py index 671a98a..ee3d6ad 100644 --- a/gitpylib/config.py +++ b/gitpylib/config.py @@ -8,5 +8,5 @@ def get(var): - ok, out, _ = common.git_call('config %s' % var) + ok, out, _ = common.git_call('config {0}'.format(var)) return out.strip() if ok else None diff --git a/gitpylib/file.py b/gitpylib/file.py index ce7e930..3cfc7c7 100644 --- a/gitpylib/file.py +++ b/gitpylib/file.py @@ -37,7 +37,7 @@ def stage(fp): fp = common.real_case(fp) - common.safe_git_call('add "%s"' % fp) + common.safe_git_call('add "{0}"'.format(fp)) return SUCCESS @@ -58,7 +58,7 @@ def unstage(fp): # http://comments.gmane.org/gmane.comp.version-control.git/211242. # So, we need to ignore the return code (unfortunately) and hope that it # works. - common.git_call('reset HEAD "%s"' % fp) + common.git_call('reset HEAD "{0}"'.format(fp)) return SUCCESS @@ -75,12 +75,12 @@ def show(fp, cp): """ fp = common.real_case(fp) - ok, out, _ = common.git_call('show %s:"%s"' % (cp, fp)) + ok, out, _ = common.git_call('show {0}:"{1}"'.format(cp, fp)) if not ok: - return (FILE_NOT_FOUND_AT_CP, None) + return FILE_NOT_FOUND_AT_CP, None - return (SUCCESS, out) + return SUCCESS, out def assume_unchanged(fp): @@ -94,7 +94,7 @@ def assume_unchanged(fp): """ fp = common.real_case(fp) - common.safe_git_call('update-index --assume-unchanged "%s"' % fp) + common.safe_git_call('update-index --assume-unchanged "{0}"'.format(fp)) return SUCCESS @@ -109,7 +109,7 @@ def not_assume_unchanged(fp): """ fp = common.real_case(fp) - common.safe_git_call('update-index --no-assume-unchanged "%s"' % fp) + common.safe_git_call('update-index --no-assume-unchanged "{0}"'.format(fp)) return SUCCESS @@ -137,14 +137,15 @@ def diff(fp, staged=False): fp = common.real_case(fp) st = '--cached' if staged else '' - out, _ = common.safe_git_call('diff %s -- "%s"' % (st, fp)) + out, _ = common.safe_git_call('diff {0} -- "{1}"'.format(st, fp)) if not out: - return ([], 0, 0, 0, None) - stats_out, _ = common.safe_git_call('diff %s --numstat -- "%s"' % (st, fp)) + return [], 0, 0, 0, None + stats_out, _ = common.safe_git_call( + 'diff {0} --numstat -- "{1}"'.format(st, fp)) header, body = _split_diff(out.splitlines()) line, padding = _process_diff_output(body) additions, removals = _process_diff_stats_output(stats_out) - return (line, padding, additions, removals, header) + return line, padding, additions, removals, header # Private functions. @@ -206,4 +207,4 @@ def _process_diff_output(diff_out): def _process_diff_stats_output(diff_stats_out): # format is additions tab removals. m = re.match(r'([0-9]+)\t([0-9]+)', diff_stats_out) - return (int(m.group(1)), int(m.group(2))) + return int(m.group(1)), int(m.group(2)) diff --git a/gitpylib/log.py b/gitpylib/log.py index e4296ec..016442c 100644 --- a/gitpylib/log.py +++ b/gitpylib/log.py @@ -9,7 +9,6 @@ from . import file as git_file -# TODO: add more info like parents, tree, committer, etc. Commit = collections.namedtuple( 'Commit', ['id', 'author', 'msg', 'diffs']) @@ -19,6 +18,7 @@ CommitDiff = collections.namedtuple( 'CommitDiff', ['fp_before', 'fp_after', 'diff']) + def log(include_diffs=False): log_fmt = r'[[%H] [%an] [%ae] [%aD] [%ar]]%n%B' out, _ = common.safe_git_call( diff --git a/gitpylib/remote.py b/gitpylib/remote.py index bc02418..41fd552 100644 --- a/gitpylib/remote.py +++ b/gitpylib/remote.py @@ -30,8 +30,8 @@ def add(remote_name, remote_url): """ if _show(remote_url)[0] == REMOTE_UNREACHABLE: return REMOTE_UNREACHABLE - common.safe_git_call('remote add %s %s' % (remote_name, remote_url)) - common.safe_git_call('fetch %s' % remote_name) + common.safe_git_call('remote add {0} {1}'.format(remote_name, remote_url)) + common.safe_git_call('fetch {0}'.format(remote_name)) return SUCCESS @@ -46,7 +46,7 @@ def show(remote_name): REMOTE_UNREACHABLE and out is the output of the show command on success. """ if remote_name not in show_all(): - return (REMOTE_NOT_FOUND, None) + return REMOTE_NOT_FOUND, None return _show(remote_name) @@ -81,15 +81,15 @@ def show_all_v(): def rm(remote_name): - common.safe_git_call('remote rm %s' % remote_name) + common.safe_git_call('remote rm {0}'.format(remote_name)) def head_exist(remote_name, head): ok, out, _ = common.git_call( - 'ls-remote --heads %s %s' % (remote_name, head)) + 'ls-remote --heads {0} {1}'.format(remote_name, head)) if not ok: - return (False, REMOTE_UNREACHABLE) - return (len(out) > 0, REMOTE_BRANCH_NOT_FOUND) + return False, REMOTE_UNREACHABLE + return len(out) > 0, REMOTE_BRANCH_NOT_FOUND def branches(remote_name): @@ -108,9 +108,9 @@ def branches(remote_name): def _show(remote): - ok, out, err = common.git_call('remote show %s' % remote) + ok, out, err = common.git_call('remote show {0}'.format(remote)) if not ok: if 'fatal: Could not read from remote repository' in err: - return (REMOTE_UNREACHABLE, None) + return REMOTE_UNREACHABLE, None raise common.UnexpectedOutputError('remote', out, err=err) - return (SUCCESS, out) + return SUCCESS, out diff --git a/gitpylib/repo.py b/gitpylib/repo.py index 295db22..ddfc726 100644 --- a/gitpylib/repo.py +++ b/gitpylib/repo.py @@ -7,7 +7,7 @@ def clone(repo): """Returns True if the clone succeeded, False if otherwise.""" - return common.git_call('clone %s .' % repo)[0] + return common.git_call('clone {0} .'.format(repo))[0] def init(): diff --git a/gitpylib/stash.py b/gitpylib/stash.py index 7db0f58..e9dfd9c 100644 --- a/gitpylib/stash.py +++ b/gitpylib/stash.py @@ -17,7 +17,7 @@ def all(msg): Args: msg: the msg for the stash to create. """ - common.safe_git_call('stash save --all -- "%s"' % msg) + common.safe_git_call('stash save --all -- "{0}"'.format(msg)) def pop(msg): @@ -30,7 +30,7 @@ def pop(msg): if not s_id: return - common.safe_git_call('stash pop %s' % s_id) + common.safe_git_call('stash pop {0}'.format(s_id)) def drop(msg): @@ -43,7 +43,7 @@ def drop(msg): if not s_id: return - common.safe_git_call('stash drop %s' % s_id) + common.safe_git_call('stash drop {0}'.format(s_id)) def _stash_id(msg): @@ -56,7 +56,7 @@ def _stash_id(msg): the stash id of the stash with the given msg or None if no matching stash is found. """ - out, _ = common.safe_git_call('stash list --grep=": %s"' % msg) + out, _ = common.safe_git_call('stash list --grep=": {0}"'.format(msg)) if not out: return None diff --git a/gitpylib/status.py b/gitpylib/status.py index 2a770d9..7cd55a3 100644 --- a/gitpylib/status.py +++ b/gitpylib/status.py @@ -7,30 +7,7 @@ import os from . import common - - -SUCCESS = 1 -FILE_NOT_FOUND = 2 - -# Possible status in which a Git file can be in. -# (There are actually many more, but these seem to be the only ones relevant -# to Gitless.) -# TODO(sperezde): just have gitpylib's status return the status code and let -# Gitless figure out the rest by itself. -TRACKED_UNMODIFIED = 3 -TRACKED_MODIFIED = 4 -UNTRACKED = 5 -ASSUME_UNCHANGED = 6 -STAGED = 7 -DELETED = 8 -DELETED_STAGED = 9 # there are staged changes but then the file was deleted. -# the file is marked as assume-unchanged but it was deleted. -DELETED_ASSUME_UNCHANGED = 10 -IN_CONFLICT = 11 -IGNORED = 12 -# the file was a tracked file that was modified after being staged. -MODIFIED_MODIFIED = 13 -ADDED_MODIFIED = 14 # file is a new file that was added and then modified. +from . import config def of_file(fp): @@ -40,148 +17,64 @@ def of_file(fp): fp: the path of the file to status (e.g., 'paper.tex'). Returns: - FILE_NOT_FOUND if the given file doesn't exist or one of the possible - status codes. + None if the given file doesn't exist or one of the possible status codes. """ - fp = common.real_case(fp) - - ok, _, _ = common.git_call('ls-files -tvco --error-unmatch "%s"' % fp) - if not ok: - return FILE_NOT_FOUND - s = _status_porcelain(fp).get(fp, None) - return _status_from_output(s, _is_au_file(fp), fp) - - -def of_repo(include_tracked_unmodified_fps=True): - """Gets the status of the repo relative to the cwd. - - Args: - include_tracked_unmodified_fps: if True, files that are tracked but - unmodified will be also reported. Setting it to False improves performance - significantly if the repo is big. (Defaults to True.) - - Yields: - A pair (status, fp) for each file in the repo. fp is a file path and - status is the status of the file (TRACKED_UNMODIFIED, TRACKED_MODIFIED, - UNTRACKED, ASSUME_UNCHANGED, STAGED, etc -- see above). - """ - status_codes = _status_porcelain(os.getcwd()) - au_fps = set(au_files(relative_to_cwd=True)) - for au_fp in au_fps: - if au_fp not in status_codes: - status_codes[au_fp] = None - if include_tracked_unmodified_fps: - all_fps_under_cwd = common.get_all_fps_under_cwd() - for fp_under_cwd in all_fps_under_cwd: - if fp_under_cwd not in status_codes: - status_codes[fp_under_cwd] = None - # Python 2/3 compatibility. - status_codes_items = [] - try: - status_codes_items = status_codes.iteritems() - except AttributeError: - status_codes_items = status_codes.items() - - for s_fp, s in status_codes_items: - status = _status_from_output(s, s_fp in au_fps, s_fp) - yield (status, s_fp) - - -def au_files(relative_to_cwd=False): - """Gets all assumed unchanged files. - - Args: - relative_to_cwd: if True then only those au files under the cwd are - reported. If False, all au files in the repository are reported. (Defaults - to False.) - """ - out, _ = common.safe_git_call( - 'ls-files -v {0}'.format( - '--full-name "{0}"'.format( - common.repo_dir()) if not relative_to_cwd else '')) - ret = [] - # There could be dups in the output from ls-files if, for example, there are - # files in conflict. - for f_out in common.remove_dups(out.splitlines(), lambda x: x[2:]): - if f_out[0] == 'h': - ret.append(f_out[2:]) - return ret + return next(of(only_paths=[fp]), (None, None))[1] -# Private functions. +def of(only_paths=None, relative_paths=None): + """Status of the repo or of the only_paths given. - -def _is_au_file(fp): - """True if the given fp corresponds to an assume unchanged file. - - Args: - fp: the filepath to check (fp must be a file not a dir). + Returns: + A list of (fp, status) pairs. fp is a file path and status is a two or three + letter code corresponding to the output from status porcelain and ls-files + (see git manual for more info). """ - out, _ = common.safe_git_call( - 'ls-files -v --full-name "{0}"'.format(fp)) - ret = False - if out: - f_out = common.remove_dups(out.splitlines(), lambda x: x[2:]) - if len(f_out) != 1: - raise common.UnexpectedOutputError('ls-files', out) - ret = f_out[0][0] == 'h' - return ret - - -def _status_porcelain(pathspec): - """Executes the status porcelain command with the given pathspec. + if not relative_paths: + c = config.get('status.relativePaths') + relative_paths = c if c else True # git seems to default to true - Ignored and untracked files are reported. + if only_paths: + pathspecs = '"' + '" "'.join(only_paths) + '"' + else: + pathspecs = common.repo_dir() - Returns: - A dict of fp -> status code. All fps are relative to the cwd. - """ - cwd = os.getcwd() + ret = {} repo_dir = common.repo_dir() - def sanitize_fp(fp): - # The more pythonic way would be to use startswith instead of [0] but the - # latter is faster and it makes a difference when the repo is big. - if fp[0] == '"' and fp.endswith('"'): - fp = fp[1:-1] - # The paths outputted by status are relative to the repodir, we need to make - # them relative to the cwd. - return os.path.relpath(os.path.join(repo_dir, fp), cwd) - - out_status, _ = common.safe_git_call( - 'status --porcelain -u --ignored "{0}"'.format(pathspec)) - ret = {} - for f_out_status in out_status.splitlines(): - # Output is in the form . - # is 2 chars long. - ret[sanitize_fp(f_out_status[3:])] = f_out_status[:2] - return ret - - -def _status_from_output(s, is_au, fp): - if not s: - if is_au: - if not os.path.exists(fp): - return DELETED_ASSUME_UNCHANGED - return ASSUME_UNCHANGED - else: - return TRACKED_UNMODIFIED - if s == '??': - return UNTRACKED - elif s == '!!': - return IGNORED - elif s == ' M': - return TRACKED_MODIFIED - elif s == 'A ': - return STAGED - elif s == ' D': - return DELETED - elif s == 'AD': - return DELETED_STAGED - elif s == 'MM': - return MODIFIED_MODIFIED - elif s == 'AM': - return ADDED_MODIFIED - elif s == 'AA' or s == 'M ' or s == 'DD' or 'U' in s: - return IN_CONFLICT - raise Exception('Failed to get status of file {0}, s is "{1}"'.format(fp, s)) + def status_porcelain(): + out, _ = common.safe_git_call( + 'status --porcelain -u --ignored {0}'.format(pathspecs)) + for f_out in out.splitlines(): + # Output is in the form . + # is 2 chars long. + fp, s = f_out[3:], f_out[:2] + if fp.startswith('"'): + fp = fp[1:-1] + if relative_paths: + fp = os.path.relpath(os.path.join(repo_dir, fp)) + ret[fp] = s + + def ls_files(): + out, _ = common.safe_git_call( + 'ls-files -v --full-name -- {0}'.format(pathspecs)) + for f_out in common.remove_dups(out.splitlines(), lambda x: x[2:]): + fp, s = f_out[2:], f_out[0] + if relative_paths: + fp = os.path.relpath(os.path.join(repo_dir, fp)) + if fp in ret: + ret[fp] = ret[fp] + s + else: + ret[fp] = ' ' + s + + status_porcelain() + ls_files() + return common.items(ret) + + +def au_files(): + """Assumed unchanged files.""" + out, _ = common.safe_git_call('ls-files -v --full-name') + for f_out in common.remove_dups(out.splitlines(), lambda x: x[2:]): + if f_out[0] == 'h': + yield f_out[2:] diff --git a/gitpylib/sync.py b/gitpylib/sync.py index ed4ce02..006b160 100644 --- a/gitpylib/sync.py +++ b/gitpylib/sync.py @@ -48,24 +48,23 @@ def merge(src): Args: src: the source branch to pick up changes from. """ - ok, out, err = common.git_call('merge %s' % src) + ok, out, err = common.git_call('merge {0}'.format(src)) return _parse_merge_output(ok, out, err) def _parse_merge_output(ok, out, err): - # print 'out is <%s>, err is <%s>' % (out, err) if not ok: #if out.startswith('Auto-merging'): # conflict? # raise Exception('conflict?') if ('Automatic merge failed; fix conflicts and then commit the result.' in out): - return (CONFLICT, None) + return CONFLICT, None else: - return (LOCAL_CHANGES_WOULD_BE_LOST, err.splitlines()[1:-2]) + return LOCAL_CHANGES_WOULD_BE_LOST, err.splitlines()[1:-2] if out == 'Already up-to-date.\n': - return (NOTHING_TO_MERGE, None) - return (SUCCESS, None) + return NOTHING_TO_MERGE, None + return SUCCESS, None def abort_merge(): @@ -78,7 +77,7 @@ def merge_in_progress(): def rebase(new_base): - ok, out, err = common.git_call('rebase %s' % new_base) + ok, out, err = common.git_call('rebase {0}'.format(new_base)) return _parse_rebase_output(ok, out, err) @@ -87,31 +86,31 @@ def _parse_rebase_output(ok, out, err): if not ok: if 'Please commit or stash them' in err: # TODO(sperezde): add the files whose changes would be lost. - return (LOCAL_CHANGES_WOULD_BE_LOST, None) + return LOCAL_CHANGES_WOULD_BE_LOST, None elif ('The following untracked working tree files would be overwritten' in err): # TODO(sperezde): add the files whose changes would be lost. - return (LOCAL_CHANGES_WOULD_BE_LOST, None) - return (CONFLICT, None) + return LOCAL_CHANGES_WOULD_BE_LOST, None + return CONFLICT, None if re.match(r'Current branch [^\s]+ is up to date.\n', out): - return (NOTHING_TO_REBASE, None) - return (SUCCESS, out) + return NOTHING_TO_REBASE, None + return SUCCESS, out def rebase_continue(): ok, out, _ = common.git_call('rebase --continue') # print 'out is <%s>, err is <%s>' % (out, err) if not ok: - return (CONFLICT, None) - return (SUCCESS, out) + return CONFLICT, None + return SUCCESS, out def skip_rebase_commit(): ok, out, _ = common.git_call('rebase --skip') # print 'out is <%s>, err is <%s>' % (out, err) if not ok: - return (CONFLICT, ['tbd1', 'tbd2']) - return (SUCCESS, out) + return CONFLICT, None + return SUCCESS, out def abort_rebase(): @@ -124,21 +123,22 @@ def rebase_in_progress(): def push(src_branch, dst_remote, dst_branch): _, _, err = common.git_call( - 'push %s %s:%s' % (dst_remote, src_branch, dst_branch)) + 'push {0} {1}:{2}'.format(dst_remote, src_branch, dst_branch)) if err == 'Everything up-to-date\n': - return (NOTHING_TO_PUSH, None) + return NOTHING_TO_PUSH, None elif ('Updates were rejected because a pushed branch tip is behind its remote' in err): - return (PUSH_FAIL, None) + return PUSH_FAIL, None # Not sure why, but git push returns output in stderr. - return (SUCCESS, err) + return SUCCESS, err def pull_rebase(remote, remote_b): - ok, out, err = common.git_call('pull --rebase %s %s' % (remote, remote_b)) + ok, out, err = common.git_call( + 'pull --rebase {0} {1}'.format(remote, remote_b)) return _parse_rebase_output(ok, out, err) def pull_merge(remote, remote_b): - ok, out, err = common.git_call('pull %s %s' % (remote, remote_b)) + ok, out, err = common.git_call('pull {0} {1}'.format(remote, remote_b)) return _parse_merge_output(ok, out, err) diff --git a/setup.py b/setup.py index f75f8f6..22e537a 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name='gitpylib', - version='0.6', + version='0.7', description='A Python library for Git', long_description=open('README.md').read(), author='Santiago Perez De Rosso', @@ -14,7 +14,7 @@ packages=['gitpylib'], license='GPLv2', classifiers=( - 'Development Status :: 2 - Pre-Alpha', + 'Development Status :: 7 - Inactive', 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)', 'Natural Language :: English',