Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
Release 0.5
  • Loading branch information
spderosso committed Feb 4, 2014
2 parents b5ac1a1 + 0137b96 commit 880d530
Show file tree
Hide file tree
Showing 13 changed files with 162 additions and 68 deletions.
14 changes: 14 additions & 0 deletions .pylint.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[FORMAT]

indent-string=' '


[MESSAGES CONTROL]

# W0511 = fixme
# C0111 = missing docstring
# C0103 = invalid name
# I0011 = locally disabling
# W0142 = star-args

disable=W0511,C0111,C0103,I0011,W0142
11 changes: 10 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@ Gitpylib's Release Notes
========================


3rd Feb 2014 - 0.5
------------------

* gitpylib now works in python 2.6, 3.2 and 3.3 (in addition to 2.7).
* Added a function to get the names of the branches in a remote.
* remote.add will also do a fetch.
* Bug fixes and code cleanups.


16th Jan 2014 - 0.4.3
---------------------

* More performance improvements in status.
* Added an option to not report tracked unmodified files if desired (which makes
status much faster if the caller doesn't care about knowing which files are
tracked unmodified)
tracked unmodified).


15th Jan 2014 - 0.4.2
Expand Down
19 changes: 9 additions & 10 deletions gitpylib/branch.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import re

import common
from . import common


SUCCESS = 1
Expand Down Expand Up @@ -44,7 +44,7 @@ def create(name, sp='HEAD'):
Returns:
SUCCESS, INVALID_NAME or BRANCH_ALREADY_EXISTS.
"""
ok, _, err = common.git_call('branch {} {}'.format(name, sp))
ok, _, err = common.git_call('branch {0} {1}'.format(name, sp))
if not ok:
if 'is not a valid branch name' in err:
return INVALID_NAME
Expand Down Expand Up @@ -72,7 +72,7 @@ def force_delete(name):

def current():
"""Get the name of the current branch."""
for name, is_current, unused_tracks in status_all():
for name, is_current, _ in status_all():
if is_current:
return name

Expand All @@ -89,11 +89,11 @@ def status(name):
tracks (in the format 'remote_name/remote_branch') or None if it is a local
branch.
"""
out, unused_err = common.safe_git_call('branch --list -vv %s' % name)
out, _ = common.safe_git_call('branch --list -vv %s' % name)
if not out:
return (False, False, None)

unused_name, is_current, tracks = _parse_output(out)
_, is_current, tracks = _parse_output(out)
return (True, is_current, tracks)


Expand All @@ -106,7 +106,7 @@ def status_all():
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, unused_err = common.safe_git_call('branch --list -vv')
out, _ = common.safe_git_call('branch --list -vv')
for b in out.splitlines():
yield _parse_output(b)

Expand All @@ -118,7 +118,7 @@ def set_upstream(branch, upstream_branch):
branch: the branch to set an upstream branch.
upstream_branch: the upstream branch.
"""
ok, out, err = common.git_call(
ok, _, err = common.git_call(
'branch --set-upstream %s %s' % (branch, upstream_branch))

if not ok:
Expand All @@ -145,7 +145,7 @@ def _parse_output(out):
if out.startswith('* (no branch)'):
return ('(no branch)', True, None)

pattern = '([\*| ]) ([^\s]+)[ ]+\w+ (.+)'
pattern = r'([\*| ]) ([^\s]+)[ ]+\w+ (.+)'
result = re.match(pattern, out)
if not result:
raise Exception('Unexpected output %s' % out)
Expand All @@ -158,5 +158,4 @@ def _parse_output(out):
tracks = track_info.split(':')[0]
else:
tracks = track_info

return (result.group(2).strip(), result.group(1) == '*', tracks)
return (result.group(2), result.group(1) == '*', tracks)
9 changes: 7 additions & 2 deletions gitpylib/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@

import os
import subprocess
import sys


# Detect if FS is case-sensitive.
import tempfile

tmp_handle, tmp_path = tempfile.mkstemp()
with tempfile.NamedTemporaryFile() as f:
FS_CASE_SENSITIVE = not os.path.exists(f.name.upper())
with tempfile.NamedTemporaryFile() as f_tmp:
FS_CASE_SENSITIVE = not os.path.exists(f_tmp.name.upper())


def safe_git_call(cmd):
Expand All @@ -29,6 +30,10 @@ def git_call(cmd):
'git %s' % cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
shell=True)
out, err = p.communicate()
# Python 2/3 compatibility.
if sys.version > '3':
out = out.decode('utf-8')
err = err.decode('utf-8')
return p.returncode == 0, out, err


Expand Down
4 changes: 2 additions & 2 deletions gitpylib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
"""Module for dealing with Git config."""


import common
from . import common


def get(var):
ok, out, unused_err = common.git_call('config %s' % var)
ok, out, _ = common.git_call('config %s' % var)
return out.strip() if ok else None
10 changes: 5 additions & 5 deletions gitpylib/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import os.path
import re

import common
from . import common


SUCCESS = 1
Expand Down Expand Up @@ -76,7 +76,7 @@ def show(fp, cp):
"""
fp = common.real_case(fp)

ok, out, unused_err = common.git_call('show %s:"%s"' % (cp, fp))
ok, out, _ = common.git_call('show %s:"%s"' % (cp, fp))

if not ok:
return (FILE_NOT_FOUND_AT_CP, None)
Expand Down Expand Up @@ -125,7 +125,7 @@ def diff(fp):
"""
fp = common.real_case(fp)

out, unused_err = common.safe_git_call('diff -- "%s"' % fp)
out, _ = common.safe_git_call('diff -- "%s"' % fp)
return _process_diff_output(_strip_diff_header(out.splitlines()))


Expand All @@ -140,7 +140,7 @@ def staged_diff(fp):
"""
fp = common.real_case(fp)

out, unused_err = common.safe_git_call('diff --cached -- "%s"' % fp)
out, _ = common.safe_git_call('diff --cached -- "%s"' % fp)
return _process_diff_output(_strip_diff_header(out.splitlines()))


Expand Down Expand Up @@ -186,7 +186,7 @@ def _process_diff_output(diff_out):

for line in diff_out:
# @@ -(start of old),(length of old) +(start of new),(length of new) @@
new_hunk_regex = "^@@ -([0-9]+)[,]?([0-9]*) \+([0-9]+)[,]?([0-9]*) @@"
new_hunk_regex = r'^@@ -([0-9]+)[,]?([0-9]*) \+([0-9]+)[,]?([0-9]*) @@'
new_hunk_info = re.search(new_hunk_regex, line)
if new_hunk_info:
get_info_or_zero = lambda g: 0 if g == '' else int(g)
Expand Down
4 changes: 2 additions & 2 deletions gitpylib/hook.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import os
import subprocess

import common
from . import common


def pre_commit():
Expand All @@ -19,7 +19,7 @@ def pre_commit():

def _hook_call(hook_name):
HookCall = collections.namedtuple('hook_call', ['ok', 'out', 'err'])
hook_path = '{}/hooks/{}'.format(common.git_dir(), hook_name)
hook_path = '{0}/hooks/{1}'.format(common.git_dir(), hook_name)
if not os.path.exists(hook_path):
return HookCall(True, '', '')
p = subprocess.Popen(
Expand Down
96 changes: 79 additions & 17 deletions gitpylib/remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@
"""Module for dealing with Git remotes."""


import common
import collections
import re

from . import common


SUCCESS = 1
REMOTE_NOT_FOUND = 2
Expand All @@ -14,14 +18,22 @@


def add(remote_name, remote_url):
"""Adds the given remote."""
common.safe_git_call('remote add %s %s' % (remote_name, remote_url))
"""Adds the given remote.
Adds the remote mapping and also does a fetch.
def list():
"""Returns a list with all the remotes."""
out, unused_err = common.safe_git_call('remote')
return out.splitlines()
Args:
remote_name: the name of the remote to add.
remote_url: the url of the remote to add.
Returns:
SUCCESS or REMOTE_UNREACHABLE.
"""
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)
return SUCCESS


def show(remote_name):
Expand All @@ -31,26 +43,76 @@ def show(remote_name):
remote_name: the name of the remote to get info from.
Returns:
None if remote doesn't exist or the output of the cmd if otherwise.
a tuple (status, out) where status is one of SUCCESS, REMOTE_NOT_FOUND, or
REMOTE_UNREACHABLE and out is the output of the show command on success.
"""
ok, out, err = common.git_call('remote show %s' % remote_name)
if not ok:
if 'fatal: Could not read from remote repository' in err:
return (REMOTE_UNREACHABLE, None)
else:
raise Exception('Unexpected output %s, err %s' % (out, err))
if not out:
if remote_name not in show_all():
return (REMOTE_NOT_FOUND, None)
return (SUCCESS, out)
return _show(remote_name)


def show_all():
"""Get information of all the remotes."""
out, _ = common.safe_git_call('remote')
return out.splitlines()


RemoteInfo = collections.namedtuple(
'RemoteInfo', ['name', 'fetch', 'push'])


def show_all_v():
"""Get information of all the remotes (verbose)."""
out, _ = common.safe_git_call('remote -v')
# format is remote_name url (fetch/push)
pattern = r'(\w+)\s+(.+)\s+\((\w+)\)'
ret = {}
for r in out.splitlines():
result = re.match(pattern, r)
if not result:
raise Exception('Unexpected output "%s"' % r)
remote_name = result.group(1)
url = result.group(2)
url_type = result.group(3)
if remote_name not in ret:
ret[remote_name] = {}
ret[remote_name][url_type] = url
return [
RemoteInfo(rn, ret[rn]['fetch'], ret[rn]['push']) for rn in ret.keys()]


def rm(remote_name):
common.safe_git_call('remote rm %s' % remote_name)


def head_exist(remote_name, head):
ok, out, unused_err = common.git_call(
ok, out, _ = common.git_call(
'ls-remote --heads %s %s' % (remote_name, head))
if not ok:
return (False, REMOTE_UNREACHABLE)
return (len(out) > 0, REMOTE_BRANCH_NOT_FOUND)


def branches(remote_name):
"""Gets the name of the branches in the given remote."""
out, _ = common.safe_git_call('branch -r')
remote_name_len = len(remote_name)
for line in out.splitlines():
if '->' in line:
continue
line = line.strip()
if line.startswith(remote_name):
yield line[remote_name_len+1:]


# Private functions.


def _show(remote):
ok, out, err = common.git_call('remote show %s' % remote)
if not ok:
if 'fatal: Could not read from remote repository' in err:
return (REMOTE_UNREACHABLE, None)
else:
raise Exception('Unexpected output %s, err %s' % (out, err))
return (SUCCESS, out)
5 changes: 3 additions & 2 deletions gitpylib/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
# Licensed under GNU GPL, version 2.


import common
from . import common


def clone(repo):
common.safe_git_call('clone %s .' % repo)
"""Returns True if the clone succeeded, False if otherwise."""
return common.git_call('clone %s .' % repo)[0]


def init():
Expand Down
6 changes: 3 additions & 3 deletions gitpylib/stash.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"""Module for dealing with Git stashes."""


import common
from . import common

import re

Expand Down Expand Up @@ -57,12 +57,12 @@ def _stash_id(msg):
the stash id of the stash with the given msg or None if no matching stash is
found.
"""
out, unused_err = common.safe_git_call('stash list --grep=": %s"' % msg)
out, _ = common.safe_git_call('stash list --grep=": %s"' % msg)

if not out:
return None

pattern = '(stash@\{.+\}): '
pattern = r'(stash@\{.+\}): '
result = re.match(pattern, out)
if not result:
raise Exception('Unexpected output %s' % out)
Expand Down
Loading

0 comments on commit 880d530

Please sign in to comment.