[PATCH] CLI/git: add reset command

Subject: [PATCH] CLI/git: add reset command

Date: Mon, 3 Apr 2023 07:22:48 -0300

To: notmuch@notmuchmail.org

Cc:

From: David Bremner


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 57098aae..80787814 100644
--- a/notmuch-git.py
+++ b/notmuch-git.py
@@ -369,7 +369,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 != '':
@@ -380,7 +380,7 @@ def check_safe_fraction(status):
         _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:
@@ -388,6 +388,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.
@@ -620,6 +639,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.
@@ -1048,6 +1076,7 @@ if __name__ == '__main__':
             'merge',
             'pull',
             'push',
+            'reset',
             'status',
             ]:
         func = locals()[command]
@@ -1142,6 +1171,10 @@ if __name__ == '__main__':
                     'Refspec (usually a branch name) to push.  See '
                     'the <refspec> 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 55cec78a..ae6e7a03 100755
--- a/test/T850-git.sh
+++ b/test/T850-git.sh
@@ -213,6 +213,51 @@ cat <<EOF > 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 <<EOF > 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
@@ -305,7 +350,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
-- 
2.39.2

_______________________________________________
notmuch mailing list -- notmuch@notmuchmail.org
To unsubscribe send an email to notmuch-leave@notmuchmail.org

Thread: