From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Arthur Miller Newsgroups: gmane.emacs.bugs Subject: bug#49980: 28.0.50; [PATCH] Should we have project-save-buffers? Date: Sun, 15 Aug 2021 12:44:57 +0200 Message-ID: References: <87czqlodpl.fsf@gmail.com> <871r70lnyi.fsf@mail.linkov.net> <87y297azuu.fsf@mail.linkov.net> <3ea58870-6b9a-41b9-c06e-d045ff9764b9@yandex.ru> <87h7ftba8p.fsf@mail.linkov.net> <87bl5zl3na.fsf@mail.linkov.net> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="20018"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux) Cc: Giap Tran , 49980@debbugs.gnu.org, Dmitry Gutov To: Juri Linkov Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Sun Aug 15 12:46:25 2021 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1mFDeq-0004wZ-Hs for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 15 Aug 2021 12:46:24 +0200 Original-Received: from localhost ([::1]:50746 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1mFDeo-0002g9-FC for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 15 Aug 2021 06:46:22 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:37026) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1mFDeU-0002fq-JL for bug-gnu-emacs@gnu.org; Sun, 15 Aug 2021 06:46:02 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]:34305) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1mFDeU-0001kB-Bk for bug-gnu-emacs@gnu.org; Sun, 15 Aug 2021 06:46:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1mFDeU-0002CD-58 for bug-gnu-emacs@gnu.org; Sun, 15 Aug 2021 06:46:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Arthur Miller Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 15 Aug 2021 10:46:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 49980 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch fixed Original-Received: via spool by 49980-submit@debbugs.gnu.org id=B49980.16290243098358 (code B ref 49980); Sun, 15 Aug 2021 10:46:02 +0000 Original-Received: (at 49980) by debbugs.gnu.org; 15 Aug 2021 10:45:09 +0000 Original-Received: from localhost ([127.0.0.1]:45847 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1mFDdd-0002Aj-0F for submit@debbugs.gnu.org; Sun, 15 Aug 2021 06:45:09 -0400 Original-Received: from mail-oln040092071068.outbound.protection.outlook.com ([40.92.71.68]:56807 helo=EUR03-DB5-obe.outbound.protection.outlook.com) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1mFDdZ-00029u-8u for 49980@debbugs.gnu.org; Sun, 15 Aug 2021 06:45:08 -0400 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=Lybcy8+/8+n7HBUonzpeqOqzfdgzEw4Oa+qYxVKZbs0IKJJwpVpODFQ917v9crM1M/AerS7k7N34JOjh1PK+kstICH4d8jvRhe3BOPwap2ltupSbIeiFcOPWpYBOdGQRmXRLpZHyiP9Yujuv+KsL+cNucH6G6AyNAzt88rHnPuBhGDzZL75srj/HLQvXwvwvKjqrJIIiKHvEbdsL/6gDBDyQfDTCu0uDuhLe03iQkMIgPH/iQylW4u6pAo6OKgiTcVQfUBMmoQzuFtA2OT7HKESfW5+x3wO5rxcmR6CWD513iQQjoREXZYrte/QEfoNKq+DryolnGGiHGsVBUQhvIg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=IOL8mvtgzoXvRUF2yfh61bDdbx9G2b4jSj1DcVRZc3g=; b=n9mX1ML2v/Jg4WMQsDfq/Uxy14IJr+QeK3+/eqS5pLgMMm7ViYWCo7g3u2MRMqLatb95EWBLiP9YTxdpo7JCmVMumpF9fQKI6SBnSj7pmQu+VgeQcSJ50S0i6KzQr2X4D7YXafMk2OYQbDCcmOHZU3K1QzWY73rn1xFzJMzTzxup04qTbE0uB8xYd70RSpHnNgIRLudiaYFr/VNEyBoyXoMR4jlQRgzcNtbeDKe82Xm9V3l47SCa8SZilGwawVb2JsPE3Eo1DnaHiLZYwhnrIZReYzoYO/TId2L+ij8h68TjIzRhffKznmFcxqT9IyHkuANcYpG9IaWvhVJ4jbj8LA== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=none; dmarc=none; dkim=none; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=live.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=IOL8mvtgzoXvRUF2yfh61bDdbx9G2b4jSj1DcVRZc3g=; b=N74DhLBek2dAjesn4IEqP+vlRQxBSRL9p9KY5wuAGVxIhMeilWd8/qb2AgGY6/r4Yhyw7xRpD1+Kw6Fg0d/+Jam4EdPFqRz/KJ7+uTfDgDH/50AMc1sPX5ufTNSKFAHltVEBo6cc0tXKIrT5Rq8qO6EaBhNd4eNIDOiavcnS+E2sn3jcg3qdBYtepGtEoGv1gEBNM3j+nlRUn1vgx/wO6efdXyv/G/Cn1acIw9NlCRADd3vjxhL3HAa6Y1KTCzXnUUHObfHCE5PPQ9SDbghREXiEhvVCbm0LwegUBXSr7p81joUpB96UyG1iC11VG3mJJtUJ+TjCbwn8tmRt93YW6Q== Original-Received: from DB5EUR03FT033.eop-EUR03.prod.protection.outlook.com (2a01:111:e400:7e0a::53) by DB5EUR03HT043.eop-EUR03.prod.protection.outlook.com (2a01:111:e400:7e0a::352) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4415.16; Sun, 15 Aug 2021 10:44:58 +0000 Original-Received: from AM9PR09MB4977.eurprd09.prod.outlook.com (2a01:111:e400:7e0a::43) by DB5EUR03FT033.mail.protection.outlook.com (2a01:111:e400:7e0a::76) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4415.16 via Frontend Transport; Sun, 15 Aug 2021 10:44:58 +0000 X-IncomingTopHeaderMarker: OriginalChecksum:7D3879EA39CFA3D4E7A6A7D019022C1212B0A626A2DB5CA3944AB4755A78E7A1; UpperCasedChecksum:14B8C5D47F9B2F291107D963146FE19454709E25828BEE83A3FC4AF7487A2130; SizeAsReceived:7894; Count:46 Original-Received: from AM9PR09MB4977.eurprd09.prod.outlook.com ([fe80::6558:f201:6d1a:3f39]) by AM9PR09MB4977.eurprd09.prod.outlook.com ([fe80::6558:f201:6d1a:3f39%2]) with mapi id 15.20.4415.022; Sun, 15 Aug 2021 10:44:58 +0000 In-Reply-To: <87bl5zl3na.fsf@mail.linkov.net> (Juri Linkov's message of "Sun, 15 Aug 2021 11:32:49 +0300") X-TMN: [dUgUqO8smHiGIQuYo7RXiHD4FGyZcA+m] X-ClientProxiedBy: AM5PR0701CA0058.eurprd07.prod.outlook.com (2603:10a6:203:2::20) To AM9PR09MB4977.eurprd09.prod.outlook.com (2603:10a6:20b:304::20) X-Microsoft-Original-Message-ID: <87mtpjc7h2.fsf@live.com> X-MS-Exchange-MessageSentRepresentingType: 1 Original-Received: from pascal.homepc (81.232.177.30) by AM5PR0701CA0058.eurprd07.prod.outlook.com (2603:10a6:203:2::20) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4436.9 via Frontend Transport; Sun, 15 Aug 2021 10:44:58 +0000 X-MS-PublicTrafficType: Email X-IncomingHeaderCount: 46 X-EOPAttributedMessage: 0 X-MS-Office365-Filtering-Correlation-Id: 52768990-1b12-4f63-2bb8-08d95fd9b9ea X-MS-TrafficTypeDiagnostic: DB5EUR03HT043: X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: UWKS9RfWXq3mhj3nPvNrGuzCdTJc0X8qbvirKLIvrruKoaB75Q6w4Ocjs7+8ZevBbcS65XHKm/8c32AFkm5/MB+vehSZIcszQSo/HX/PpXJPwcwfnYGv5oNVi6PZyI8WvZHmsaygAEZZ2QTGev6lnof6VnfRaml4369DJEzymJTfC+ddgW3SHttnpGcK1vBZlaP1mddrzEK9IsVRM764JqJYru0mVog9jPLF88AH6vLJXTw1VhoisIKPhqi2RmJJKHSFlkvkZqUf4BE3GuTkepQaoyQWszHLKbQMKZeL9SQdwp4aMXAdEDiQ/YrPqyYrDnR7VnIC6D91bMpW5Qq+jV/1r/3Fg8OBBhNg2wY2+wNBe7aP82XDTB6m+IuPgjRxxBdj6FFpyRiFbcxLUopB7FBmNRo5WXj9qEQLJRjSdebndn6ay2ZmL4urXquevnXTTpi8c1uNPegjBcDmrwOQaofIdAjH1Fonnn42lEQ7CA4= X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: qlTV/ophoX+5Ye3GgqpIsbS9FSDb2cXC2BXGhtsCdPj2Cp0O2Nq0QPbfsli90sPea6qy70i8bJ++AnHnTaXieIwVZvInZANyXXNPJFf2YmKXnck/TETlmwMJ/rgL9ifupCS4l+jdpo0KcmWkSMjmpw== X-OriginatorOrg: live.com X-MS-Exchange-CrossTenant-Network-Message-Id: 52768990-1b12-4f63-2bb8-08d95fd9b9ea X-MS-Exchange-CrossTenant-OriginalArrivalTime: 15 Aug 2021 10:44:58.4764 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 84df9e7f-e9f6-40af-b435-aaaaaaaaaaaa X-MS-Exchange-CrossTenant-AuthSource: DB5EUR03FT033.eop-EUR03.prod.protection.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: Internet X-MS-Exchange-CrossTenant-RMS-PersistedConsumerOrg: 00000000-0000-0000-0000-000000000000 X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB5EUR03HT043 X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:211880 Archived-At: --=-=-= Content-Type: text/plain Juri Linkov writes: >> (save-some-buffers t 'save-some-buffers-root) >> >> So how does it work? save-some-buffers-root auto detects a git repo >> root via project-find-functions in project.el or something there? I >> havent followed entire call chain. I ask, because I guess this is going >> to be part of Emacs 28 but wonder if you have any plans to port it >> into older versions? > > You can make it backward-compatible with older versions by copying > the contents of the function save-some-buffers-root literally > to your code. This call should work in older versions: Ah, that was was the magic. I was looking into those functions, but wasn't really sure what I should snitch :). > to your code. This call should work in older versions: > > #+begin_src emacs-lisp > (save-some-buffers > t > (let ((root (or (and (project-current) (project-root (project-current))) > default-directory))) > (lambda () (file-in-directory-p default-directory root)))) > #+end_src Thank you! >> Could you guys care to take a look at my little branch switcher? If you >> have time. I could use some review and advice. It is ~200 sloc and most >> of it is helm boiler code, I am really interesting about opinion on >> stash/unstash strategy. I stash everything include untracked >> files. However I am thinking of implementing other strategy, via commit >> and undo commit. > > Thanks, this is a useful package. It uses the right strategy > to stash and unstash changes. This is much better than committing > and undoing commits like we recently discussed in > https://lists.gnu.org/archive/html/emacs-devel/2021-08/msg00346.html I have to admit, I haven't followed so much the discussion, this was just sommething I wished to had for longer time, but never come about to implement. I am not sure if my approach is correct. I think it can mess up things when multiple branches get changed. If there are only two branches invloved, a->b, b->c, there are no problems (I think). Also with multiple branches without changes there should be no problems. But in case of a->b, b->c, ... , c->n, I am not sure, but I believe they would have to be checked out in correct reversed order. Otherwise I think there is possiblity that applied stashes tramp over each others and cause conflicts. I don't know, I haven't had time to test it that far. I had this for a couple of weeks, and works fine for my modest need, but I would appreciate some more input on the code. I have attached it the source in this mail, if anyone is interested to look at it, but there is also a github project with some more info in a readme file at: https://github.com/amno1/helm-git-branch I am thankful for all input, and thanks for all your help Juri! --=-=-= Content-Type: text/plain Content-Disposition: attachment; filename=helm-git-branch.el ;;; helm-git-branch.el --- Switch git branch with Helm interface. -*- lexical-binding: t; -*- ;; Copyright (C) 2021 Arthur Miller ;; Author: Arthur Miller ;; Keywords: ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; 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 . ;;; Commentary: ;; ;;; Code: (require 'helm-ls-git) (defvar helm-source-git-local-branches nil "This source will built at runtime. It can be build explicitly with function `helm-git-build-local-branches-source'.") (defface helm-git-branch-current-clean-face '((t :foreground "#2aa198")) "Files which contain rebase/merge conflicts." :group 'helm-ls-git) (defface helm-git-branch-current-dirty-face '((t :foreground "#dc322f")) "Files which contain rebase/merge conflicts." :group 'helm-ls-git) ;;; Custom (defgroup helm-git-branch nil "Fast git branch switching with Helm." :group 'helm) (defcustom helm-git-branch-changes-action 'stash "The default action upon changing a branch with uncommited changes. If this option is set to 'stash the changes will be automatically stashed. If this option is set to 'commit the changes will automatically commited." :type 'symbol :group 'helm) (defgroup helm-git-branch nil "Fast git branch switching with Helm." :group 'helm) (defcustom helm-git-branch-stash-prefix "helm-git-branch--" "The prefix to prepend to stash names when autostashing a branch." :type 'string :group 'helm) (defcustom helm-git-branch-auto-save-on-change t "" :type 'boolean :group 'helm) (defcustom helm-git-branch-auto-stash-unstaged t "" :type 'boolean :group 'helm) (defcustom helm-git-branch-fuzzy-match nil "Enable fuzzy matching in `helm-git-*-branch'." :group 'helm-branch-git :set (lambda (var val) (set var val) (setq helm-source-git-local-branches nil)) :type 'boolean) (defmacro replace-all (from to &optional buffer) `(with-current-buffer (or ,buffer (current-buffer)) (goto-char (point-min)) (while (search-forward ,from nil t) (replace-match ,to)))) (defun git-make-cmd (git-args &rest cmd-args) (let ((cmd (seq-concatenate 'list '(call-process "git" nil t nil) (split-string git-args) cmd-args))) `(lambda () ,cmd))) (defun git-call (git-args) (funcall (git-make-cmd git-args))) (defmacro with-helm-git (git-cmd &rest body) `(nbutlast (split-string (helm-aif (helm-ls-git-root-dir) (with-helm-default-directory it (with-output-to-string (with-current-buffer standard-output (insert (format "%s" (git-call ,git-cmd))) ,@body (buffer-string))))) "\n" t "[\s\t]*"))) (defvar helm-git-branch-map (let ((map (make-sparse-keymap))) (set-keymap-parent map helm-generic-files-map) map)) (defvar helm-git-branch-buffer-map (let ((map (make-sparse-keymap))) (set-keymap-parent map helm-buffer-map) map)) ;;; Sources (defun helm-git--local-branches () (with-helm-git "branch")) (defun helm-git-branch-local-transformer (candidates _source) (cl-loop with root = (helm-ls-git-root-dir) for i in candidates collect (cond ((string-match "^\\(*\\)\\(.*\\)" i) ; current branch (if (helm-git-branch--dirty-p) (cons (propertize i 'face 'helm-git-branch-current-dirty-face) (expand-file-name (match-string 2 i) root)) (cons (propertize i 'face 'helm-git-branch-current-clean-face) (expand-file-name (match-string 2 i) root)))) (t i)))) (defun helm-git-branch-local-action-transformer (actions _candidate) (when (= 1 (length (helm-marked-candidates))) (helm-make-actions "Checkout branch" (lambda (_candidate) (let ((marked (helm-marked-candidates))) (dolist (f marked) (vc-git-revert f) (helm-aif (get-file-buffer f) (with-current-buffer it (revert-buffer t t))))))) actions)) (defclass helm-git-local-source (helm-source-in-buffer) ((header-name :initform 'helm-ls-git-header-name) (init :initform (lambda () (helm-init-candidates-in-buffer 'global (helm-git--local-branches)))) (keymap :initform 'helm-git-branch-map) (action :initform (helm-make-actions "Git checkout" (lambda (_candidate) (helm-git-branch--checkout _candidate)))) (filtered-candidate-transformer :initform 'helm-git-branch-local-transformer) (action-transformer :initform 'helm-git-branch-local-action-transformer))) (defun helm-git-build-local-branches-source () ;;(and (memq 'helm-source-ls-git-branches helm-ls-git-default-sources) (helm-make-source "Local branches" 'helm-git-local-source :fuzzy-match helm-git-branch-fuzzy-match :group 'helm :keymap helm-git-branch-map)) (defun helm-git-branch--dirty-p () (not (string-blank-p (helm-ls-git-status)))) (defun helm-git-branch--stash () (helm-aif (helm-ls-git-root-dir) (with-helm-default-directory it (with-output-to-string (with-current-buffer standard-output (insert (call-process "git" nil t nil "stash" "save" (when helm-git-branch-auto-stash-unstaged "-u") (concat helm-git-branch-stash-prefix (helm-ls-git--branch))))))))) (defun helm-git-branch--unstash () (helm-aif (helm-ls-git-root-dir) (with-helm-default-directory it (with-output-to-string (with-current-buffer standard-output (insert (call-process "git" nil t nil "stash" "list")) (goto-char (point-min)) (when (search-forward (concat helm-git-branch-stash-prefix (helm-ls-git--branch)) nil t) (goto-char (line-beginning-position)) (search-forward "}" (line-end-position)) (let ((stash (buffer-substring-no-properties (line-beginning-position) (point)))) (call-process "git" nil t nil "stash" "apply" stash) (call-process "git" nil t nil "stash" "drop" stash)))))))) (defun helm-git-branch--checkout (branch) (helm-aif (helm-ls-git-root-dir) (with-helm-default-directory it (with-output-to-string (with-current-buffer standard-output (when helm-git-branch-auto-save-on-change (save-some-buffers t 'save-some-buffers-root)) (when (helm-git-branch--dirty-p) (helm-git-branch--stash)) (insert (call-process "git" nil t nil "checkout" branch)) (helm-git-branch--unstash)))))) ;;; Commands ;;;###autoload (defun helm-git-local-branches (&optional arg) (interactive "p") (let ((helm-ff-default-directory (or helm-ff-default-directory default-directory))) (when (and arg (not (helm-ls-git-root-dir))) (error "Not inside a Git repository")) (setq helm-source-git-local-branches (helm-git-build-local-branches-source)) (helm-set-local-variable 'helm-ls-git--current-branch (helm-ls-git--branch)) (helm :sources helm-source-git-local-branches :ff-transformer-show-only-basename nil :truncate-lines helm-buffers-truncate-lines :buffer "*helm branches*"))) (provide 'helm-git-branch) ;;; helm-git-branch.el ends here --=-=-=--