if item.revno == revno:
return item.revision
croak("{0} has no revno {1}".format(self.name, revno))
- def resolve(self, revspec):
+ def resolve(self, ctx):
"Resolve a revision spec that may contain tags into revnos."
- if revspec is None:
- return None
- (start, end) = revspec
- if type(start) == type(""):
- if start in self.symbols:
- start = self.symbols[start]
+ if ctx.is_empty():
+ return ctx
+ if type(ctx.start) == type(""):
+ if ctx.start in self.symbols:
+ ctx.start = self.symbols[ctx.start]
else:
- croak("can't resolve symbol {0} in {1}".format(start,self.name))
- if type(end) == type(""):
- if end in self.symbols:
- end = self.symbols[end]
+ croak("can't resolve symbol {0} in {1}".format(ctx.start,self.name))
+ if type(ctx.end) == type(""):
+ if ctx.end in self.symbols:
+ ctx.end = self.symbols[ctx.end]
else:
- croak("can't resolve symbol {0} in {1}".format(end, self.name))
- if end > len(self):
- croak("{0} has no {1} revision".format(self.name, end))
- return range(start, end+1)
+ croak("can't resolve symbol {0} in {1}".format(ctx.end, self.name))
+ if ctx.end > len(self):
+ croak("{0} has no {1} revision".format(self.name, ctx.end))
+ return ctx
def isbranch(self, symbol):
"Is this a branch symbol?"
return ".0." in symbol
The omission of 'src remove' is a deliberate speed bump.
"""
-def preamble(cmd, args):
- "Common setup code."
- if not os.path.exists(repodir):
- croak("repository subdirectory %s does not exist" % repodir)
- args = list(args)
- revspec = None
- if args:
- if args[0] == "--":
- args.pop(0)
- elif args[0].isdigit():
- revspec = args.pop(0)
- try:
- if "-" in revspec or ".." in revspec:
- try:
- (start, end) = revspec.split("-")
- except ValueError:
+class CommandContext:
+ "Consime a revision specification or range from an argument list"
+ def __init__(self, cmd, args):
+ if not os.path.exists(repodir):
+ croak("repository subdirectory %s does not exist" % repodir)
+ self.start = self.end = None
+ self.args = list(args)
+ revspec = None
+ if self.args:
+ if self.args[0] == "--":
+ self.args.pop(0)
+ elif self.args[0].isdigit():
+ revspec = self.args.pop(0)
+ try:
+ if "-" in revspec or ".." in revspec:
try:
- (start, end) = revspec.split("..")
+ (self.start, self.end) = revspec.split("-")
except ValueError:
- croak("internal error - argument parser is confused")
- try:
- start = int(start)
- except ValueError:
- pass
- try:
- end = int(end)
- except ValueError:
- pass
- else:
- try:
- start = int(revspec)
- except ValueError:
- pass
- end = start
- except ValueError:
- croak("malformed revision spec: %s" % revspec)
- revspec = (start, end)
- if not args:
- try:
- masters = os.listdir(repodir)
- except OSError:
- croak("repo directory %s does not exist" % repodir)
- if len(masters) == 1:
- args += [backend.workfile(master) for master in masters]
- else:
- croak("%s requires at least one file argument" % cmd)
- return(revspec, args)
+ try:
+ (self.start, self.end) = revspec.split("..")
+ except ValueError:
+ croak("internal error - argument parser is confused")
+ try:
+ self.start = int(self.start)
+ except ValueError:
+ pass
+ try:
+ self.end = int(self.end)
+ except ValueError:
+ pass
+ else:
+ try:
+ self.start = int(revspec)
+ except ValueError:
+ pass
+ self.end = self.start
+ except ValueError:
+ croak("malformed revision spec: %s" % revspec)
+ if not self.args:
+ try:
+ masters = os.listdir(repodir)
+ except OSError:
+ croak("repo directory %s does not exist" % repodir)
+ if len(masters) == 1:
+ self.args += [backend.workfile(master) for master in masters]
+ else:
+ croak("%s requires at least one file argument" % cmd)
+ def is_empty(self):
+ "Is the spec empty?"
+ return self.start is None
+ def is_singleton(self):
+ "Is the spec a singleton?"
+ return self.start is not None and self.start == self.end
+ def is_range(self):
+ "Is the spec a range?"
+ return self.start is not None and self.start != self.end
+ def select_all(self, metadata):
+ "Set the range to all revisions."
+ self.start = 1
+ self.end = len(metadata)
+ def __contains__(self, i):
+ "Does the spec contain the given revno?"
+ return i >= self.start and (self.end is None or i <= self.end)
def add_method(*args):
"Register a file into src control."
os.mkdir(repodir)
except OSError:
croak(" %s creation failed, check directory permissions." % repodir)
- (revspec, args) = preamble("add", args)
- if revspec is not None:
+ ctx = CommandContext("add", args)
+ if not ctx.is_empty():
croak("add doesn't take a revision spec")
- for arg in args:
+ for arg in ctx.args:
if not os.path.exists(arg):
croak("I see no '%s' here." % arg)
elif os.path.exists(backend.master(arg)):
croak("%s is already registered." % arg)
else:
backend.add(arg)
- if not quiet and len(args) > 1:
+ if not quiet and len(ctx.args) > 1:
announce("%s -> 0" % arg)
def get_comment(legend, args):
croak("repository subdirectory %s does not exist" % repodir)
args = list(args)
comment = get_comment("commit", args)
- (revspec, args) = preamble("commit", args)
- if revspec is not None:
+ ctx = CommandContext("commit", args)
+ if not ctx.is_empty():
croak("commit doesn't take a revision spec")
- for arg in args:
+ for arg in ctx.args:
if not os.path.exists(arg):
croak("I see no '%s' here." % arg)
elif not os.path.exists(backend.master(arg)):
croak("repository subdirectory %s does not exist" % repodir)
args = list(args)
comment = get_comment("amend", args)
- (revspec, args) = preamble("amend", args)
- for arg in args:
+ ctx = CommandContext("amend", args)
+ if ctx.is_range():
+ croak("amend cannot take a range")
+ for arg in ctx.args:
if not os.path.exists(arg):
croak("I see no '%s' here." % arg)
elif not os.path.exists(backend.master(arg)):
croak("%s is not registered." % arg)
else:
metadata = History(arg)
- revspec = metadata.resolve(revspec)
- if revspec is None:
- revspec = metadata.tip().revno
- elif (revspec[0] != revspec[1]):
- croak("amend cannot take a range")
+ metadata.resolve(ctx)
+ if ctx.is_empty():
+ ctx.start = metadata.tip().revno
if not comment:
comment = edit_comment()
comment = prepare_comment(comment, metadata)
with backend.lifter(arg):
- backend.amend(arg, revspec, comment)
+ backend.amend(arg, metadata.revno_to_rcs(ctx.start), comment)
if not quiet and len(args) > 1:
announce("%s : %d" % (arg, len(metadata)))
def list_method(*args):
"Generate a summary listing of commits, one line per commit."
- (revspec, args) = preamble("list", args)
+ ctx = CommandContext("list", args)
width = screenwidth()
- for arg in args:
+ for arg in ctx.args:
sys.stdout.write("= %s %s\n" % (arg, ((width - len(arg) - 5) * "=")))
metadata = History(arg)
- revspec = metadata.resolve(revspec)
+ metadata.resolve(ctx)
for item in metadata:
- if not revspec or item.revno in revspec:
+ if ctx.is_empty() or item.revno in ctx:
# Must allow enough room for revno and date
summary = item.log.strip()[:width - 34]
if item.selected():
def log_method(*args):
"Report revision logs"
- (revspec, args) = preamble("log", args)
+ ctx = CommandContext("log", args)
width = screenwidth()
- for arg in args:
+ for arg in ctx.args:
sys.stdout.write("= %s %s\n" % (arg, ((width - len(arg) - 5) * "=")))
metadata = History(arg)
- revspec = metadata.resolve(revspec)
+ metadata.resolve(ctx)
for item in metadata:
- if not revspec or item.revno in revspec:
+ if ctx.is_empty() or item.revno in ctx:
sys.stdout.write("%-4d | %s\n%s" % (item.revno, item.date, item.log))
sys.stdout.write(("-" * (width - 1)) + "\n")
def checkout_method(*args):
"Refresh the working copy from the history file."
- (revspec, args) = preamble("checkout", args)
- if revspec and revspec[0] != revspec[1]:
+ ctx = CommandContext("checkout", args)
+ if ctx.is_range():
croak("checkout needs an empty or singleton revision spec")
- for arg in args:
+ for arg in ctx.args:
metadata = History(arg)
- revspec = metadata.resolve(revspec)
- if revspec is None:
- revspec = [len(metadata)]
+ metadata.resolve(ctx)
+ if ctx.is_empty():
+ ctx.start = metadata.tip().revno
revision = ""
- elif len(revspec) != 1:
- croak("checkout cannot take a range")
- elif revspec[0] > len(metadata):
+ elif ctx.start > len(metadata):
croak("%s has only %d revisions" % (arg, len(metadata)))
else:
- revision = metadata[len(metadata) - revspec[0]].revision
+ revision = metadata.revno_to_rcs(ctx.start)
with backend.lifter(arg):
backend.checkout(arg, revision)
# Changes the access and modification times
# Later, src status will see this. Alas,
# there's a tiny race window here.
- os.utime(backend.metadata(arg, None))
+ os.utime(backend.master(arg, None))
if not quiet and len(args) > 1:
- announce("%s <- %d" % (arg, revspec[0]))
+ announce("%s <- %d" % (arg, ctx.start))
def status_method(*args):
"Get status of some or all fles."
def cat_method(*args):
"Dump revision content to standard output."
- (revspec, args) = preamble("cat", args)
- for arg in args:
- master = History(arg)
- revspec = master.resolve(revspec)
- if revspec is None:
- revspec = [master.tip().revno]
+ ctx = CommandContext("cat", args)
+ for arg in ctx.args:
+ metadata = History(arg)
+ metadata.resolve(ctx)
+ if ctx.is_empty():
+ ctx.select_all(metadata)
with backend.lifter(arg):
- for item in master:
- if item.revno in revspec:
+ for item in metadata:
+ if item.revno in ctx:
backend.cat(arg, item.revision)
def diff_method(*args):
"Dump diffs between revisions to standard output."
- (revspec, args) = preamble("diff", args)
+ ctx = CommandContext("diff", args)
diffopts = "-u"
while args and args[0].startswith("-"):
diffopts += " " + args.pop(0)
- for arg in args:
+ for arg in ctx.args:
diffspec1 = diffspec2 = ""
- if revspec is not None:
- master = History(arg)
- revspec = master.resolve(revspec)
- diffspec1 += master.revno_to_rcs(revspec[0])
- if len(revspec) > 1:
- diffspec2 += master.revno_to_rcs(revspec[1])
+ if not ctx.is_empty():
+ metadata = History(arg)
+ metadata.resolve(ctx)
+ diffspec1 += metadata.revno_to_rcs(ctx.start)
+ if ctx.is_range():
+ diffspec2 += metadata.revno_to_rcs(ctx.end)
with backend.lifter(arg):
backend.diff(diffspec1, diffspec2, diffopts, arg)
if not args:
croak("%s deletion requires a name argument" % legend)
name = args.pop(0)
- (revspec, args) = preamble(legend, args)
- if revspec is not None:
+ ctx = CommandContext(legend, args)
+ if not ctx.is_empty():
croak("can't accept a revision-spec when %s deleting." % legend)
- for arg in args:
- master = History(arg)
- if name not in master.symbols:
+ for arg in ctx.args:
+ metadata = History(arg)
+ if name not in metadata.symbols:
croak("%s is not a %s in %s" % (name, legend, arg))
else:
with backend.lifter(arg):
return
if args[0] in ("-l", "list"):
args.pop(0)
- (revspec, args) = preamble(legend, args)
- if revspec is not None:
+ ctx = CommandContext(legend, args)
+ if not ctx.is_empty():
croak("can't accept a revision-spec when %s listing." % legend)
width = screenwidth()
- for arg in args:
- master = History(arg)
+ for arg in ctx.args:
+ metadata = History(arg)
sys.stdout.write("= %s %s\n" % (arg, ((width - len(arg) - 5) * "=")))
if legend == "branch":
sys.stdout.write("%s\n" % metadata.branch)
- for (key, value) in master.symbols.items():
+ for (key, value) in metadata.symbols.items():
if legend == "branch" and key == branch.metadata:
continue
- if master.isbranch(arg) == (legend == "branch"):
- sys.stdout.write("%4d\t%s\n" % (master.rcs_to_revno(value),key))
+ if metadata.isbranch(arg) == (legend == "branch"):
+ sys.stdout.write("%4d\t%s\n" % (metadata.rcs_to_revno(value),key))
return
if args[0] in ("-r", "rename"):
args.pop(0)
newname = args.pop(0)
if newname in metadata.symbols:
croak("cannot rename to existing %s" % legend)
- (revspec, args) = preamble(legend, args)
- if revspec is not None:
+ ctx = CommandContext(legend, args)
+ if not ctx.is_empty():
croak("can't accept a revision-spec when %s renaming." % legend)
# In the case of a branch, we want to change only the tag reference.
backend.tag_set(newname, self.symbols[name], arg)
if not args:
croak("%s setting requires a name argument" % legend)
name = args.pop(0)
- (revspec, args) = preamble(legend, args)
- for arg in args:
+ ctx = CommandContext(legend, args)
+ for arg in ctx.args:
metadata = History(arg)
# Semantics differs between tags and branches here
if legend == "tag":
- revspec = metadata.resolve(revspec)
+ metadata.resolve(ctx)
if name in metadata.symbols:
croak("%s %s already set." % (legend, name))
- if revspec is None:
+ if ctx.is_empty():
revision = metadata.tip().revision
revno = metadata.tip().revno
- elif len(revspec) == 1:
- revision = metadata.revno_to_rcs(revspec[0])
- revno = revspec[0]
+ elif ctx.is_singleton():
+ revision = metadata.revno_to_rcs(ctx.start)
+ revno = ctx.start
else:
croak("can't accept a range when %s setting" % legend)
elif legend == "branch":
- if revspec is not None:
+ if not ctx.is_empty():
croak("cannot accept a revision after a branch name")
if name not in metadata.symbols:
backend.create_branch(metadata.new_branchpoint(metadata_current()))
tag_helper(args, "branch", backend.delete_branch, backend.set_branch)
def filecmd(legend, hook, args):
- (revspec, args) = preamble(legend, args)
- if revspec is not None:
+ ctx = CommandContext(legend, args)
+ if not ctx.is_empty():
croak("%s doesn't take a revision spec" % legend)
elif len(args) != 2:
croak("%s requires exactly two arguments" % legend)
def release_method(*args):
"Release locks."
- (revspec, args) = preamble("release", args)
- if revspec is not None:
+ ctx = CommandContext("release", args)
+ if not ctx.is_empty():
croak("release doesn't take a revision spec")
- for arg in args:
+ for arg in ctx.args:
if not os.path.exists(arg):
croak("I see no '%s' here." % arg)
elif not os.path.exists(backend.master(arg)):
def fast_export_method(*args):
"Dump revision content to standard output."
- (revspec, args) = preamble("cat", args)
- if revspec and len(revspec) > 1:
- croak("checkout needs an empty or singleton revision spec")
+ ctx = CommandContext("cat", args)
mark = 0
if pseudotime:
username = "J. Random Hacker"
username = capture_or_die("git config --get user.name").strip()
useremail = capture_or_die("git config --get user.email").strip()
attribution = "%s <%s>" % (username, useremail)
- for arg in args:
+ for arg in ctx.args:
if not os.path.exists(backend.master(arg)):
croak("%s is not registered" % arg)
executable = os.stat(arg).st_mode & stat.S_IXUSR
else:
perms = "100644"
metadata = History(arg)
- revspec = metadata.resolve(revspec)
- if revspec is None:
- revspec = 1
- revrange = range(revspec, len(metadata)+1)
+ metadata.resolve(ctx)
+ if ctx.is_empty():
+ ctx.select_all(metadata)
with backend.lifter(arg):
markmap = {}
last_commit_mark = 0
# FIXME: Sanitize branch names.
# FIXME: Make this canonical even with tags
branch = arg + "master"
- for revno in revrange:
+ for revno in ctx:
scratchfile = "/tmp/src%dtmp" % os.getpid()
backend.cat(arg, metadata.revno_to_rcs(revno), scratchfile)
size = os.path.getsize(scratchfile)
item = metadata.by_revno(revno)
sys.stdout.write("blob\nmark :%d\ndata %d\n" % (mark, size))
sys.stdout.write(open(scratchfile).read() + "\n")
- if revno == revspec:
- if revspec == 1:
+ if revno == ctx.start:
+ if ctx.start == 1:
sys.stdout.write("reset refs/heads/%s\n" % branch)
else:
sys.stdout.write("from refs/heads/%s^0\n" % branch)