git-mr-reply: initial implementation
authorFrantisek Hrbata <frantisek@hrbata.com>
Wed, 16 Dec 2020 09:58:18 +0000 (10:58 +0100)
committerFrantisek Hrbata <frantisek@hrbata.com>
Wed, 16 Dec 2020 09:58:18 +0000 (10:58 +0100)
Signed-off-by: Frantisek Hrbata <frantisek@hrbata.com>
git-mr-reply [new file with mode: 0755]

diff --git a/git-mr-reply b/git-mr-reply
new file mode 100755 (executable)
index 0000000..4197647
--- /dev/null
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+
+# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
+
+"""
+Copyright © 2020 Frantisek Hrbata <frantisek@hrbata.com>
+This program is free software. It comes without any warranty, to
+the extent permitted by applicable law. You can redistribute it
+and/or modify it under the terms of the Do What The Fuck You Want
+To Public License, Version 2, as published by Sam Hocevar. See
+http://www.wtfpl.net/ for more details.
+"""
+
+import argparse
+import sys
+import mrlib
+from email.utils import formatdate
+import os
+import fcntl
+
+parser = argparse.ArgumentParser(description="Reply to comment, patch or MR.")
+parser.add_argument("rev", nargs="?", metavar="REV",
+        help="Comment or patch revision.")
+parser.add_argument("-m", "--mr", default="HEAD",
+        help="MR revision.")
+parser.add_argument("-w", "--write-revs", metavar="FILE",
+        help="Append comment rev as read comment to FILE (default: $HOME/.gimrr/comment.revs).")
+
+args = parser.parse_args()
+refs = mrlib.load_refs()
+rev_comment = False
+
+if not args.rev:
+    mr = mrlib.verify_mrs([args.mr], refs)[0]
+    rev = mrlib.rev_zero
+    tags = mrlib.load_tags([mr], refs=refs, commits=False)
+    mrtags = mrlib.get_commit_tags(tags, mr, mrlib.rev_zero)
+    an = mrlib.get_tag_value(mrtags, "AN")
+    ae = mrlib.get_tag_value(mrtags, "AE")
+    at = mrlib.get_tag_value(mrtags, "AT")
+    subj = mrlib.get_tag_value(mrtags, "S")
+    body = mrlib.get_tag_value(mrtags, "D").replace("\\n", "\n")
+else:
+    rev = mrlib.verify_rev(args.rev)
+    c2m = mrlib.comments2mrs(refs)
+    if rev in c2m:
+        mr = c2m[rev]
+        rev_comment = True
+    else:
+        p2m = mrlib.patches2mrs(refs)
+        if rev not in p2m:
+            sys.exit("Revision '{}' is not comment nor patch.".format(rev))
+
+        if len(p2m[rev]) > 1:
+            mr = mrlib.verify_mrs([args.mr], refs)[0]
+            if mr not in p2m[rev]:
+                sys.exit("Patch '{}' included in more MRs '{}'. Use -m option.".format(rev, " ".join(p2m[rev])))
+        else:
+            mr = p2m[rev][0]
+
+    rv, out, err = mrlib.run(["git", "show", "--no-patch", "--format=%s%x09%an%x09%ae%x09%at", rev])
+    subj, an, ae, at = out.strip().split("\t")
+    rv, body, err = mrlib.run(["git", "show", "--format=%b", rev])
+
+if not subj.startswith("Re: "):
+    subj = "Re: " + subj
+
+ad = formatdate(int(at))
+qbody = ""
+for line in body.split("\n"):
+    qbody += "> {}\n".format(line)
+
+msg = """To: {an} <{ae}>
+Subject: {s}
+In-Reply-To: <{mr}.{rev}@localhost>
+
+On {ad}, {an} wrote:
+{b}""".format(an=an, ae=ae, s=subj, mr=mr, rev=rev, ad=ad, b=qbody)
+
+msg = mrlib.editor(msg)
+
+rv, out, err = mrlib.run(["git", "mr-sendmail"], stdin=msg)
+
+if not rev_comment:
+    sys.exit()
+
+if args.write_revs:
+    revs_path = args.read_revs
+else:
+    revs_path = os.environ.get('HOME') + "/" + ".gimrr/comment.revs"
+
+fd = open(revs_path, "a+")
+fd.seek(0)
+fcntl.lockf(fd.fileno(), fcntl.LOCK_SH | fcntl.LOCK_NB)
+oldrevs = [r for r in fd.read().split("\n") if r]
+if rev in oldrevs:
+    sys.exit()
+
+try:
+    pos = fd.tell()
+    fd.write("{}\n".format(rev))
+    fd.flush()
+except:
+    fd.truncate(pos)
+    raise