X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=notmuch-git.py;h=b4253c0dfc4dc739d0bb5b2cd2d06396f9f92e71;hb=b07e121923a4ca00d0ec68ba9eebe8dafb70e13a;hp=9d5c78764d1d8d4c36b329d433927856a97b8249;hpb=8ed68c1bbe8c1b7bb69d7bea1e0c8919bfcb0d0a;p=notmuch diff --git a/notmuch-git.py b/notmuch-git.py index 9d5c7876..b4253c0d 100644 --- a/notmuch-git.py +++ b/notmuch-git.py @@ -40,7 +40,7 @@ from urllib.parse import quote as _quote from urllib.parse import unquote as _unquote import json as _json -_LOG = _logging.getLogger('nmbug') +_LOG = _logging.getLogger('notmuch-git') _LOG.setLevel(_logging.WARNING) _LOG.addHandler(_logging.StreamHandler()) @@ -239,6 +239,16 @@ def _tag_query(prefix=None): prefix = TAG_PREFIX return '(tag (starts-with "{:s}"))'.format(prefix.replace('"','\\\"')) +def count_messages(prefix=None): + "count messages with a given prefix." + (status, stdout, stderr) = _spawn( + args=['notmuch', 'count', '--query=sexp', _tag_query(prefix)], + stdout=_subprocess.PIPE, wait=True) + if status != 0: + _LOG.error("failed to run notmuch config") + sys.exit(1) + return int(stdout.rstrip()) + def get_tags(prefix=None): "Get a list of tags with a given prefix." (status, stdout, stderr) = _spawn( @@ -248,7 +258,7 @@ def get_tags(prefix=None): def archive(treeish='HEAD', args=()): """ - Dump a tar archive of the current nmbug tag set. + Dump a tar archive of the current notmuch-git tag set. Using 'git archive'. @@ -266,13 +276,13 @@ def archive(treeish='HEAD', args=()): def clone(repository): """ - Create a local nmbug repository from a remote source. + Create a local notmuch-git repository from a remote source. This wraps 'git clone', adding some options to avoid creating a working tree while preserving remote-tracking branches and upstreams. """ - with _tempfile.TemporaryDirectory(prefix='nmbug-clone.') as workdir: + with _tempfile.TemporaryDirectory(prefix='notmuch-git-clone.') as workdir: _spawn( args=[ 'git', 'clone', '--no-checkout', '--separate-git-dir', NOTMUCH_GIT_DIR, @@ -357,7 +367,26 @@ class CachedIndex: _git(args=['read-tree', self.current_treeish], wait=True) -def commit(treeish='HEAD', message=None): +def check_safe_fraction(status): + safe = 0.1 + conf = _notmuch_config_get ('git.safe_fraction') + if conf and conf != '': + safe=float(conf) + + total = count_messages (TAG_PREFIX) + if total == 0: + _LOG.error('No existing tags with given prefix, stopping.'.format(safe)) + _LOG.error('Use --force to override.') + exit(1) + change = len(status['added'])+len(status['deleted']) + fraction = change/total + _LOG.debug('total messages {:d}, change: {:d}, fraction: {:f}'.format(total,change,fraction)) + if fraction > safe: + _LOG.error('safe fraction {:f} exceeded, stopping.'.format(safe)) + _LOG.error('Use --force to override or reconfigure git.safe_fraction.') + exit(1) + +def commit(treeish='HEAD', message=None, force=False): """ Commit prefix-matching tags from the notmuch database to Git. """ @@ -368,6 +397,9 @@ def commit(treeish='HEAD', message=None): _LOG.warning('Nothing to commit') return + if not force: + check_safe_fraction (status) + with CachedIndex(NOTMUCH_GIT_DIR, treeish) as index: try: _update_index(status=status) @@ -420,7 +452,7 @@ def fetch(remote=None): def init(remote=None): """ - Create an empty nmbug repository. + Create an empty notmuch-git repository. This wraps 'git init' with a few extra steps to support subsequent status and commit commands. @@ -445,7 +477,7 @@ def init(remote=None): wait=True) -def checkout(): +def checkout(force=None): """ Update the notmuch database from Git. @@ -453,6 +485,10 @@ def checkout(): to Git. """ status = get_status() + + if not force: + check_safe_fraction(status) + with _spawn( args=['notmuch', 'tag', '--batch'], stdin=_subprocess.PIPE) as p: for id, tags in status['added'].items(): @@ -484,9 +520,9 @@ def _insist_committed(): _LOG.error('\n'.join([ 'Uncommitted changes to {prefix}* tags in notmuch', '', - "For a summary of changes, run 'nmbug status'", - "To save your changes, run 'nmbug commit' before merging/pull", - "To discard your changes, run 'nmbug checkout'", + "For a summary of changes, run 'notmuch-git status'", + "To save your changes, run 'notmuch-git commit' before merging/pull", + "To discard your changes, run 'notmuch-git checkout'", ]).format(prefix=TAG_PREFIX)) _sys.exit(1) @@ -508,7 +544,7 @@ def pull(repository=None, refspecs=None): args.append(repository) if refspecs: args.extend(refspecs) - with _tempfile.TemporaryDirectory(prefix='nmbug-pull.') as workdir: + with _tempfile.TemporaryDirectory(prefix='notmuch-git-pull.') as workdir: for command in [ ['reset', '--hard'], args]: @@ -526,7 +562,7 @@ def merge(reference='@{upstream}'): The default reference is '@{upstream}'. """ _insist_committed() - with _tempfile.TemporaryDirectory(prefix='nmbug-merge.') as workdir: + with _tempfile.TemporaryDirectory(prefix='notmuch-git-merge.') as workdir: for command in [ ['reset', '--hard'], ['merge', reference]]: @@ -541,8 +577,8 @@ def log(args=()): """ A simple wrapper for 'git log'. - After running 'nmbug fetch', you can inspect the changes with - 'nmbug log HEAD..@{upstream}'. + After running 'notmuch-git fetch', you can inspect the changes with + 'notmuch-git log HEAD..@{upstream}'. """ # we don't want output trapping here, because we want the pager. args = ['log', '--name-status', '--no-renames'] + list(args) @@ -551,7 +587,7 @@ def log(args=()): def push(repository=None, refspecs=None): - "Push the local nmbug Git state to a remote repository." + "Push the local notmuch-git Git state to a remote repository." if refspecs and not repository: repository = _get_remote() args = ['push'] @@ -574,13 +610,13 @@ def status(): * A - Tag is present in notmuch database, but not committed to nmbug - (equivalently, tag has been deleted in nmbug repo, e.g. by a + Tag is present in notmuch database, but not committed to notmuch-git + (equivalently, tag has been deleted in notmuch-git repo, e.g. by a pull, but not restored to notmuch database). * D - Tag is present in nmbug repo, but not restored to notmuch + Tag is present in notmuch-git repo, but not restored to notmuch database (equivalently, tag has been deleted in notmuch). * U @@ -588,7 +624,7 @@ def status(): Message is unknown (missing from local notmuch database). The second character (if present) represents a difference between - local and upstream branches. Typically 'nmbug fetch' needs to be + local and upstream branches. Typically 'notmuch-git fetch' needs to be run to update this. * a @@ -847,15 +883,15 @@ def _unpack_diff_lines(stream): def _help(parser, command=None): """ - Show help for an nmbug command. + Show help for an notmuch-git command. Because some folks prefer: - $ nmbug help COMMAND + $ notmuch-git help COMMAND to - $ nmbug COMMAND --help + $ notmuch-git COMMAND --help """ if command: parser.parse_args([command, '--help']) @@ -943,6 +979,10 @@ if __name__ == '__main__': help=( "Argument passed through to 'git archive'. Set anything " 'before , see git-archive(1) for details.')) + elif command == 'checkout': + subparser.add_argument( + '-f', '--force', action='store_true', + help='checkout a large fraction of tags.') elif command == 'clone': subparser.add_argument( 'repository', @@ -951,6 +991,9 @@ if __name__ == '__main__': 'URLS section of git-clone(1) for more information on ' 'specifying repositories.')) elif command == 'commit': + subparser.add_argument( + '-f', '--force', action='store_true', + help='commit a large fraction of tags.') subparser.add_argument( 'message', metavar='MESSAGE', default='', nargs='?', help='Text for the commit message.') @@ -1019,7 +1062,9 @@ if __name__ == '__main__': if nmbug_mode: default = _os.path.join('~', '.nmbug') else: - default = xdg_data_path(notmuch_profile) + default = _notmuch_config_get ('git.path') + if default == '': + default = xdg_data_path(notmuch_profile) NOTMUCH_GIT_DIR = _os.path.expanduser(_os.getenv('NOTMUCH_GIT_DIR', default)) @@ -1033,7 +1078,7 @@ if __name__ == '__main__': if nmbug_mode: prefix = 'notmuch::' else: - prefix = '' + prefix = _notmuch_config_get ('git.tag_prefix') TAG_PREFIX = _os.getenv('NOTMUCH_GIT_PREFIX', prefix)