+class RevParseException(Exception):
+ """Revision spec parse error."""
+ pass
+
+def parse_rev(rev):
+ """Parse a revision specification into its
+ patchname@branchname//patch_id parts. If no branch name has a slash
+ in it, also accept / instead of //."""
+ files, dirs = list_files_and_dirs(os.path.join(basedir.get(),
+ 'refs', 'heads'))
+ if len(dirs) != 0:
+ # We have branch names with / in them.
+ branch_chars = r'[^@]'
+ patch_id_mark = r'//'
+ else:
+ # No / in branch names.
+ branch_chars = r'[^@/]'
+ patch_id_mark = r'(/|//)'
+ patch_re = r'(?P<patch>[^@/]+)'
+ branch_re = r'@(?P<branch>%s+)' % branch_chars
+ patch_id_re = r'%s(?P<patch_id>[a-z.]*)' % patch_id_mark
+
+ # Try //patch_id.
+ m = re.match(r'^%s$' % patch_id_re, rev)
+ if m:
+ return None, None, m.group('patch_id')
+
+ # Try path[@branch]//patch_id.
+ m = re.match(r'^%s(%s)?%s$' % (patch_re, branch_re, patch_id_re), rev)
+ if m:
+ return m.group('patch'), m.group('branch'), m.group('patch_id')
+
+ # Try patch[@branch].
+ m = re.match(r'^%s(%s)?$' % (patch_re, branch_re), rev)
+ if m:
+ return m.group('patch'), m.group('branch'), None
+
+ # No, we can't parse that.
+ raise RevParseException
+