From 30740296e7b211047936ade679e4a15e494ee91c Mon Sep 17 00:00:00 2001 From: David Bremner Date: Mon, 3 Apr 2023 07:22:48 -0300 Subject: [PATCH] CLI/git: add reset command Sometimes merging is not what we want with tags; in particular it tends to keep tags in the local repo that have been removed elsewhere. This commit provides a new reset command; the reset itself is trivial, but the work is to provide a safety check that uses the existing --force and git.safe_fraction machinery. --- notmuch-git.py | 37 +++++++++++++++++++++++++++++++++++-- test/T850-git.sh | 46 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 3 deletions(-) diff --git a/notmuch-git.py b/notmuch-git.py index 97073c80..ee87bec6 100644 --- a/notmuch-git.py +++ b/notmuch-git.py @@ -368,7 +368,7 @@ class CachedIndex: _git(args=['read-tree', self.current_treeish], wait=True) -def check_safe_fraction(status): +def _check_fraction(change): safe = 0.1 conf = _notmuch_config_get ('git.safe_fraction') if conf and conf != '': @@ -379,7 +379,7 @@ def check_safe_fraction(status): _LOG.error('No existing tags with given prefix, stopping.') _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: @@ -387,6 +387,25 @@ def check_safe_fraction(status): _LOG.error('Use --force to override or reconfigure git.safe_fraction.') exit(1) +def check_safe_fraction(status): + + change = len(status['added'])+len(status['deleted']) + _check_fraction(change) + +def check_diff_fraction(): + + # check number of directories (i.e. messages) changed. + change_set = set() + + with _git(args=['diff', '--name-only', 'HEAD', '@{upstream}'], + stdout=_subprocess.PIPE) as git: + for path in git.stdout: + change_set.add(_os.path.dirname(path)) + + change=len(change_set) + _check_fraction(change) + + def commit(treeish='HEAD', message=None, force=False): """ Commit prefix-matching tags from the notmuch database to Git. @@ -619,6 +638,15 @@ def push(repository=None, refspecs=None): _git(args=args, wait=True) +def reset(force=False): + """ + reset the local git branch to match the remote one + """ + if not force: + check_diff_fraction() + + _git(args=["reset","--soft","origin/master"],wait=True) + def status(): """ Show pending updates in notmuch or git repo. @@ -1047,6 +1075,7 @@ if __name__ == '__main__': 'merge', 'pull', 'push', + 'reset', 'status', ]: func = locals()[command] @@ -1141,6 +1170,10 @@ if __name__ == '__main__': 'Refspec (usually a branch name) to push. See ' 'the entry in the OPTIONS section of ' 'git-push(1) for other possibilities.')) + elif command == 'reset': + subparser.add_argument( + '-f', '--force', action='store_true', + help='reset a large fraction of tags.') args = parser.parse_args() diff --git a/test/T850-git.sh b/test/T850-git.sh index 831e4678..2591521b 100755 --- a/test/T850-git.sh +++ b/test/T850-git.sh @@ -213,6 +213,51 @@ cat < EXPECTED EOF test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "reset" +notmuch git -C reset.git -p '' clone remote.git +notmuch git -C reset.git checkout --force +notmuch tag +test4 id:20091117190054.GU3165@dottiness.seas.harvard.edu +notmuch git -C remote.git commit --force +notmuch tag -test4 id:20091117190054.GU3165@dottiness.seas.harvard.edu +notmuch git -C reset.git fetch +notmuch git -C reset.git reset +notmuch git -C reset.git checkout --force +notmuch dump id:20091117190054.GU3165@dottiness.seas.harvard.edu | grep -v '^#' > OUTPUT +cat < EXPECTED ++inbox +signed +test2 +test3 +test4 +unread -- id:20091117190054.GU3165@dottiness.seas.harvard.edu +EOF +test_expect_equal_file EXPECTED OUTPUT + +test_begin_subtest "reset (require force for large change)" +notmuch git -C reset2.git -p '' clone remote.git +notmuch git -C reset2.git checkout --force +notmuch tag +test5 '*' +notmuch git -C remote.git commit --force +notmuch tag -test5 '*' +notmuch git -C reset2.git fetch +test_expect_code 1 "notmuch git -C reset2.git -l debug reset" + +test_begin_subtest "reset (don't require force for large change to one message)" +notmuch git -C reset3.git -p '' clone remote.git +notmuch git -C reset3.git checkout --force +notmuch dump id:20091117190054.GU3165@dottiness.seas.harvard.edu > BEFORE +for tag in $(seq 1 100); do + notmuch tag +$tag id:20091117190054.GU3165@dottiness.seas.harvard.edu +done +notmuch git -C remote.git commit --force +notmuch restore < BEFORE +notmuch git -C reset3.git fetch +test_expect_code 0 "notmuch git -C reset3.git -l debug reset" + +test_begin_subtest "reset --force" +notmuch git -C reset4.git -p '' clone remote.git +notmuch git -C reset4.git checkout --force +notmuch tag +test6 '*' +notmuch git -C remote.git commit --force +notmuch tag -test6 '*' +notmuch git -C reset4.git fetch +test_expect_code 0 "notmuch git -C reset4.git -l debug reset --force" + test_begin_subtest "environment passed through when run as 'notmuch git'" env NOTMUCH_GIT_DIR=foo NOTMUCH_GIT_PREFIX=bar NOTMUCH_PROFILE=default notmuch git -C tags.git -p '' -ldebug status |& \ grep '^env ' | notmuch_dir_sanitize > OUTPUT @@ -311,7 +356,6 @@ prefix = env:: EOF test_expect_equal_file EXPECTED OUTPUT - test_begin_subtest "init, xdg default location" repo=home/.local/share/notmuch/default/git notmuch git -ldebug init |& grep '^repository' | notmuch_dir_sanitize > OUTPUT