# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program. If not, see http://www.gnu.org/licenses/ .
+# along with this program. If not, see https://www.gnu.org/licenses/ .
"""
Manage notmuch tags with Git
import codecs as _codecs
import collections as _collections
+import functools as _functools
import inspect as _inspect
import locale as _locale
import logging as _logging
__version__ = '0.2'
_LOG = _logging.getLogger('nmbug')
-_LOG.setLevel(_logging.ERROR)
+_LOG.setLevel(_logging.WARNING)
_LOG.addHandler(_logging.StreamHandler())
NMBGIT = _os.path.expanduser(
TAG_PREFIX = _os.getenv('NMBPREFIX', 'notmuch::')
_HEX_ESCAPE_REGEX = _re.compile('%[0-9A-F]{2}')
-_TAG_FILE_REGEX = _re.compile('tags/(?P<id>[^/]*)/(?P<tag>[^/]*)')
+_TAG_DIRECTORY = 'tags/'
+_TAG_FILE_REGEX = _re.compile(_TAG_DIRECTORY + '(?P<id>[^/]*)/(?P<tag>[^/]*)')
# magic hash for Git (git hash-object -t blob /dev/null)
_EMPTYBLOB = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'
See PEP 343 for details on context managers [1].
- [1]: http://legacy.python.org/dev/peps/pep-0343/
+ [1]: https://www.python.org/dev/peps/pep-0343/
"""
def __init__(self, **kwargs):
self.name = _tempfile.mkdtemp(**kwargs)
Xapian uses double-quotes for quoting strings. You can escape
internal quotes by repeating them [1,2,3].
- [1]: http://trac.xapian.org/ticket/128#comment:2
- [2]: http://trac.xapian.org/ticket/128#comment:17
- [3]: http://trac.xapian.org/changeset/13823/svn
+ [1]: https://trac.xapian.org/ticket/128#comment:2
+ [2]: https://trac.xapian.org/ticket/128#comment:17
+ [3]: https://trac.xapian.org/changeset/13823/svn
"""
return '"{0}"'.format(string.replace('"', '""'))
stream.close()
setattr(self._process, name, None)
status = self._process.wait()
- _LOG.debug('collect {args} with status {status}'.format(
- args=self._args, status=status))
+ _LOG.debug(
+ 'collect {args} with status {status} (expected {expect})'.format(
+ args=self._args, status=status, expect=self._expect))
if status not in self._expect:
raise SubprocessError(args=self._args, status=status)
input = input.encode(encoding)
(stdout, stderr) = p.communicate(input=input)
status = p.wait()
- _LOG.debug('collect {args} with status {status}'.format(
- args=args, status=status))
+ _LOG.debug(
+ 'collect {args} with status {status} (expected {expect})'.format(
+ args=args, status=status, expect=expect))
if stdout is not None:
stdout = stdout.decode(encoding)
if stderr is not None:
stderr = stderr.decode(encoding)
- if status:
+ if status not in expect:
raise SubprocessError(
args=args, status=status, stdout=stdout, stderr=stderr)
return (status, stdout, stderr)
'git', 'clone', '--no-checkout', '--separate-git-dir', NMBGIT,
repository, workdir],
wait=True)
- _git(args=['config', '--unset', 'core.worktree'], wait=True)
+ _git(args=['config', '--unset', 'core.worktree'], wait=True, expect=(0, 5))
_git(args=['config', 'core.bare', 'true'], wait=True)
+ _git(args=['branch', 'config', 'origin/config'], wait=True)
+ existing_tags = get_tags()
+ if existing_tags:
+ _LOG.warning(
+ 'Not checking out to avoid clobbering existing tags: {}'.format(
+ ', '.join(existing_tags)))
+ else:
+ checkout()
def _is_committed(status):
'nmbug log HEAD..@{upstream}'.
"""
# we don't want output trapping here, because we want the pager.
- args = ['log', '--name-status'] + list(args)
+ args = ['log', '--name-status', '--no-renames'] + list(args)
with _git(args=args, expect=(0, 1, -13)) as p:
p.wait()
stdin=_subprocess.PIPE,
additional_env={'GIT_INDEX_FILE': path}) as git:
for line in notmuch.stdout:
+ if line.strip().startswith('#'):
+ continue
(tags_string, id) = [_.strip() for _ in line.split(' -- id:')]
tags = [
_unquote(tag[len(prefix):])
for line in stream:
match = _TAG_FILE_REGEX.match(line.strip())
if not match:
- raise ValueError(
- 'Invalid line in diff: {!r}'.format(line.strip()))
+ message = 'non-tag line in diff: {!r}'.format(line.strip())
+ if line.startswith(_TAG_DIRECTORY):
+ raise ValueError(message)
+ _LOG.info(message)
+ continue
id = _unquote(match.group('id'))
tag = _unquote(match.group('tag'))
yield (id, tag)
+def _help(parser, command=None):
+ """
+ Show help for an nmbug command.
+
+ Because some folks prefer:
+
+ $ nmbug help COMMAND
+
+ to
+
+ $ nmbug COMMAND --help
+ """
+ if command:
+ parser.parse_args([command, '--help'])
+ else:
+ parser.parse_args(['--help'])
+
+
if __name__ == '__main__':
import argparse
help='Log verbosity. Defaults to {!r}.'.format(
_logging.getLevelName(_LOG.level).lower()))
+ help = _functools.partial(_help, parser=parser)
+ help.__doc__ = _help.__doc__
subparsers = parser.add_subparsers(
title='commands',
description=(
'clone',
'commit',
'fetch',
+ 'help',
'log',
'merge',
'pull',
'Override the default configured in branch.<name>.remote '
'to fetch from a particular remote repository (e.g. '
"'origin')."))
+ elif command == 'help':
+ subparser.add_argument(
+ 'command', metavar='COMMAND', nargs='?',
+ help='The command to show help for.')
elif command == 'log':
subparser.add_argument(
'args', metavar='ARG', nargs='*',
parser.print_usage()
_sys.exit(1)
- (arg_names, varargs, varkw) = _inspect.getargs(args.func.__code__)
+ if args.func == help:
+ arg_names = ['command']
+ else:
+ (arg_names, varargs, varkw) = _inspect.getargs(args.func.__code__)
kwargs = {key: getattr(args, key) for key in arg_names if key in args}
try:
args.func(**kwargs)