aboutsummaryrefslogtreecommitdiffstats
path: root/libbe
diff options
context:
space:
mode:
authorW. Trevor King <wking@drexel.edu>2009-07-19 15:24:51 -0400
committerW. Trevor King <wking@drexel.edu>2009-07-19 15:24:51 -0400
commita6d5f2891dc353ebe5d9d8598790a6674c174eec (patch)
treea7828e4bc0b981540eacd7f3c5199a0c9f2ab6a4 /libbe
parentb3ce47285a66a35904e5e50636ce471ecb4ce29d (diff)
downloadbugseverywhere-a6d5f2891dc353ebe5d9d8598790a6674c174eec.tar.gz
Added --allow-empty to "be commit"
Previously many backends would silently add an empty commit. Not very useful. When the new --allow-empty flag and related allow_empty options are false, every versioning backend is guaranteed to raise the EmptyCommit exception in the case of an attempted empty commit.
Diffstat (limited to 'libbe')
-rw-r--r--libbe/arch.py19
-rw-r--r--libbe/bzr.py18
-rw-r--r--libbe/cmdutil.py6
-rw-r--r--libbe/darcs.py29
-rw-r--r--libbe/git.py15
-rw-r--r--libbe/hg.py11
-rw-r--r--libbe/rcs.py48
7 files changed, 100 insertions, 46 deletions
diff --git a/libbe/arch.py b/libbe/arch.py
index 30983e7..2f45aa9 100644
--- a/libbe/arch.py
+++ b/libbe/arch.py
@@ -260,16 +260,17 @@ class Arch(RCS):
else:
status,output,error = \
self._u_invoke_client("get", revision,directory)
- def _rcs_commit(self, commitfile):
+ def _rcs_commit(self, commitfile, allow_empty=False):
+ if allow_empty == False:
+ # arch applies empty commits without complaining, so check first
+ status,output,error = self._u_invoke_client("changes",expect=(0,1))
+ if status == 0:
+ raise rcs.EmptyCommit()
summary,body = self._u_parse_commitfile(commitfile)
- #status,output,error = self._invoke_client("make-log")
- if body == None:
- status,output,error \
- = self._u_invoke_client("commit","--summary",summary)
- else:
- status,output,error \
- = self._u_invoke_client("commit","--summary",summary,
- "--log-message",body)
+ args = ["commit", "--summary", summary]
+ if body != None:
+ args.extend(["--log-message",body])
+ status,output,error = self._u_invoke_client(*args)
revision = None
revline = re.compile("[*] committed (.*)")
match = revline.search(output)
diff --git a/libbe/bzr.py b/libbe/bzr.py
index fcbd6ac..b33292c 100644
--- a/libbe/bzr.py
+++ b/libbe/bzr.py
@@ -71,9 +71,21 @@ class Bzr(RCS):
else:
self._u_invoke_client("branch", "--revision", revision,
".", directory)
- def _rcs_commit(self, commitfile):
- status,output,error = self._u_invoke_client("commit", "--unchanged",
- "--file", commitfile)
+ def _rcs_commit(self, commitfile, allow_empty=False):
+ args = ["commit", "--file", commitfile]
+ if allow_empty == True:
+ args.append("--unchanged")
+ status,output,error = self._u_invoke_client(*args)
+ else:
+ kwargs = {"expect":(0,3)}
+ status,output,error = self._u_invoke_client(*args, **kwargs)
+ if status != 0:
+ strings = ["ERROR: no changes to commit.", # bzr 1.3.1
+ "ERROR: No changes to commit."] # bzr 1.15.1
+ if self._u_any_in_string(strings, error) == True:
+ raise rcs.EmptyCommit()
+ else:
+ raise rcs.CommandError(args, status, error)
revision = None
revline = re.compile("Committed revision (.*)[.]")
match = revline.search(error)
diff --git a/libbe/cmdutil.py b/libbe/cmdutil.py
index bba3e0e..853a75a 100644
--- a/libbe/cmdutil.py
+++ b/libbe/cmdutil.py
@@ -73,8 +73,10 @@ def get_command(command_name):
def execute(cmd, args):
enc = encoding.get_encoding()
cmd = get_command(cmd)
- cmd.execute([a.decode(enc) for a in args])
- return 0
+ ret = cmd.execute([a.decode(enc) for a in args])
+ if ret == None:
+ ret = 0
+ return ret
def help(cmd=None, parser=None):
if cmd != None:
diff --git a/libbe/darcs.py b/libbe/darcs.py
index 1beb45d..e7132c0 100644
--- a/libbe/darcs.py
+++ b/libbe/darcs.py
@@ -131,24 +131,25 @@ class Darcs(RCS):
RCS._rcs_duplicate_repo(self, directory, revision)
else:
self._u_invoke_client("put", "--to-patch", revision, directory)
- def _rcs_commit(self, commitfile):
+ def _rcs_commit(self, commitfile, allow_empty=False):
id = self.get_user_id()
if '@' not in id:
id = "%s <%s@invalid.com>" % (id, id)
- # Darcs doesn't like commitfiles without trailing endlines.
- f = codecs.open(commitfile, 'r', self.encoding)
- contents = f.read()
- f.close()
- if contents[-1] != '\n':
- f = codecs.open(commitfile, 'a', self.encoding)
- f.write('\n')
- f.close()
- status,output,error = self._u_invoke_client('record', '--all',
- '--author', id,
- '--logfile', commitfile)
+ args = ['record', '--all', '--author', id, '--logfile', commitfile]
+ status,output,error = self._u_invoke_client(*args)
+ empty_strings = ["No changes!"]
revision = None
-
- revline = re.compile("Finished recording patch '(.*)'")
+ if self._u_any_in_string(empty_strings, output) == True:
+ if allow_empty == False:
+ raise rcs.EmptyCommit()
+ else: # we need a extra call to get the current revision
+ args = ["changes", "--last=1", "--xml"]
+ status,output,error = self._u_invoke_client(*args)
+ revline = re.compile("[ \t]*<name>(.*)</name>")
+ # note that darcs does _not_ make an empty revision.
+ # this returns the last non-empty revision id...
+ else:
+ revline = re.compile("Finished recording patch '(.*)'")
match = revline.search(output)
assert match != None, output+error
assert len(match.groups()) == 1
diff --git a/libbe/git.py b/libbe/git.py
index 4a91d44..2f9ffa9 100644
--- a/libbe/git.py
+++ b/libbe/git.py
@@ -93,9 +93,18 @@ class Git(RCS):
#self._u_invoke_client("archive", revision, directory) # makes tarball
self._u_invoke_client("clone", "--no-checkout",".",directory)
self._u_invoke_client("checkout", revision, directory=directory)
- def _rcs_commit(self, commitfile):
- status,output,error = self._u_invoke_client('commit', '-a',
- '-F', commitfile)
+ def _rcs_commit(self, commitfile, allow_empty=False):
+ args = ['commit', '--all', '--file', commitfile]
+ if allow_empty == True:
+ args.append("--allow-empty")
+ status,output,error = self._u_invoke_client(*args)
+ else:
+ kwargs = {"expect":(0,1)}
+ status,output,error = self._u_invoke_client(*args, **kwargs)
+ strings = ["nothing to commit",
+ "nothing added to commit"]
+ if self._u_any_in_string(strings, output) == True:
+ raise rcs.EmptyCommit()
revision = None
revline = re.compile("(.*) (.*)[:\]] (.*)")
match = revline.search(output)
diff --git a/libbe/hg.py b/libbe/hg.py
index c301948..a20eeb5 100644
--- a/libbe/hg.py
+++ b/libbe/hg.py
@@ -58,7 +58,7 @@ class Hg(RCS):
def _rcs_add(self, path):
self._u_invoke_client("add", path)
def _rcs_remove(self, path):
- self._u_invoke_client("rm", path)
+ self._u_invoke_client("rm", "--force", path)
def _rcs_update(self, path):
pass
def _rcs_get_file_contents(self, path, revision=None, binary=False):
@@ -73,8 +73,13 @@ class Hg(RCS):
return RCS._rcs_duplicate_repo(self, directory, revision)
else:
self._u_invoke_client("archive", "--rev", revision, directory)
- def _rcs_commit(self, commitfile):
- self._u_invoke_client('commit', '--logfile', commitfile)
+ def _rcs_commit(self, commitfile, allow_empty=False):
+ args = ['commit', '--logfile', commitfile]
+ status,output,error = self._u_invoke_client(*args)
+ if allow_empty == False:
+ strings = ["nothing changed"]
+ if self._u_any_in_string(strings, output) == True:
+ raise rcs.EmptyCommit()
status,output,error = self._u_invoke_client('identify')
revision = None
revline = re.compile("(.*) tip")
diff --git a/libbe/rcs.py b/libbe/rcs.py
index 7138d01..3bf8c9d 100644
--- a/libbe/rcs.py
+++ b/libbe/rcs.py
@@ -61,10 +61,13 @@ def installed_rcs():
class CommandError(Exception):
- def __init__(self, err_str, status):
- Exception.__init__(self, "Command failed (%d): %s" % (status, err_str))
- self.err_str = err_str
+ def __init__(self, command, status, err_str):
+ strerror = ["Command failed (%d):\n %s\n" % (status, err_str),
+ "while executing\n %s" % command]
+ Exception.__init__(self, "\n".join(strerror))
+ self.command = command
self.status = status
+ self.err_str = err_str
class SettingIDnotSupported(NotImplementedError):
pass
@@ -86,6 +89,10 @@ class NoSuchFile(Exception):
path = os.path.abspath(os.path.join(root, pathname))
Exception.__init__(self, "No such file: %s" % path)
+class EmptyCommit(Exception):
+ def __init__(self):
+ Exception.__init__(self, "No changes to commit")
+
def new():
return RCS()
@@ -197,11 +204,14 @@ class RCS(object):
dir specifies a directory to create the duplicate in.
"""
shutil.copytree(self.rootdir, directory, True)
- def _rcs_commit(self, commitfile):
+ def _rcs_commit(self, commitfile, allow_empty=False):
"""
Commit the current working directory, using the contents of
commitfile as the comment. Return the name of the old
revision (or None if commits are not supported).
+
+ If allow_empty == False, raise EmptyCommit if there are no
+ changes to commit.
"""
return None
def installed(self):
@@ -364,22 +374,25 @@ class RCS(object):
shutil.rmtree(self._duplicateBasedir)
self._duplicateBasedir = None
self._duplicateDirname = None
- def commit(self, summary, body=None):
+ def commit(self, summary, body=None, allow_empty=False):
"""
Commit the current working directory, with a commit message
string summary and body. Return the name of the old revision
(or None if versioning is not supported).
+
+ If allow_empty == False (the default), raise EmptyCommit if
+ there are no changes to commit.
"""
- summary = summary.strip()
+ summary = summary.strip()+'\n'
if body is not None:
- summary += '\n\n' + body.strip() + '\n'
+ summary += '\n' + body.strip() + '\n'
descriptor, filename = tempfile.mkstemp()
revision = None
try:
temp_file = os.fdopen(descriptor, 'wb')
temp_file.write(summary)
temp_file.flush()
- revision = self._rcs_commit(filename)
+ revision = self._rcs_commit(filename, allow_empty=allow_empty)
temp_file.close()
finally:
os.remove(filename)
@@ -388,7 +401,20 @@ class RCS(object):
pass
def postcommit(self, directory):
pass
+ def _u_any_in_string(self, list, string):
+ """
+ Return True if any of the strings in list are in string.
+ Otherwise return False.
+ """
+ for list_string in list:
+ if list_string in string:
+ return True
+ return False
def _u_invoke(self, args, stdin=None, expect=(0,), cwd=None):
+ """
+ expect should be a tuple of allowed exit codes. cwd should be
+ the directory from which the command will be executed.
+ """
if cwd == None:
cwd = self.rootdir
if self.verboseInvoke == True:
@@ -401,15 +427,13 @@ class RCS(object):
q = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE,
shell=True, cwd=cwd)
except OSError, e :
- strerror = "%s\nwhile executing %s" % (e.args[1], args)
- raise CommandError(strerror, e.args[0])
+ raise CommandError(args, e.args[0], e)
output, error = q.communicate(input=stdin)
status = q.wait()
if self.verboseInvoke == True:
print >> sys.stderr, "%d\n%s%s" % (status, output, error)
if status not in expect:
- strerror = "%s\nwhile executing %s\n%s" % (args[1], args, error)
- raise CommandError(strerror, status)
+ raise CommandError(args, status, error)
return status, output, error
def _u_invoke_client(self, *args, **kwargs):
directory = kwargs.get('directory',None)