From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.ciao.gmane.io!not-for-mail From: Wolfgang Scherer Newsgroups: gmane.emacs.bugs Subject: bug#37189: 25.4.1: vc-hg-ignore implementation is missing Date: Sun, 9 Feb 2020 22:06:02 +0100 Message-ID: <6f6fe037-056c-67cf-58d3-17bce36f8f03@gmx.de> References: <1ba53ae2-42a4-3ab3-d4f2-2ceae565d198@gmx.de> <52917e6f-2f00-25cf-4353-dfb40287d0ea@gmx.de> <83pnkrdpb3.fsf@gnu.org> <679942e8-abe9-b0fc-720d-75a54d8d0b5a@gmx.de> <95da41e8-7a55-a15c-cfa7-d70366f9ee6b@gmx.de> <412195c1-e196-12af-933b-0312f5075847@yandex.ru> <57825d73-27a4-d5f5-8198-a172796a558a@gmx.de> <1ebc6077-9175-65ba-4996-282bb2c8eca5@yandex.ru> <6145d6f6-37a8-7166-731b-57669086b145@gmx.de> <838slmk90j.fsf@gnu.org> <83h806gp2w.fsf@gnu.org> <8336bmg1o9.fsf@gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="------------5734CFC43CA54F90F53A969E" Injection-Info: ciao.gmane.io; posting-host="ciao.gmane.io:159.69.161.202"; logging-data="126435"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Thunderbird/60.9.1 Cc: 37189@debbugs.gnu.org, dgutov@yandex.ru To: Eli Zaretskii Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Sun Feb 09 22:07:38 2020 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 1j0tnm-000WmV-5t for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 09 Feb 2020 22:07:38 +0100 Original-Received: from localhost ([::1]:54442 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1j0tnk-0007hH-Kt for geb-bug-gnu-emacs@m.gmane-mx.org; Sun, 09 Feb 2020 16:07:36 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:42639) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1j0tnF-0007h5-DN for bug-gnu-emacs@gnu.org; Sun, 09 Feb 2020 16:07:08 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1j0tnC-0002dg-0m for bug-gnu-emacs@gnu.org; Sun, 09 Feb 2020 16:07:05 -0500 Original-Received: from debbugs.gnu.org ([209.51.188.43]:47339) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1j0tnB-0002dY-SN for bug-gnu-emacs@gnu.org; Sun, 09 Feb 2020 16:07:01 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1j0tnB-0005Ng-KJ for bug-gnu-emacs@gnu.org; Sun, 09 Feb 2020 16:07:01 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Wolfgang Scherer Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Sun, 09 Feb 2020 21:07:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 37189 X-GNU-PR-Package: emacs Original-Received: via spool by 37189-submit@debbugs.gnu.org id=B37189.158128237620633 (code B ref 37189); Sun, 09 Feb 2020 21:07:01 +0000 Original-Received: (at 37189) by debbugs.gnu.org; 9 Feb 2020 21:06:16 +0000 Original-Received: from localhost ([127.0.0.1]:53312 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1j0tmR-0005Mi-0i for submit@debbugs.gnu.org; Sun, 09 Feb 2020 16:06:16 -0500 Original-Received: from mout.gmx.net ([212.227.17.20]:39795) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1j0tmL-0005MN-Uh for 37189@debbugs.gnu.org; Sun, 09 Feb 2020 16:06:13 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=gmx.net; s=badeba3b8450; t=1581282363; bh=hzUvFtZ7yKr+5OpBnUfSrumnraSJ0/i/IOEQvggRn0M=; h=X-UI-Sender-Class:Subject:To:Cc:References:From:Date:In-Reply-To; b=ZoIV19nCG9dBsIrpiFx6KbwMeBCSfPQZFFN0tqV8BR/Eg3ByHqRXIRL4IZxgWfIqL wIx54PCV78EAkxCjYZx+6divGt1aGEc2XhRs4f62TzWZkfVT8k8NBysKkaUhHBOVLW wJvDBcVqn9kjNqfVyG1FjIaE9DgRL0ixJy3M9hNM= X-UI-Sender-Class: 01bb95c1-4bf8-414a-932a-4f6e2808ef9c Original-Received: from sheckley.simul.de ([87.160.210.52]) by mail.gmx.com (mrgmx104 [212.227.17.168]) with ESMTPSA (Nemesis) id 1MrhQC-1joTfD1IEG-00nhaW; Sun, 09 Feb 2020 22:06:03 +0100 Original-Received: from [127.0.0.1] (sheckley.simul.de [127.0.0.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by sheckley.simul.de (Postfix) with ESMTPSA id 53B7D19431CE; Sun, 9 Feb 2020 22:06:02 +0100 (CET) Openpgp: preference=signencrypt Autocrypt: addr=Wolfgang.Scherer@gmx.de; prefer-encrypt=mutual; keydata= xsDiBEb46IgRBACMHOAb1KNo1Ylk+ebri+4R+bG4tyKlqBlrpv8D9/ZwRdXSGt+0DyCHoaAd 7KW7noHapLe87DunABOjKG4nqTGv+dRiWuUBlp3I4aYRFDVa3Da+XnIYkMHKqhK59VEHQCdp Km42nuLS7TS+n99at9YwzTG6VBdOlBKTlRFngOjVLwCg1RGXJ6X3EjS1FKCQeXziURVpWlkD /2zY6Ayhxi62TS84VjikXrrmjXykAAaAmMVEyKKYb9L5pGlqiZz9g/K9xw1EUoZTYuaufquD v4rAGR58K/3V4CYfJLEeshMWiaXHvMmlxMznlG16/um4MvmR8B3r+cx0nOPK1JBdD2qrkNnF Mw8FB+zouLFB4Gt2IUC5IlOmZ8OQA/4qdU53CItzWsCr9Nux4L0qUlRweSmCnV8xGQ2wP5XI MawIQxxREvSrsYDG8cNnYETMg4iQFfIktwAoxCJvuFAwIB6ZxHGF4FcEZm64CXc2u7CmFLqt rVhXhIfMz9oEYC+HhGczGamn9ofbGTFd2hJEtPcQgWNR4f7+aKknmi2+OM0fV29sZmdhbmcg U2NoZXJlciA8d3NAc3ctYW10LndzPsJhBBMRAgAhBQJYmz3YAhsjBQsJCAcCBhUICQoLAgQW AgMBAh4BAheAAAoJEIUCr3Gr112VZZoAoLTBSTp1qGuNhLdXY04iaWCMYmHCAJ4kHPtQ6nTw kEq9qCHgVgXDaY7wjs7ATQRG+OiIEAQAhi0wjcxvA4tychg2NQuwBIf9LX/46l+74+QbewCn a4a+mw/9s5KY In-Reply-To: <8336bmg1o9.fsf@gnu.org> Content-Language: de-DE X-Provags-ID: V03:K1:Fv5JmQFKIj2ctoqnmny7Y/TbxVs/9CLZxjjW+Bs4Y7PQhWG0+YU EgdrpncZ7jT6P0LUcaQt0W9FFZi7ZQmDJOQA7KC0ywlGfETC3+zXRqAna66vT1JaJqWvu/M C1cFpxmdcXkU7oMNmryhh6sO1+Tx6Y7k8KUdcXWaF3WkZanwA6CviYxHlRm7p2Xq/Lj9DYz OcZS5cxDRJhY6BNcvEMqQ== X-UI-Out-Filterresults: notjunk:1;V03:K0:WfFJA3m59uc=:E922YzmesSiicB+RtGvJAx 11G6uxQW6rolZX3PVnz2V/UXCNpAhB6UGc4EzbHpKPybXUY4bnOFOB2mEVxcD43/v/v2pxtjm CMgGPrxR+8hWbt3kgsIDgQoZ4gqyFEj2pqQ6XiamHqjwiL4PLKWMDBWGRI8LXLW7g7LCxAvOX AJSaWBlG6vMthlBK+IoSxwJaPZ9P2Pc5ghIfW80rMPiIx0x03Hs7k+B2P9fGdQggk6M01sFlr C8q5r//23WGH5iTaq1o2IvbEeVKa+JYmNKICue+JQdn5Wq2OE899M7S1bvtU7qhNcRmCFt0+T rbelwWRNAm2ZL/sgjSpCACgwVE6c96qapkUKMQw1VgZAjy+yO67Lw14+M9dFTv7WAqweSlMk+ E5wEpYAl39n1GqD+7qbkU79q6VJSGOAvJoacpwVmuincljoPQwkk8jxdiU/XhW+uQXazpvBK9 7xIPp3yD6jfkyTmdNaYGq65E+b0sgr4g7qAa68+Qa8Et2Lcdv/ZVAbhvsg164Bnk3YPg+PWJ1 aMgPmdfJUsWxLsvFWZZWGlWdVWC1v1iAEtWJtoQDRfVPJUmIdQC8lNjJoX8YvRUE5NYf7vtth rwrGi3V46wMSgKblFfG79Up/SAkqP2lvbn+sLuAF6J/cejq4oV+gOmWdaIlX5H5Tu3BOxWfsH pe1fBwg9kKgujh6fDR8so28SC1oIP49nL/oTi2GWucmjJYutIj7cWAchFCMZuw5pS6fdUCKos NkVmRvwowZpxYpR/ixfqOC5eRPgb+J6H8bYUPt0oRcRUw1vjHGC3ZU3s4M/HS3kzieS0BI2y X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.51.188.43 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:175845 Archived-At: This is a multi-part message in MIME format. --------------5734CFC43CA54F90F53A969E Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Am 07.02.20 um 10:57 schrieb Eli Zaretskii: >> Cc: dgutov@yandex.ru, 37189@debbugs.gnu.org >> From: Wolfgang Scherer >> Date: Wed, 5 Feb 2020 20:06:15 +0100 >> >> coming to think of it, I realized that there probably is no maintainer = for the vc ignore feature. I.e., there is no use in explaining the design = with many words, since it will not be implemented anyway. > AFAIU, Dmitry oversees the VC development and maintenance. That > includes the issues you raised here. I have finished the implementation of the vc ignore feature. See attached = patch. The standalone vc extension for old Emacsen is available at http://sw-amt.= ws/emacs/doc/_build/html/_static/x-vc-repair.el This obsoletes #37215 vc-cvs-ignore writes absolute filenames and duplicate strings, =2D----- Subject: [PATCH] vc ignore feature repair Complete implementation of ignore feature with proper filename escaping and anchoring for all applicable supported backends: =C2=A0 CVS, SVN, SRC, Bzr, Git, Hg, Mtn. Going back to my day job ... --------------5734CFC43CA54F90F53A969E Content-Type: text/x-patch; name="0001-vc-ignore-feature-repair.patch" Content-Transfer-Encoding: quoted-printable Content-Disposition: attachment; filename="0001-vc-ignore-feature-repair.patch" =46rom 08d888a903796ef65fa0fe733ecfd71e3b367c26 Mon Sep 17 00:00:00 2001 From: Wolfgang Scherer Date: Sun, 9 Feb 2020 21:50:34 +0100 Subject: [PATCH] vc ignore feature repair MIME-Version: 1.0 Content-Type: text/plain; charset=3DUTF-8 Content-Transfer-Encoding: 8bit Complete implementation of ignore feature with proper filename escaping and anchoring for all applicable supported backends: CVS, SVN, SRC, Bzr, Git, Hg, Mtn. * lisp/vc/vc.el: (vc-ignore-param-none, vc-ignore-param-glob) (vc-ignore-param-glob-anchored, vc-ignore-param-regexp) (vc-default-ignore-param, vc-glob-escape) (vc--py-regexp-special-chars, vc-py-regexp-quote): new vc ignore parameters (vc-ignore-pattern, vc-ignore-file): new user interface commands (vc-ignore-fileset): new frontend command (vc-ignore): thin wrapper around =E2=80=98vc-default-ignore=E2=80=99 (vc-default-ignore): thin wrapper around =E2=80=98vc-default-get-ignore-file-and-pattern=E2=80=99 and =E2=80=98vc-default-modify-ignores=E2=80=99 (vc-default-get-ignore-file-and-pattern): workhorse for preparing pattern and file ignore parameters (vc-default-modify-ignores): default ignore ignore file manipulator (vc-file-name-directory, vc-file-relative-name): (vc-no-final-slash, vc-has-final-slash): utilities * lisp/vc/vc-bzr.el: (vc-bzr-ignore-param-regexp) (vc-bzr-ignore-param): new vc ignore parameters * lisp/vc/vc-cvs.el: (vc-cvs-find-ignore-file) (vc-cvs-ignore-param-glob, vc-cvs-ignore-param) (vc-cvs-glob-escape): new vc ignore parameters (vc-cvs-ignore, vc-cvs-append-to-ignore): removed * lisp/vc/vc-git.el: (vc-git-ignore-param): new vc ignore parameters * lisp/vc/vc-hg.el: (vc-hg-ignore-param-regexp) (vc-hg-ignore-param-glob, vc-hg-ignore-param): new vc ignore parameters * lisp/vc/vc-mtn.el: (vc-mtn-ignore-param-regexp) (vc-mtn-ignore-param): new vc ignore parameters * lisp/vc/vc-src.el: (vc-src-find-ignore-file, vc-src-glob-escape) (vc-src-ignore-param-glob, vc-src-ignore-param): new vc ignore parameters * lisp/vc/vc-svn.el: (vc-svn-find-ignore-file) (vc-svn-ignore-param-glob, vc-svn-ignore-param) (vc-svn-modify-ignores): new vc ignore parameters, (vc-svn-ignore): removed * lisp/vc/vc-dir.el: (vc-dir-mode-map) new binding "F" =3D> 'vc-ignore-file new binding "G" =3D> 'vc-ignore-pattern * lisp/vc/vc-hooks.el: (vc-prefix-map) new binding "F" =3D> 'vc-ignore-file new binding "G" =3D> 'vc-ignore-pattern (vc-menu-map): new menu item "Ignore Pattern..." =2D-- lisp/vc/vc-bzr.el | 8 ++ lisp/vc/vc-cvs.el | 38 +++---- lisp/vc/vc-dir.el | 3 +- lisp/vc/vc-git.el | 4 + lisp/vc/vc-hg.el | 23 +++++ lisp/vc/vc-hooks.el | 8 +- lisp/vc/vc-mtn.el | 8 ++ lisp/vc/vc-src.el | 26 +++++ lisp/vc/vc-svn.el | 26 +++-- lisp/vc/vc.el | 278 +++++++++++++++++++++++++++++++++++++++++++++--= ----- 10 files changed, 352 insertions(+), 70 deletions(-) diff --git a/lisp/vc/vc-bzr.el b/lisp/vc/vc-bzr.el index e5d307e..1546aba 100644 =2D-- a/lisp/vc/vc-bzr.el +++ b/lisp/vc/vc-bzr.el @@ -683,6 +683,14 @@ or a superior directory.") (expand-file-name ".bzrignore" (vc-bzr-root file))) +(defvar vc-bzr-ignore-param-regexp + '(:escape: vc-py-regexp-quote :anchor: "RE:^" :trailer: "$" :dir-traile= r: "/.*") + "Ignore parameters for Bzr anchored regular expressions.") + +(defun vc-bzr-ignore-param (&optional _ignore-file) + "Appropriate Bzr ignore parameters for IGNORE-FILE." + vc-bzr-ignore-param-regexp) + (defun vc-bzr-checkout (_file &optional rev) (if rev (error "Operation not supported") ;; Else, there's nothing to do. diff --git a/lisp/vc/vc-cvs.el b/lisp/vc/vc-cvs.el index 16566a8..ae60c6c 100644 =2D-- a/lisp/vc/vc-cvs.el +++ b/lisp/vc/vc-cvs.el @@ -1220,29 +1220,21 @@ is non-nil." "Return the administrative directory of FILE." (vc-find-root file "CVS")) -(defun vc-cvs-ignore (file &optional _directory _remove) - "Ignore FILE under CVS." - (vc-cvs-append-to-ignore (file-name-directory file) file)) - -(defun vc-cvs-append-to-ignore (dir str &optional old-dir) - "In DIR, add STR to the .cvsignore file. -If OLD-DIR is non-nil, then this is a directory that we don't want -to hear about anymore." - (with-current-buffer - (find-file-noselect (expand-file-name ".cvsignore" dir)) - (when (ignore-errors - (and buffer-read-only - (eq 'CVS (vc-backend buffer-file-name)) - (not (vc-editable-p buffer-file-name)))) - ;; CVSREAD=3Don special case - (vc-checkout buffer-file-name t)) - (goto-char (point-max)) - (unless (bolp) (insert "\n")) - (insert str (if old-dir "/\n" "\n")) - ;; FIXME this is a pcvs variable. - (if (bound-and-true-p cvs-sort-ignore-file) - (sort-lines nil (point-min) (point-max))) - (save-buffer))) +(defun vc-cvs-find-ignore-file (file) + "Return the ignore file for FILE." + (expand-file-name ".cvsignore" (if file (file-name-directory file)))) + +(defvar vc-cvs-ignore-param-glob + '(:escape: vc-cvs-glob-escape :anchor: "" :trailer: "" :dir-trailer: "/= ") + "Ignore parameters for CVS partially anchored glob wildcards.") + +(defun vc-cvs-ignore-param (&optional _ignore-file) + "Appropriate CVS ignore parameters for IGNORE-FILE." + vc-cvs-ignore-param-glob) + +(defun vc-cvs-glob-escape (string) + "Escape special glob characters and spaces in STRING." + (replace-regexp-in-string " " "?" (vc-glob-escape string) t)) (provide 'vc-cvs) diff --git a/lisp/vc/vc-dir.el b/lisp/vc/vc-dir.el index 0c29352..0a1deeb 100644 =2D-- a/lisp/vc/vc-dir.el +++ b/lisp/vc/vc-dir.el @@ -302,7 +302,8 @@ See `run-hooks'." (define-key map "Q" 'vc-dir-query-replace-regexp) (define-key map (kbd "M-s a C-s") 'vc-dir-isearch) (define-key map (kbd "M-s a M-C-s") 'vc-dir-isearch-regexp) - (define-key map "G" 'vc-dir-ignore) + (define-key map "F" 'vc-ignore-file) + (define-key map "G" 'vc-ignore-pattern) (let ((branch-map (make-sparse-keymap))) (define-key map "B" branch-map) diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 2caa287..5292f21 100644 =2D-- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -976,6 +976,10 @@ It is based on `log-edit-mode', and has Git-specific = extensions.") (expand-file-name ".gitignore" (vc-git-root file))) +(defun vc-git-ignore-param (&optional _ignore-file) + "Appropriate Git ignore parameters for IGNORE-FILE." + vc-ignore-param-glob-anchored) + (defun vc-git-checkout (file &optional rev) (vc-git-command nil 0 file "checkout" (or rev "HEAD"))) diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el index eac9a6f..3d22ae2 100644 =2D-- a/lisp/vc/vc-hg.el +++ b/lisp/vc/vc-hg.el @@ -1212,6 +1212,29 @@ REV is ignored." (expand-file-name ".hgignore" (vc-hg-root file))) +(defvar vc-hg-ignore-param-regexp + '(:escape: vc-py-regexp-quote :anchor: "^" :trailer: "$" :dir-trailer: = "/") + "Ignore parameters for Hg anchored regular expressions.") + +(defvar vc-hg-ignore-param-glob + '(:escape: vc-glob-escape :anchor: "" :trailer: "" :dir-trailer: "/*") + "Ignore parameters for Hg anchored regular expressions.") + +(defun vc-hg-ignore-param (&optional ignore-file) + "Appropriate Hg ignore parameters for IGNORE-FILE." + (let ((syntax "regexp")) + (if (not ignore-file) + (setq ignore-file (vc-hg-find-ignore-file default-directory))) + (if (file-exists-p ignore-file) + (with-current-buffer (find-file-noselect ignore-file) + (save-match-data + (goto-char (point-max)) + (if (re-search-backward "^ *syntax: *\\(regexp\\|glob\\)$" ni= l t) + (setq syntax (match-string 1)))))) + (if (string=3D syntax "regexp") + vc-hg-ignore-param-regexp + vc-hg-ignore-param-glob))) + ;; Modeled after the similar function in vc-bzr.el (defun vc-hg-checkout (file &optional rev) "Retrieve a revision of FILE. diff --git a/lisp/vc/vc-hooks.el b/lisp/vc/vc-hooks.el index 345a28d..3492dc1 100644 =2D-- a/lisp/vc/vc-hooks.el +++ b/lisp/vc/vc-hooks.el @@ -883,7 +883,8 @@ In the latter case, VC mode is deactivated for this bu= ffer." (define-key map "b" 'vc-switch-backend) (define-key map "d" 'vc-dir) (define-key map "g" 'vc-annotate) - (define-key map "G" 'vc-ignore) + (define-key map "F" 'vc-ignore-file) + (define-key map "G" 'vc-ignore-pattern) (define-key map "h" 'vc-region-history) (define-key map "i" 'vc-register) (define-key map "l" 'vc-print-log) @@ -970,8 +971,11 @@ In the latter case, VC mode is deactivated for this b= uffer." '(menu-item "Register" vc-register :help "Register file set into a version control system")) (bindings--define-key map [vc-ignore] - '(menu-item "Ignore File..." vc-ignore + '(menu-item "Ignore File..." vc-ignore-file :help "Ignore a file under current version control system")) + (bindings--define-key vc-menu-map [vc-ignore-pattern] + '(menu-item "Ignore Pattern..." vc-ignore-pattern + :help "Ignore a pattern under current version control s= ystem")) (bindings--define-key map [vc-dir] '(menu-item "VC Dir" vc-dir :help "Show the VC status of files in a directory")) diff --git a/lisp/vc/vc-mtn.el b/lisp/vc/vc-mtn.el index 092d8b5..af42d3f 100644 =2D-- a/lisp/vc/vc-mtn.el +++ b/lisp/vc/vc-mtn.el @@ -106,6 +106,14 @@ switches." "Return the mtn ignore file that controls FILE." (expand-file-name ".mtnignore" (vc-mtn-root file))) +(defvar vc-mtn-ignore-param-regexp + '(:escape: vc-py-regexp-quote :anchor: "^" :trailer: "$" :dir-trailer: = "/") + "Ignore parameters for Mtn anchored regular expressions.") + +(defun vc-mtn-ignore-param (&optional _ignore-file) + "Appropriate Mtn ignore parameters for IGNORE-FILE." + vc-mtn-ignore-param-regexp) + (defun vc-mtn-registered (file) (let ((root (vc-mtn-root file))) (when root diff --git a/lisp/vc/vc-src.el b/lisp/vc/vc-src.el index db127ee..cd9f032 100644 =2D-- a/lisp/vc/vc-src.el +++ b/lisp/vc/vc-src.el @@ -310,6 +310,32 @@ If LIMIT is non-nil, show no more than this many entr= ies." "Rename file from OLD to NEW using `src mv'." (vc-src-command nil 0 new "mv" old)) +(defun vc-src-find-ignore-file (file) + "Return the ignore file for FILE." + (expand-file-name ".srcignore" (if file (file-name-directory file)))) + +(defun vc-src-glob-escape (string) + "Escape special glob characters in STRING." + (save-match-data + (if (string-match "[?*[]" string) + (mapconcat (lambda (c) + (pcase c + (?? "[?]") + (?* "[*]") + (?\[ "[[]") + (_ (char-to-string c)))) + string "") + string))) +;; (vc-src-glob-escape "full[glo]?\\b*") + +(defvar vc-src-ignore-param-glob + '(:escape: vc-src-glob-escape :anchor: "" :trailer: "" :dir-trailer: ""= ) + "Ignore parameters for SRC unanchored glob wildcards.") + +(defun vc-src-ignore-param (&optional _ignore-file) + "Appropriate SRC ignore parameters for IGNORE-FILE." + vc-src-ignore-param-glob) + (provide 'vc-src) ;;; vc-src.el ends here diff --git a/lisp/vc/vc-svn.el b/lisp/vc/vc-svn.el index d039bf3..9d35b75 100644 =2D-- a/lisp/vc/vc-svn.el +++ b/lisp/vc/vc-svn.el @@ -352,17 +352,25 @@ to the SVN command." (concat "-r" rev)) (vc-switches 'SVN 'checkout)))) -(defun vc-svn-ignore (file &optional directory remove) - "Ignore FILE under Subversion. -FILE is a wildcard specification, either relative to -DIRECTORY or absolute." - (let* ((path (directory-file-name (expand-file-name file directory))) - (directory (file-name-directory path)) - (file (file-name-nondirectory path)) +(defun vc-svn-find-ignore-file (file) + "Return the virtual ignore file for FILE." + (expand-file-name ".svnignore" (if file (file-name-directory file)))) + +(defvar vc-svn-ignore-param-glob + '(:escape: vc-glob-escape :anchor: "" :trailer: "" :dir-trailer: "") + "Ignore parameters for SVN unanchored glob wildcards.") + +(defun vc-svn-ignore-param (&optional _ignore-file) + "Appropriate SVN ignore parameters for IGNORE-FILE." + vc-svn-ignore-param-glob) + +(defun vc-svn-modify-ignores (pattern ignore-file remove) + ;; implements =E2=80=98vc-default-modify-ignores=E2=80=99 for SVN + (let* ((directory (file-name-directory ignore-file)) (ignores (vc-svn-ignore-completion-table directory)) (ignores (if remove - (delete file ignores) - (push file ignores)))) + (delete pattern ignores) + (push pattern ignores)))) (vc-svn-command nil 0 nil nil "propset" "svn:ignore" (mapconcat #'identity ignores "\n") directory))) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index ec252b7..d3e5537 100644 =2D-- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -1392,54 +1392,262 @@ first backend that could register the file is use= d." (let ((vc-handled-backends (list backend))) (call-interactively 'vc-register))) -(defun vc-ignore (file &optional directory remove) - "Ignore FILE under the VCS of DIRECTORY. - -Normally, FILE is a wildcard specification that matches the files -to be ignored. When REMOVE is non-nil, remove FILE from the list -of ignored files. +(defvar vc-ignore-param-none + '(:escape: identity :anchor: "" :trailer: "" :dir-trailer: "") + "Property list of ignore parameters for plain strings. + +All properties are optional. + +Property :escape: is a function that takes a pattern string as parameter +and returns an escaped pattern (default is =E2=80=98identity=E2=80=99). + +Property :anchor: is a string that is prepended to the ignore +pattern (default is an empty string). + +Property :trailer: is a string that is appended to non-directory +ignore patterns (default is an empty string). + +Property :dir-trailer: is a string that is appended to directory +ignore patterns (default is an empty string).") + +(defvar vc-ignore-param-glob + '(:escape: vc-glob-escape :anchor: "" :trailer: "" :dir-trailer: "") + "Ignore parameters for unanchored glob wildcards.") + +(defvar vc-ignore-param-glob-anchored + '(:escape: vc-glob-escape :anchor: "/" :trailer: "" :dir-trailer: "/") + "Ignore parameters for anchored glob wildcards.") + +(defvar vc-ignore-param-regexp + '(:escape: regexp-quote :anchor: "^" :trailer: "$" :dir-trailer: "/") + "Ignore parameters for anchored regular expressions.") + +(defun vc-default-ignore-param (_backend &optional _ignore-file) + "Default ignore parameters for IGNORE-FILE." + vc-ignore-param-glob) + +(defun vc-glob-escape (string) + "Escape special glob characters in STRING." + (save-match-data + (if (string-match "[\\?*[]" string) + (mapconcat (lambda (c) + (pcase c + (?\\ "\\\\") + (?? "\\?") + (?* "\\*") + (?\[ "\\[") + (_ (char-to-string c)))) + string "") + string))) + +(defvar vc--py-regexp-special-chars + (mapcar + (function + (lambda (c) + (cons c (concat "\\" (char-to-string c))))) + (append "()[]{}?*+-|^$\\.&~# \t\n\r\v\f" nil)) + "Characters that have special meaning in Python regular expressions.") + +(defun vc-py-regexp-quote (string) + "Python regexp to match exactly STRING and nothing else. +Ported from Python v3.7" + (mapconcat + (function + (lambda (c) + (or (cdr (assq c vc--py-regexp-special-chars)) + (char-to-string c)))) + string "")) + +(defun vc-ignore-pattern (pattern &optional directory remove) + "Ignore PATTERN under VCS of DIRECTORY. DIRECTORY defaults to `default-directory' and is used to determine the responsible VC backend. -When called interactively, prompt for a FILE to ignore, unless a -prefix argument is given, in which case prompt for a file FILE to -remove from the list of ignored files." +PATTERN is an expression following the rules of the backend +pattern syntax, matching the files to be ignored. When REMOVE is +non-nil, remove PATTERN from the list of ignored files. + +When called interactively, prompt for a PATTERN to ignore, unless +a prefix argument is given, in which case prompt for a PATTERN to +remove from the list of ignored files offering currently defined +patterns for completion." (interactive (list (if (not current-prefix-arg) - (read-file-name "File to ignore: ") + (read-string "Pattern to ignore: ") (completing-read - "File to remove: " + "Pattern to remove: " (vc-call-backend (or (vc-responsible-backend default-directory) (error "Unknown backend")) 'ignore-completion-table default-directory))) nil current-prefix-arg)) - (let* ((directory (or directory default-directory)) - (backend (or (vc-responsible-backend default-directory) - (error "Unknown backend")))) - (vc-call-backend backend 'ignore file directory remove))) - -(defun vc-default-ignore (backend file &optional directory remove) - "Ignore FILE under the VCS of DIRECTORY (default is `default-directory'= ). -FILE is a wildcard specification, either relative to -DIRECTORY or absolute. -When called from Lisp code, if DIRECTORY is non-nil, the -repository to use will be deduced by DIRECTORY; if REMOVE is -non-nil, remove FILE from ignored files. -Argument BACKEND is the backend you are using." - (let ((ignore - (vc-call-backend backend 'find-ignore-file (or directory default-direct= ory))) - file-path root-dir pattern) - (setq file-path (expand-file-name file directory)) - (setq root-dir (file-name-directory ignore)) - (when (not (string=3D (substring file-path 0 (length root-dir)) root-= dir)) - (error "Ignore spec %s is not below project root %s" file-path root= -dir)) - (setq pattern (substring file-path (length root-dir))) - (if remove - (vc--remove-regexp (concat "^" (regexp-quote pattern ) "\\(\n\\|$\\)") i= gnore) - (vc--add-line pattern ignore)))) + (vc-ignore pattern directory remove t)) + +(defun vc-ignore-file (file &optional directory remove) + "Ignore FILE under VCS of DIRECTORY. + +DIRECTORY defaults to `default-directory' and is used to +determine the responsible VC backend. + +If FILE is nil, =E2=80=98vc-ignore-fileset=E2=80=99 is called. + +Otherwise, FILE is a file path that must be escaped and anchored. +The directory name of FILE expanded against DIRECTORY is used to +determine the ignore file. The effective pattern consists of the +file path relative to the directory of the ignore file, properly +escaped and anchored by the VC backend. + +The effective pattern is added to the list of ignored files, +unless REMOVE is non-nil, in which case it is removed. + +When called interactively and the mode is neither =E2=80=98vc-dir-mode=E2= =80=99 +nor =E2=80=98dired-mode=E2=80=99, prompt for a FILE to ignore, unless a p= refix +argument is given, in which case prompt for a FILE to remove from +the list of ignored files." + (interactive + (list + (unless (or (derived-mode-p 'vc-dir-mode) (derived-mode-p 'dired-mode= )) + (read-file-name + (concat "File to " + (if (not current-prefix-arg) "ignore" "remove") ": "))) + nil current-prefix-arg)) + (if file + (vc-ignore file directory remove nil) + (vc-ignore-fileset nil remove))) + +(defun vc-ignore-fileset (&optional vc-fileset remove) + "Ignore file set under a version control system.. + +If VC-FILESET is not given, it is deduced with +=E2=80=98vc-deduce-fileset=E2=80=99. + +When REMOVE is non-nil (prefix arg, if interactive), remove the +files from the list of ignored files." + (interactive (list nil current-prefix-arg)) + (let* ((fileset-arg (or vc-fileset (vc-deduce-fileset t t))) + (backend (car fileset-arg)) + (files (delq nil (nth 1 fileset-arg)))) + (when files + (message "Ignoring %s... " files) + (mapc + (lambda (file) + (vc-call-backend backend 'ignore file nil remove nil) + (vc-dir-resynch-file file)) + files)) + (when (derived-mode-p 'vc-dir-mode) + (vc-dir-move-to-goal-column)) + (when files (message "Ignoring %s... done" files)))) + +(defun vc-ignore (file-or-pattern &optional directory remove as-is) + "Ignore FILE-OR-PATTERN under VCS of DIRECTORY. + +DIRECTORY defaults to `default-directory' and is used to +determine the responsible VC backend. + +When REMOVE is non-nil, remove FILE-OR-PATTERN from the list of +ignored files. + +If AS-IS is nil, FILE-OR-PATTERN is considered a file path that +must be escaped and anchored. The directory name of +FILE-OR-PATTERN expanded against DIRECTORY is used to determine +the ignore file. The effective pattern consists of the file path +relative to the directory of the ignore file, properly escaped +and anchored by the VC backend. + +If AS-IS is non-nil, FILE-OR-PATTERN is considered a pattern that +should not be modified. DIRECTORY is used to determine the +ignore file." + (setq directory (or directory default-directory)) + (vc-call-backend (or (vc-responsible-backend directory) + (error "Unknown backend")) + 'ignore file-or-pattern directory remove as-is)) + +(defun vc-default-ignore (backend file-or-pattern &optional directory rem= ove as-is) + ;; implements =E2=80=98vc-ignore=E2=80=99 generically + (apply #'vc-call-backend backend 'modify-ignores + (vc-call-backend backend 'get-ignore-file-and-pattern + file-or-pattern directory as-is remove))) + +(defun vc-default-get-ignore-file-and-pattern (backend file-or-pattern &o= ptional directory as-is remove) + "Determine ignore file and pattern for BACKEND from FILE-OR-PATTERN. +Implements API of =E2=80=98vc-ignore=E2=80=99 for FILE-OR-PATTERN, DIRECT= ORY and AS-IS. +REMOVE is passed through without evaluation. +Returns (pattern ignore-file remove) suitable for calling +=E2=80=98vc-default-modify-ignores=E2=80=99." + + (setq directory (or directory default-directory)) + (when (not as-is) + (setq file-or-pattern (expand-file-name file-or-pattern directory)) + ;; apply directory-as-file-name, otherwise, if file-or-pattern was + ;; a sub-repository, find-ignore-file would return the wrong + ;; ignore file: + ;; (vc-cvs-find-ignore-file "/re/po/dir/") =3D> /re/po/dir/.cvsignore + ;; (vc-cvs-find-ignore-file "/re/po/dir") =3D> /re/po/.cvsignore + (setq directory (file-name-directory (vc-no-final-slash file-or-patte= rn)))) + + (let* ((ignore-file (vc-call-backend backend 'find-ignore-file director= y)) + (ignore-dir (file-name-directory ignore-file)) + is-dir ignore-param pattern) + (if as-is + (setq ignore-param vc-ignore-param-none) + (when (not (string=3D (substring file-or-pattern 0 (length ignore-d= ir)) + ignore-dir)) + (error "Ignore spec %s is not below project root %s" + file-or-pattern ignore-dir)) + ;; directory may not yet exist + (setq is-dir (or (file-directory-p file-or-pattern) + (vc-has-final-slash file-or-pattern))) + (setq file-or-pattern (vc-no-final-slash + (substring file-or-pattern (length ignore-di= r)))) + (setq ignore-param (vc-call-backend backend 'ignore-param ignore-fi= le))) + + (setq pattern + (concat + (plist-get ignore-param :anchor:) + (funcall (or (plist-get ignore-param :escape:) #'identity) + file-or-pattern) + (or (and is-dir (plist-get ignore-param :dir-trailer:)) + (plist-get ignore-param :trailer:)))) + (list pattern ignore-file remove))) + +(defun vc-default-modify-ignores (_backend pattern ignore-file remove) + "Add PATTERN to IGNORE-FILE, if REMOVE is nil.. +Otherwise remove PATTERN from IGNORE-FILE." + (if remove + (vc--remove-regexp + (concat "^" (regexp-quote pattern) "\\(\n\\|$\\)") ignore-file) + (vc--add-line pattern ignore-file))) + +(defun vc-file-name-directory (file &optional dir dir-as-file) + "Get directory name for FILE. +FILE is expanded against DIR. If FILE is a directory and DIR-AS-FILE +is non-nil, its parent directory is returned." + (and file + (let* ((path (expand-file-name file dir))) + (file-name-directory + (if dir-as-file + (vc-no-final-slash path) + path))))) + +(defun vc-file-relative-name (file &optional dir dir-is-empty) + "Get relative file name for FILE against DIR. +If FILE is a directory and DIR-IS-EMPTY is non-nil, nil is returned. +Otherwise, if FILE is a directory, the final slash is removed." + (and (not (and dir-is-empty (file-directory-p file))) + (vc-no-final-slash (file-relative-name file dir)))) + +(defun vc-no-final-slash (s) + "Remove optional final slash from string S." + ;; based on =E2=80=98ido-no-final-slash=E2=80=99 + (let ((l (vc-has-final-slash s))) + (if l (substring s 0 l) s))) + +(defun vc-has-final-slash (s) + ;"Return index of final slash in string S or nil." + (let ((l (1- (length s)))) + (and (> l 0) (eq (aref s l) ?/) l))) (defun vc-default-ignore-completion-table (backend file) "Return the list of ignored files under BACKEND." =2D- 2.7.4 --------------5734CFC43CA54F90F53A969E--