12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918 |
- ;;; vc.el --- drive a version-control system from within Emacs -*- lexical-binding:t -*-
- ;; Copyright (C) 1992-1998, 2000-2015 Free Software Foundation, Inc.
- ;; Author: FSF (see below for full credits)
- ;; Maintainer: Andre Spiegel <spiegel@gnu.org>
- ;; Keywords: vc tools
- ;; This file is part of GNU Emacs.
- ;; GNU Emacs 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.
- ;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
- ;;; Credits:
- ;; VC was initially designed and implemented by Eric S. Raymond
- ;; <esr@thyrsus.com> in 1992. Over the years, many other people have
- ;; contributed substantial amounts of work to VC. These include:
- ;;
- ;; Per Cederqvist <ceder@lysator.liu.se>
- ;; Paul Eggert <eggert@twinsun.com>
- ;; Sebastian Kremer <sk@thp.uni-koeln.de>
- ;; Martin Lorentzson <martinl@gnu.org>
- ;; Dave Love <fx@gnu.org>
- ;; Stefan Monnier <monnier@cs.yale.edu>
- ;; Thien-Thi Nguyen <ttn@gnu.org>
- ;; Dan Nicolaescu <dann@ics.uci.edu>
- ;; J.D. Smith <jdsmith@alum.mit.edu>
- ;; Andre Spiegel <spiegel@gnu.org>
- ;; Richard Stallman <rms@gnu.org>
- ;;
- ;; In July 2007 ESR returned and redesigned the mode to cope better
- ;; with modern version-control systems that do commits by fileset
- ;; rather than per individual file.
- ;;
- ;; If you maintain a client of the mode or customize it in your .emacs,
- ;; note that some backend functions which formerly took single file arguments
- ;; now take a list of files. These include: register, checkin, print-log,
- ;; and diff.
- ;;; Commentary:
- ;; This mode is fully documented in the Emacs user's manual.
- ;;
- ;; Supported version-control systems presently include CVS, RCS, SRC,
- ;; GNU Subversion, Bzr, Git, Mercurial, Monotone and SCCS (or its free
- ;; replacement, CSSC).
- ;;
- ;; If your site uses the ChangeLog convention supported by Emacs, the
- ;; function `log-edit-comment-to-change-log' could prove a useful checkin hook,
- ;; although you might prefer to use C-c C-a (i.e. `log-edit-insert-changelog')
- ;; from the commit buffer instead or to set `log-edit-setup-invert'.
- ;;
- ;; When using SCCS, RCS, CVS: be careful not to do repo surgery, or
- ;; operations like registrations and deletions and renames, outside VC
- ;; while VC is running. The support for these systems was designed
- ;; when disks were much slower, and the code maintains a lot of
- ;; internal state in order to reduce expensive operations to a
- ;; minimum. Thus, if you mess with the repo while VC's back is turned,
- ;; VC may get seriously confused.
- ;;
- ;; When using Subversion or a later system, anything you do outside VC
- ;; *through the VCS tools* should safely interlock with VC
- ;; operations. Under these VC does little state caching, because local
- ;; operations are assumed to be fast.
- ;;
- ;; The 'assumed to be fast' category includes SRC, even though it's
- ;; a wrapper around RCS.
- ;;
- ;; ADDING SUPPORT FOR OTHER BACKENDS
- ;;
- ;; VC can use arbitrary version control systems as a backend. To add
- ;; support for a new backend named SYS, write a library vc-sys.el that
- ;; contains functions of the form `vc-sys-...' (note that SYS is in lower
- ;; case for the function and library names). VC will use that library if
- ;; you put the symbol SYS somewhere into the list of
- ;; `vc-handled-backends'. Then, for example, if `vc-sys-registered'
- ;; returns non-nil for a file, all SYS-specific versions of VC commands
- ;; will be available for that file.
- ;;
- ;; VC keeps some per-file information in the form of properties (see
- ;; vc-file-set/getprop in vc-hooks.el). The backend-specific functions
- ;; do not generally need to be aware of these properties. For example,
- ;; `vc-sys-working-revision' should compute the working revision and
- ;; return it; it should not look it up in the property, and it needn't
- ;; store it there either. However, if a backend-specific function does
- ;; store a value in a property, that value takes precedence over any
- ;; value that the generic code might want to set (check for uses of
- ;; the macro `with-vc-properties' in vc.el).
- ;;
- ;; In the list of functions below, each identifier needs to be prepended
- ;; with `vc-sys-'. Some of the functions are mandatory (marked with a
- ;; `*'), others are optional (`-').
- ;; BACKEND PROPERTIES
- ;;
- ;; * revision-granularity
- ;;
- ;; Takes no arguments. Returns either 'file or 'repository. Backends
- ;; that return 'file have per-file revision numbering; backends
- ;; that return 'repository have per-repository revision numbering,
- ;; so a revision level implicitly identifies a changeset
- ;; STATE-QUERYING FUNCTIONS
- ;;
- ;; * registered (file)
- ;;
- ;; Return non-nil if FILE is registered in this backend. Both this
- ;; function as well as `state' should be careful to fail gracefully
- ;; in the event that the backend executable is absent. It is
- ;; preferable that this function's *body* is autoloaded, that way only
- ;; calling vc-registered does not cause the backend to be loaded
- ;; (all the vc-FOO-registered functions are called to try to find
- ;; the controlling backend for FILE).
- ;;
- ;; * state (file)
- ;;
- ;; Return the current version control state of FILE. For a list of
- ;; possible values, see `vc-state'. This function should do a full and
- ;; reliable state computation; it is usually called immediately after
- ;; C-x v v.
- ;;
- ;; - dir-status-files (dir files update-function)
- ;;
- ;; Produce RESULT: a list of lists of the form (FILE VC-STATE EXTRA)
- ;; for FILES in DIR. If FILES is nil, report on all files in DIR.
- ;; (It is OK, though possibly inefficient, to ignore the FILES argument
- ;; and always report on all files in DIR.)
- ;;
- ;; If FILES is non-nil, this function should report on all requested
- ;; files, including up-to-date or ignored files.
- ;;
- ;; EXTRA can be used for backend specific information about FILE.
- ;; If a command needs to be run to compute this list, it should be
- ;; run asynchronously using (current-buffer) as the buffer for the
- ;; command.
- ;;
- ;; When RESULT is computed, it should be passed back by doing:
- ;; (funcall UPDATE-FUNCTION RESULT nil). If the backend uses a
- ;; process filter, hence it produces partial results, they can be
- ;; passed back by doing: (funcall UPDATE-FUNCTION RESULT t) and then
- ;; do a (funcall UPDATE-FUNCTION RESULT nil) when all the results
- ;; have been computed.
- ;;
- ;; To provide more backend specific functionality for `vc-dir'
- ;; the following functions might be needed: `dir-extra-headers',
- ;; `dir-printer', and `extra-dir-menu'.
- ;;
- ;; - dir-extra-headers (dir)
- ;;
- ;; Return a string that will be added to the *vc-dir* buffer header.
- ;;
- ;; - dir-printer (fileinfo)
- ;;
- ;; Pretty print the `vc-dir-fileinfo' FILEINFO.
- ;; If a backend needs to show more information than the default FILE
- ;; and STATE in the vc-dir listing, it can store that extra
- ;; information in `vc-dir-fileinfo->extra'. This function can be
- ;; used to display that extra information in the *vc-dir* buffer.
- ;;
- ;; - status-fileinfo-extra (file)
- ;;
- ;; Compute `vc-dir-fileinfo->extra' for FILE.
- ;;
- ;; * working-revision (file)
- ;;
- ;; Return the working revision of FILE. This is the revision fetched
- ;; by the last checkout or update, not necessarily the same thing as the
- ;; head or tip revision. Should return "0" for a file added but not yet
- ;; committed.
- ;;
- ;; * checkout-model (files)
- ;;
- ;; Indicate whether FILES need to be "checked out" before they can be
- ;; edited. See `vc-checkout-model' for a list of possible values.
- ;;
- ;; - mode-line-string (file)
- ;;
- ;; If provided, this function should return the VC-specific mode
- ;; line string for FILE. The returned string should have a
- ;; `help-echo' property which is the text to be displayed as a
- ;; tooltip when the mouse hovers over the VC entry on the mode-line.
- ;; The default implementation deals well with all states that
- ;; `vc-state' can return.
- ;;
- ;; STATE-CHANGING FUNCTIONS
- ;;
- ;; * create-repo (backend)
- ;;
- ;; Create an empty repository in the current directory and initialize
- ;; it so VC mode can add files to it. For file-oriented systems, this
- ;; need do no more than create a subdirectory with the right name.
- ;;
- ;; * register (files &optional comment)
- ;;
- ;; Register FILES in this backend. Optionally, an initial
- ;; description of the file, COMMENT, may be specified, but it is not
- ;; guaranteed that the backend will do anything with this. The
- ;; implementation should pass the value of vc-register-switches to
- ;; the backend command. (Note: in older versions of VC, this
- ;; command had an optional revision first argument that was
- ;; not used; in still older ones it took a single file argument and
- ;; not a list.)
- ;;
- ;; - responsible-p (file)
- ;;
- ;; Return non-nil if this backend considers itself "responsible" for
- ;; FILE, which can also be a directory. This function is used to find
- ;; out what backend to use for registration of new files and for things
- ;; like change log generation. The default implementation always
- ;; returns nil.
- ;;
- ;; - receive-file (file rev)
- ;;
- ;; Let this backend "receive" a file that is already registered under
- ;; another backend. The default implementation simply calls `register'
- ;; for FILE, but it can be overridden to do something more specific,
- ;; e.g. keep revision numbers consistent or choose editing modes for
- ;; FILE that resemble those of the other backend.
- ;;
- ;; - unregister (file)
- ;;
- ;; Unregister FILE from this backend. This is only needed if this
- ;; backend may be used as a "more local" backend for temporary editing.
- ;;
- ;; * checkin (files comment &optional rev)
- ;;
- ;; Commit changes in FILES to this backend. COMMENT is used as a
- ;; check-in comment. The implementation should pass the value of
- ;; vc-checkin-switches to the backend command. The optional REV
- ;; revision argument is only supported with some older VCSes, like
- ;; RCS and CVS, and is otherwise silently ignored.
- ;;
- ;; * find-revision (file rev buffer)
- ;;
- ;; Fetch revision REV of file FILE and put it into BUFFER.
- ;; If REV is the empty string, fetch the head of the trunk.
- ;; The implementation should pass the value of vc-checkout-switches
- ;; to the backend command.
- ;;
- ;; * checkout (file &optional rev)
- ;;
- ;; Check out revision REV of FILE into the working area. FILE
- ;; should be writable by the user and if locking is used for FILE, a
- ;; lock should also be set. If REV is non-nil, that is the revision
- ;; to check out (default is the working revision). If REV is t,
- ;; that means to check out the head of the current branch; if it is
- ;; the empty string, check out the head of the trunk. The
- ;; implementation should pass the value of vc-checkout-switches to
- ;; the backend command. The 'editable' argument of older VC versions
- ;; is gone; all files are checked out editable.
- ;;
- ;; * revert (file &optional contents-done)
- ;;
- ;; Revert FILE back to the working revision. If optional
- ;; arg CONTENTS-DONE is non-nil, then the contents of FILE have
- ;; already been reverted from a version backup, and this function
- ;; only needs to update the status of FILE within the backend.
- ;; If FILE is in the `added' state it should be returned to the
- ;; `unregistered' state.
- ;;
- ;; - merge-file (file rev1 rev2)
- ;;
- ;; Merge the changes between REV1 and REV2 into the current working
- ;; file (for non-distributed VCS). It is expected that with an
- ;; empty first revision this will behave like the merge-news method.
- ;;
- ;; - merge-branch ()
- ;;
- ;; Merge another branch into the current one, prompting for a
- ;; location to merge from.
- ;;
- ;; - merge-news (file)
- ;;
- ;; Merge recent changes from the current branch into FILE.
- ;; (for non-distributed VCS).
- ;;
- ;; - pull (prompt)
- ;;
- ;; Pull "upstream" changes into the current branch (for distributed
- ;; VCS). If PROMPT is non-nil, or if necessary, prompt for a
- ;; location to pull from.
- ;;
- ;; - steal-lock (file &optional revision)
- ;;
- ;; Steal any lock on the working revision of FILE, or on REVISION if
- ;; that is provided. This function is only needed if locking is
- ;; used for files under this backend, and if files can indeed be
- ;; locked by other users.
- ;;
- ;; - modify-change-comment (files rev comment)
- ;;
- ;; Modify the change comments associated with the files at the
- ;; given revision. This is optional, many backends do not support it.
- ;;
- ;; - mark-resolved (files)
- ;;
- ;; Mark conflicts as resolved. Some VC systems need to run a
- ;; command to mark conflicts as resolved.
- ;;
- ;; - find-admin-dir (file)
- ;;
- ;; Return the administrative directory of FILE.
- ;; HISTORY FUNCTIONS
- ;;
- ;; * print-log (files buffer &optional shortlog start-revision limit)
- ;;
- ;; Insert the revision log for FILES into BUFFER.
- ;; If SHORTLOG is true insert a short version of the log.
- ;; If LIMIT is true insert only insert LIMIT log entries. If the
- ;; backend does not support limiting the number of entries to show
- ;; it should return `limit-unsupported'.
- ;; If START-REVISION is given, then show the log starting from that
- ;; revision ("starting" in the sense of it being the _newest_
- ;; revision shown, rather than the working revision, which is normally
- ;; the case). Not all backends support this. At present, this is
- ;; only ever used with LIMIT = 1 (by vc-annotate-show-log-revision-at-line).
- ;;
- ;; * log-outgoing (backend remote-location)
- ;;
- ;; Insert in BUFFER the revision log for the changes that will be
- ;; sent when performing a push operation to REMOTE-LOCATION.
- ;;
- ;; * log-incoming (backend remote-location)
- ;;
- ;; Insert in BUFFER the revision log for the changes that will be
- ;; received when performing a pull operation from REMOTE-LOCATION.
- ;;
- ;; - log-view-mode ()
- ;;
- ;; Mode to use for the output of print-log. This defaults to
- ;; `log-view-mode' and is expected to be changed (if at all) to a derived
- ;; mode of `log-view-mode'.
- ;;
- ;; - show-log-entry (revision)
- ;;
- ;; If provided, search the log entry for REVISION in the current buffer,
- ;; and make sure it is displayed in the buffer's window. The default
- ;; implementation of this function works for RCS-style logs.
- ;;
- ;; - comment-history (file)
- ;;
- ;; Return a string containing all log entries that were made for FILE.
- ;; This is used for transferring a file from one backend to another,
- ;; retaining comment information.
- ;;
- ;; - update-changelog (files)
- ;;
- ;; Using recent log entries, create ChangeLog entries for FILES, or for
- ;; all files at or below the default-directory if FILES is nil. The
- ;; default implementation runs rcs2log, which handles RCS- and
- ;; CVS-style logs.
- ;;
- ;; * diff (files &optional rev1 rev2 buffer async)
- ;;
- ;; Insert the diff for FILE into BUFFER, or the *vc-diff* buffer if
- ;; BUFFER is nil. If ASYNC is non-nil, run asynchronously. If REV1
- ;; and REV2 are non-nil, report differences from REV1 to REV2. If
- ;; REV1 is nil, use the working revision (as found in the
- ;; repository) as the older revision if REV2 is nil as well;
- ;; otherwise, diff against an empty tree. If REV2 is nil, use the
- ;; current working-copy contents as the newer revision. This
- ;; function should pass the value of (vc-switches BACKEND 'diff) to
- ;; the backend command. It should return a status of either 0 (no
- ;; differences found), or 1 (either non-empty diff or the diff is
- ;; run asynchronously).
- ;;
- ;; - revision-completion-table (files)
- ;;
- ;; Return a completion table for existing revisions of FILES.
- ;; The default is to not use any completion table.
- ;;
- ;; - annotate-command (file buf &optional rev)
- ;;
- ;; If this function is provided, it should produce an annotated display
- ;; of FILE in BUF, relative to revision REV. Annotation means each line
- ;; of FILE displayed is prefixed with version information associated with
- ;; its addition (deleted lines leave no history) and that the text of the
- ;; file is fontified according to age.
- ;;
- ;; - annotate-time ()
- ;;
- ;; Only required if `annotate-command' is defined for the backend.
- ;; Return the time of the next line of annotation at or after point,
- ;; as a floating point fractional number of days. The helper
- ;; function `vc-annotate-convert-time' may be useful for converting
- ;; multi-part times as returned by `current-time' and `encode-time'
- ;; to this format. Return nil if no more lines of annotation appear
- ;; in the buffer. You can safely assume that point is placed at the
- ;; beginning of each line, starting at `point-min'. The buffer that
- ;; point is placed in is the Annotate output, as defined by the
- ;; relevant backend. This function also affects how much of the line
- ;; is fontified; where it leaves point is where fontification begins.
- ;;
- ;; - annotate-current-time ()
- ;;
- ;; Only required if `annotate-command' is defined for the backend,
- ;; AND you'd like the current time considered to be anything besides
- ;; (vc-annotate-convert-time (current-time)) -- i.e. the current
- ;; time with hours, minutes, and seconds included. Probably safe to
- ;; ignore. Return the current-time, in units of fractional days.
- ;;
- ;; - annotate-extract-revision-at-line ()
- ;;
- ;; Only required if `annotate-command' is defined for the backend.
- ;; Invoked from a buffer in vc-annotate-mode, return the revision
- ;; corresponding to the current line, or nil if there is no revision
- ;; corresponding to the current line.
- ;; If the backend supports annotating through copies and renames,
- ;; and displays a file name and a revision, then return a cons
- ;; (REVISION . FILENAME).
- ;;
- ;; - region-history (FILE BUFFER LFROM LTO)
- ;;
- ;; Insert into BUFFER the history (log comments and diffs) of the content of
- ;; FILE between lines LFROM and LTO. This is typically done asynchronously.
- ;;
- ;; - region-history-mode ()
- ;;
- ;; Major mode to use for the output of `region-history'.
- ;; TAG SYSTEM
- ;;
- ;; - create-tag (dir name branchp)
- ;;
- ;; Attach the tag NAME to the state of the working copy. This
- ;; should make sure that files are up-to-date before proceeding with
- ;; the action. DIR can also be a file and if BRANCHP is specified,
- ;; NAME should be created as a branch and DIR should be checked out
- ;; under this new branch. The default implementation does not
- ;; support branches but does a sanity check, a tree traversal and
- ;; assigns the tag to each file.
- ;;
- ;; - retrieve-tag (dir name update)
- ;;
- ;; Retrieve the version tagged by NAME of all registered files at or below DIR.
- ;; If UPDATE is non-nil, then update buffers of any files in the
- ;; tag that are currently visited. The default implementation
- ;; does a sanity check whether there aren't any uncommitted changes at
- ;; or below DIR, and then performs a tree walk, using the `checkout'
- ;; function to retrieve the corresponding revisions.
- ;; MISCELLANEOUS
- ;;
- ;; - make-version-backups-p (file)
- ;;
- ;; Return non-nil if unmodified repository revisions of FILE should be
- ;; backed up locally. If this is done, VC can perform `diff' and
- ;; `revert' operations itself, without calling the backend system. The
- ;; default implementation always returns nil.
- ;;
- ;; - root (file)
- ;;
- ;; Return the root of the VC controlled hierarchy for file.
- ;;
- ;; - ignore (file &optional directory)
- ;;
- ;; Ignore FILE under the VCS of DIRECTORY (default is `default-directory').
- ;; FILE is a file wildcard.
- ;; When called interactively and with a prefix argument, remove FILE
- ;; from ignored files.
- ;; When called from Lisp code, if DIRECTORY is non-nil, the
- ;; repository to use will be deduced by DIRECTORY.
- ;;
- ;; - ignore-completion-table
- ;;
- ;; Return the completion table for files ignored by the current
- ;; version control system, e.g., the entries in `.gitignore' and
- ;; `.bzrignore'.
- ;;
- ;; - previous-revision (file rev)
- ;;
- ;; Return the revision number that precedes REV for FILE, or nil if no such
- ;; revision exists.
- ;;
- ;; - next-revision (file rev)
- ;;
- ;; Return the revision number that follows REV for FILE, or nil if no such
- ;; revision exists.
- ;;
- ;; - log-edit-mode ()
- ;;
- ;; Turn on the mode used for editing the check in log. This
- ;; defaults to `log-edit-mode'. If changed, it should use a mode
- ;; derived from`log-edit-mode'.
- ;;
- ;; - check-headers ()
- ;;
- ;; Return non-nil if the current buffer contains any version headers.
- ;;
- ;; - delete-file (file)
- ;;
- ;; Delete FILE and mark it as deleted in the repository. If this
- ;; function is not provided, the command `vc-delete-file' will
- ;; signal an error.
- ;;
- ;; - rename-file (old new)
- ;;
- ;; Rename file OLD to NEW, both in the working area and in the
- ;; repository. If this function is not provided, the renaming
- ;; will be done by (vc-delete-file old) and (vc-register new).
- ;;
- ;; - find-file-hook ()
- ;;
- ;; Operation called in current buffer when opening a file. This can
- ;; be used by the backend to setup some local variables it might need.
- ;;
- ;; - extra-menu ()
- ;;
- ;; Return a menu keymap, the items in the keymap will appear at the
- ;; end of the Version Control menu. The goal is to allow backends
- ;; to specify extra menu items that appear in the VC menu. This way
- ;; you can provide menu entries for functionality that is specific
- ;; to your backend and which does not map to any of the VC generic
- ;; concepts.
- ;;
- ;; - extra-dir-menu ()
- ;;
- ;; Return a menu keymap, the items in the keymap will appear at the
- ;; end of the VC Status menu. The goal is to allow backends to
- ;; specify extra menu items that appear in the VC Status menu. This
- ;; makes it possible to provide menu entries for functionality that
- ;; is specific to a backend and which does not map to any of the VC
- ;; generic concepts.
- ;;
- ;; - conflicted-files (dir)
- ;;
- ;; Return the list of files where conflict resolution is needed in
- ;; the project that contains DIR.
- ;; FIXME: what should it do with non-text conflicts?
- ;;; Changes from the pre-25.1 API:
- ;;
- ;; - INCOMPATIBLE CHANGE: The 'editable' optional argument of
- ;; vc-checkout is gone. The upper level assumes that all files are
- ;; checked out editable. This moves closer to emulating modern
- ;; non-locking behavior even on very old VCSes.
- ;;
- ;; - INCOMPATIBLE CHANGE: The vc-register function and its backend
- ;; implementations no longer take a first optional revision
- ;; argument, since on no system since RCS has setting the initial
- ;; revision been even possible, let alone sane.
- ;;
- ;; INCOMPATIBLE CHANGE: In older versions of the API, vc-diff did
- ;; not take an async-mode flag as a fourth optional argument. (This
- ;; change eliminated a particularly ugly global.)
- ;;
- ;; - INCOMPATIBLE CHANGE: The backend operation for non-distributed
- ;; VCSes formerly called "merge" is now "merge-file" (to contrast
- ;; with merge-branch), and does its own prompting for revisions.
- ;; (This fixes a layer violation that produced bad behavior under
- ;; SVN.)
- ;;
- ;; - INCOMPATIBLE CHANGE: The old fourth 'default-state' argument of
- ;; vc-dir-status-files is gone; none of the back ends actually used it.
- ;;
- ;; - vc-dir-status is no longer a public method; it has been replaced
- ;; by vc-dir-status-files.
- ;;
- ;; - vc-state-heuristic is no longer a public method (the CVS backend
- ;; retains it as a private one).
- ;;
- ;; - the vc-mistrust-permissions configuration variable is gone; the
- ;; code no longer relies on permissions except in one corner case where
- ;; CVS leaves no alternative (which was not gated by this variable). The
- ;; only affected back ends were SCCS and RCS.
- ;;
- ;; - vc-stay-local-p and repository-hostname are no longer part
- ;; of the public API. The vc-stay-local configuration variable
- ;; remains but only affects the CVS back end.
- ;;
- ;; - The init-revision function and the default-initial-revision
- ;; variable are gone. These have't made sense on anything shipped
- ;; since RCS, and using them was a dumb stunt even on RCS.
- ;;
- ;; - workfile-unchanged-p is no longer a public back-end method. It
- ;; was redundant with vc-state and usually implemented with a trivial
- ;; call to it. A few older back ends retain versions for internal use in
- ;; their vc-state functions.
- ;;
- ;; - could-register is no longer a public method. Only vc-cvs ever used it
- ;;
- ;; The vc-keep-workfiles configuration variable is gone. Used only by
- ;; the RCS and SCCS backends, it was an invitation to shoot self in foot
- ;; when set to the (non-default) value nil. The original justification
- ;; for it (saving disk space) is long obsolete.
- ;;
- ;; - The rollback method (implemented by RCS and SCCS only) is gone. See
- ;; the to-do note on uncommit.
- ;;
- ;; - latest-on-branch-p is no longer a public method. It was to be used
- ;; for implementing rollback. RCS keeps its implementation (the only one)
- ;; for internal use.
- ;;; Todo:
- ;;;; New Primitives:
- ;;
- ;; - uncommit: undo last checkin, leave changes in place in the workfile,
- ;; stash the commit comment for re-use.
- ;;
- ;; - deal with push operations.
- ;;
- ;;;; Primitives that need changing:
- ;;
- ;; - vc-update/vc-merge should deal with VC systems that don't do
- ;; update/merge on a file basis, but on a whole repository basis.
- ;; vc-update and vc-merge assume the arguments are always files,
- ;; they don't deal with directories. Make sure the *vc-dir* buffer
- ;; is updated after these operations.
- ;; At least bzr, git and hg should benefit from this.
- ;;
- ;;;; Improved branch and tag handling:
- ;;
- ;; - Make sure the *vc-dir* buffer is updated after merge-branch operations.
- ;;
- ;; - add a generic mechanism for remembering the current branch names,
- ;; display the branch name in the mode-line. Replace
- ;; vc-cvs-sticky-tag with that.
- ;;
- ;; - Add a primitives for switching to a branch (creating it if required.
- ;;
- ;; - Add the ability to list tags and branches.
- ;;
- ;;;; Unify two different versions of the amend capability
- ;;
- ;; - Some back ends (SCCS/RCS/SVN/SRC), have an amend capability that can
- ;; be invoked from log-view.
- ;;
- ;; - The git backend supports amending, but in a different
- ;; way (press `C-c C-e' in log-edit buffer, when making a new commit).
- ;;
- ;; - Second, `log-view-modify-change-comment' doesn't seem to support
- ;; modern backends at all because `log-view-extract-comment'
- ;; unconditionally calls `log-view-current-file'. This should be easy to
- ;; fix.
- ;;
- ;; - Third, doing message editing in log-view might be a natural way to go
- ;; about it, but editing any but the last commit (and even it, if it's
- ;; been pushed) is a dangerous operation in Git, which we shouldn't make
- ;; too easy for users to perform.
- ;;
- ;; There should be a check that the given comment is not reachable
- ;; from any of the "remote" refs?
- ;;
- ;;;; Other
- ;;
- ;; - asynchronous checkin and commit, so you can keep working in other
- ;; buffers while the repo operation happens.
- ;;
- ;; - Direct support for stash/shelve.
- ;;
- ;; - when a file is in `conflict' state, turn on smerge-mode.
- ;;
- ;; - figure out what to do with conflicts that are not caused by the
- ;; file contents, but by metadata or other causes. Example: File A
- ;; gets renamed to B in one branch and to C in another and you merge
- ;; the two branches. Or you locally add file FOO and then pull a
- ;; change that also adds a new file FOO, ...
- ;;
- ;; - make it easier to write logs. Maybe C-x 4 a should add to the log
- ;; buffer, if one is present, instead of adding to the ChangeLog.
- ;;
- ;; - When vc-next-action calls vc-checkin it could pre-fill the
- ;; *vc-log* buffer with some obvious items: the list of files that
- ;; were added, the list of files that were removed. If the diff is
- ;; available, maybe it could even call something like
- ;; `diff-add-change-log-entries-other-window' to create a detailed
- ;; skeleton for the log...
- ;;
- ;; - most vc-dir backends need more work. They might need to
- ;; provide custom headers, use the `extra' field and deal with all
- ;; possible VC states.
- ;;
- ;; - add a function that calls vc-dir to `find-directory-functions'.
- ;;
- ;; - vc-diff, vc-annotate, etc. need to deal better with unregistered
- ;; files. Now that unregistered and ignored files are shown in
- ;; vc-dir, it is possible that these commands are called
- ;; for unregistered/ignored files.
- ;;
- ;; - vc-next-action needs work in order to work with multiple
- ;; backends: `vc-state' returns the state for the default backend,
- ;; not for the backend in the current *vc-dir* buffer.
- ;;
- ;; - vc-dir-kill-dir-status-process should not be specific to dir-status,
- ;; it should work for other async commands done through vc-do-command
- ;; as well,
- ;;
- ;; - vc-dir toolbar needs more icons.
- ;;
- ;; - The backends should avoid using `vc-file-setprop' and `vc-file-getprop'.
- ;;
- ;;; Code:
- (require 'vc-hooks)
- (require 'vc-dispatcher)
- (require 'cl-lib)
- (declare-function diff-setup-whitespace "diff-mode" ())
- (eval-when-compile
- (require 'dired))
- (declare-function dired-get-filename "dired" (&optional localp noerror))
- (declare-function dired-move-to-filename "dired" (&optional err eol))
- (declare-function dired-marker-regexp "dired" ())
- (unless (assoc 'vc-parent-buffer minor-mode-alist)
- (setq minor-mode-alist
- (cons '(vc-parent-buffer vc-parent-buffer-name)
- minor-mode-alist)))
- ;; General customization
- (defgroup vc nil
- "Emacs interface to version control systems."
- :group 'tools)
- (defcustom vc-initial-comment nil
- "If non-nil, prompt for initial comment when a file is registered."
- :type 'boolean
- :group 'vc)
- (make-obsolete-variable 'vc-initial-comment "it has no effect." "23.2")
- (defcustom vc-checkin-switches nil
- "A string or list of strings specifying extra switches for checkin.
- These are passed to the checkin program by \\[vc-checkin]."
- :type '(choice (const :tag "None" nil)
- (string :tag "Argument String")
- (repeat :tag "Argument List"
- :value ("")
- string))
- :group 'vc)
- (defcustom vc-checkout-switches nil
- "A string or list of strings specifying extra switches for checkout.
- These are passed to the checkout program by \\[vc-checkout]."
- :type '(choice (const :tag "None" nil)
- (string :tag "Argument String")
- (repeat :tag "Argument List"
- :value ("")
- string))
- :group 'vc)
- (defcustom vc-register-switches nil
- "A string or list of strings; extra switches for registering a file.
- These are passed to the checkin program by \\[vc-register]."
- :type '(choice (const :tag "None" nil)
- (string :tag "Argument String")
- (repeat :tag "Argument List"
- :value ("")
- string))
- :group 'vc)
- (defcustom vc-diff-switches nil
- "A string or list of strings specifying switches for diff under VC.
- When running diff under a given BACKEND, VC uses the first
- non-nil value of `vc-BACKEND-diff-switches', `vc-diff-switches',
- and `diff-switches', in that order. Since nil means to check the
- next variable in the sequence, either of the first two may use
- the value t to mean no switches at all. `vc-diff-switches'
- should contain switches that are specific to version control, but
- not specific to any particular backend."
- :type '(choice (const :tag "Unspecified" nil)
- (const :tag "None" t)
- (string :tag "Argument String")
- (repeat :tag "Argument List" :value ("") string))
- :group 'vc
- :version "21.1")
- (defcustom vc-annotate-switches nil
- "A string or list of strings specifying switches for annotate under VC.
- When running annotate under a given BACKEND, VC uses the first
- non-nil value of `vc-BACKEND-annotate-switches', `vc-annotate-switches',
- and `annotate-switches', in that order. Since nil means to check the
- next variable in the sequence, either of the first two may use
- the value t to mean no switches at all. `vc-annotate-switches'
- should contain switches that are specific to version control, but
- not specific to any particular backend.
- As very few switches (if any) are used across different VC tools,
- please consider using the specific `vc-BACKEND-annotate-switches'
- for the backend you use."
- :type '(choice (const :tag "Unspecified" nil)
- (const :tag "None" t)
- (string :tag "Argument String")
- (repeat :tag "Argument List" :value ("") string))
- :group 'vc
- :version "25.1")
- (defcustom vc-log-show-limit 2000
- "Limit the number of items shown by the VC log commands.
- Zero means unlimited.
- Not all VC backends are able to support this feature."
- :type 'integer
- :group 'vc)
- (defcustom vc-allow-async-revert nil
- "Specifies whether the diff during \\[vc-revert] may be asynchronous.
- Enabling this option means that you can confirm a revert operation even
- if the local changes in the file have not been found and displayed yet."
- :type '(choice (const :tag "No" nil)
- (const :tag "Yes" t))
- :group 'vc
- :version "22.1")
- ;;;###autoload
- (defcustom vc-checkout-hook nil
- "Normal hook (list of functions) run after checking out a file.
- See `run-hooks'."
- :type 'hook
- :group 'vc
- :version "21.1")
- ;;;###autoload
- (defcustom vc-checkin-hook nil
- "Normal hook (list of functions) run after commit or file checkin.
- See also `log-edit-done-hook'."
- :type 'hook
- :options '(log-edit-comment-to-change-log)
- :group 'vc)
- ;;;###autoload
- (defcustom vc-before-checkin-hook nil
- "Normal hook (list of functions) run before a commit or a file checkin.
- See `run-hooks'."
- :type 'hook
- :group 'vc)
- (defcustom vc-revert-show-diff t
- "If non-nil, `vc-revert' shows a `vc-diff' buffer before querying."
- :type 'boolean
- :group 'vc
- :version "24.1")
- ;; Header-insertion hair
- (defcustom vc-static-header-alist
- '(("\\.c\\'" .
- "\n#ifndef lint\nstatic char vcid[] = \"%s\";\n#endif /* lint */\n"))
- "Associate static header string templates with file types.
- A %s in the template is replaced with the first string associated with
- the file's version control type in `vc-BACKEND-header'."
- :type '(repeat (cons :format "%v"
- (regexp :tag "File Type")
- (string :tag "Header String")))
- :group 'vc)
- (defcustom vc-comment-alist
- '((nroff-mode ".\\\"" ""))
- "Special comment delimiters for generating VC headers.
- Add an entry in this list if you need to override the normal `comment-start'
- and `comment-end' variables. This will only be necessary if the mode language
- is sensitive to blank lines."
- :type '(repeat (list :format "%v"
- (symbol :tag "Mode")
- (string :tag "Comment Start")
- (string :tag "Comment End")))
- :group 'vc)
- ;; File property caching
- (defun vc-clear-context ()
- "Clear all cached file properties."
- (interactive)
- (fillarray vc-file-prop-obarray 0))
- (defmacro with-vc-properties (files form settings)
- "Execute FORM, then maybe set per-file properties for FILES.
- If any of FILES is actually a directory, then do the same for all
- buffers for files in that directory.
- SETTINGS is an association list of property/value pairs. After
- executing FORM, set those properties from SETTINGS that have not yet
- been updated to their corresponding values."
- (declare (debug t))
- `(let ((vc-touched-properties (list t))
- (flist nil))
- (dolist (file ,files)
- (if (file-directory-p file)
- (dolist (buffer (buffer-list))
- (let ((fname (buffer-file-name buffer)))
- (when (and fname (string-prefix-p file fname))
- (push fname flist))))
- (push file flist)))
- ,form
- (dolist (file flist)
- (dolist (setting ,settings)
- (let ((property (car setting)))
- (unless (memq property vc-touched-properties)
- (put (intern file vc-file-prop-obarray)
- property (cdr setting))))))))
- ;;; Code for deducing what fileset and backend to assume
- (defun vc-backend-for-registration (file)
- "Return a backend that can be used for registering FILE.
- If no backend declares itself responsible for FILE, then FILE
- must not be in a version controlled directory, so try to create a
- repository, prompting for the directory and the VC backend to
- use."
- (catch 'found
- ;; First try: find a responsible backend, it must be a backend
- ;; under which FILE is not yet registered.
- (dolist (backend vc-handled-backends)
- (and (not (vc-call-backend backend 'registered file))
- (vc-call-backend backend 'responsible-p file)
- (throw 'found backend)))
- ;; no responsible backend
- (let* ((possible-backends
- (let (pos)
- (dolist (crt vc-handled-backends)
- (when (vc-find-backend-function crt 'create-repo)
- (push crt pos)))
- pos))
- (bk
- (intern
- ;; Read the VC backend from the user, only
- ;; complete with the backends that have the
- ;; 'create-repo method.
- (completing-read
- (format "%s is not in a version controlled directory.\nUse VC backend: " file)
- (mapcar 'symbol-name possible-backends) nil t)))
- (repo-dir
- (let ((def-dir (file-name-directory file)))
- ;; read the directory where to create the
- ;; repository, make sure it's a parent of
- ;; file.
- (read-file-name
- (format "create %s repository in: " bk)
- default-directory def-dir t nil
- (lambda (arg)
- (message "arg %s" arg)
- (and (file-directory-p arg)
- (string-prefix-p (expand-file-name arg) def-dir)))))))
- (let ((default-directory repo-dir))
- (vc-call-backend bk 'create-repo))
- (throw 'found bk))))
- ;;;###autoload
- (defun vc-responsible-backend (file)
- "Return the name of a backend system that is responsible for FILE.
- If FILE is already registered, return the
- backend of FILE. If FILE is not registered, then the
- first backend in `vc-handled-backends' that declares itself
- responsible for FILE is returned."
- (or (and (not (file-directory-p file)) (vc-backend file))
- (catch 'found
- ;; First try: find a responsible backend. If this is for registration,
- ;; it must be a backend under which FILE is not yet registered.
- (dolist (backend vc-handled-backends)
- (and (vc-call-backend backend 'responsible-p file)
- (throw 'found backend))))
- (error "No VC backend is responsible for %s" file)))
- (defun vc-expand-dirs (file-or-dir-list backend)
- "Expands directories in a file list specification.
- Within directories, only files already under version control are noticed."
- (let ((flattened '()))
- (dolist (node file-or-dir-list)
- (when (file-directory-p node)
- (vc-file-tree-walk
- node (lambda (f) (when (eq (vc-backend f) backend) (push f flattened)))))
- (unless (file-directory-p node) (push node flattened)))
- (nreverse flattened)))
- (defvar vc-dir-backend)
- (defvar log-view-vc-backend)
- (defvar log-edit-vc-backend)
- (defvar diff-vc-backend)
- (defun vc-deduce-backend ()
- (cond ((derived-mode-p 'vc-dir-mode) vc-dir-backend)
- ((derived-mode-p 'log-view-mode) log-view-vc-backend)
- ((derived-mode-p 'log-edit-mode) log-edit-vc-backend)
- ((derived-mode-p 'diff-mode) diff-vc-backend)
- ;; Maybe we could even use comint-mode rather than shell-mode?
- ((derived-mode-p 'dired-mode 'shell-mode 'compilation-mode)
- (vc-responsible-backend default-directory))
- (vc-mode (vc-backend buffer-file-name))))
- (declare-function vc-dir-current-file "vc-dir" ())
- (declare-function vc-dir-deduce-fileset "vc-dir" (&optional state-model-only-files))
- (defun vc-deduce-fileset (&optional observer allow-unregistered
- state-model-only-files)
- "Deduce a set of files and a backend to which to apply an operation.
- Return (BACKEND FILESET FILESET-ONLY-FILES STATE CHECKOUT-MODEL).
- If we're in VC-dir mode, FILESET is the list of marked files,
- or the directory if no files are marked.
- Otherwise, if in a buffer visiting a version-controlled file,
- FILESET is a single-file fileset containing that file.
- Otherwise, if ALLOW-UNREGISTERED is non-nil and the visited file
- is unregistered, FILESET is a single-file fileset containing it.
- Otherwise, throw an error.
- STATE-MODEL-ONLY-FILES if non-nil, means that the caller needs
- the FILESET-ONLY-FILES STATE and MODEL info. Otherwise, that
- part may be skipped.
- BEWARE: this function may change the current buffer."
- ;; FIXME: OBSERVER is unused. The name is not intuitive and is not
- ;; documented. It's set to t when called from diff and print-log.
- (let (backend)
- (cond
- ((derived-mode-p 'vc-dir-mode)
- (vc-dir-deduce-fileset state-model-only-files))
- ((derived-mode-p 'dired-mode)
- (if observer
- (vc-dired-deduce-fileset)
- (error "State changing VC operations not supported in `dired-mode'")))
- ((and (derived-mode-p 'log-view-mode)
- (setq backend (vc-responsible-backend default-directory)))
- (list backend default-directory))
- ((setq backend (vc-backend buffer-file-name))
- (if state-model-only-files
- (list backend (list buffer-file-name)
- (list buffer-file-name)
- (vc-state buffer-file-name)
- (vc-checkout-model backend buffer-file-name))
- (list backend (list buffer-file-name))))
- ((and (buffer-live-p vc-parent-buffer)
- ;; FIXME: Why this test? --Stef
- (or (buffer-file-name vc-parent-buffer)
- (with-current-buffer vc-parent-buffer
- (derived-mode-p 'vc-dir-mode))))
- (progn ;FIXME: Why not `with-current-buffer'? --Stef.
- (set-buffer vc-parent-buffer)
- (vc-deduce-fileset observer allow-unregistered state-model-only-files)))
- ((not buffer-file-name)
- (error "Buffer %s is not associated with a file" (buffer-name)))
- ((and allow-unregistered (not (vc-registered buffer-file-name)))
- (if state-model-only-files
- (list (vc-backend-for-registration (buffer-file-name))
- (list buffer-file-name)
- (list buffer-file-name)
- (when state-model-only-files 'unregistered)
- nil)
- (list (vc-backend-for-registration (buffer-file-name))
- (list buffer-file-name))))
- (t (error "File is not under version control")))))
- (defun vc-dired-deduce-fileset ()
- (let ((backend (vc-responsible-backend default-directory)))
- (unless backend (error "Directory not under VC"))
- (list backend
- (dired-map-over-marks (dired-get-filename nil t) nil))))
- (defun vc-ensure-vc-buffer ()
- "Make sure that the current buffer visits a version-controlled file."
- (cond
- ((derived-mode-p 'vc-dir-mode)
- (set-buffer (find-file-noselect (vc-dir-current-file))))
- (t
- (while (and vc-parent-buffer
- (buffer-live-p vc-parent-buffer)
- ;; Avoid infinite looping when vc-parent-buffer and
- ;; current buffer are the same buffer.
- (not (eq vc-parent-buffer (current-buffer))))
- (set-buffer vc-parent-buffer))
- (if (not buffer-file-name)
- (error "Buffer %s is not associated with a file" (buffer-name))
- (unless (vc-backend buffer-file-name)
- (error "File %s is not under version control" buffer-file-name))))))
- ;;; Support for the C-x v v command.
- ;; This is where all the single-file-oriented code from before the fileset
- ;; rewrite lives.
- (defsubst vc-editable-p (file)
- "Return non-nil if FILE can be edited."
- (let ((backend (vc-backend file)))
- (and backend
- (or (eq (vc-checkout-model backend (list file)) 'implicit)
- (memq (vc-state file) '(edited needs-merge conflict))))))
- (defun vc-compatible-state (p q)
- "Controls which states can be in the same commit."
- (or
- (eq p q)
- (and (member p '(edited added removed)) (member q '(edited added removed)))))
- (defun vc-read-backend (prompt)
- (intern
- (completing-read prompt (mapcar 'symbol-name vc-handled-backends)
- nil 'require-match)))
- ;; Here's the major entry point.
- ;;;###autoload
- (defun vc-next-action (verbose)
- "Do the next logical version control operation on the current fileset.
- This requires that all files in the current VC fileset be in the
- same state. If not, signal an error.
- For merging-based version control systems:
- If every file in the VC fileset is not registered for version
- control, register the fileset (but don't commit).
- If every work file in the VC fileset is added or changed, pop
- up a *vc-log* buffer to commit the fileset.
- For a centralized version control system, if any work file in
- the VC fileset is out of date, offer to update the fileset.
- For old-style locking-based version control systems, like RCS:
- If every file is not registered, register the file(s).
- If every file is registered and unlocked, check out (lock)
- the file(s) for editing.
- If every file is locked by you and has changes, pop up a
- *vc-log* buffer to check in the changes. Leave a
- read-only copy of each changed file after checking in.
- If every file is locked by you and unchanged, unlock them.
- If every file is locked by someone else, offer to steal the lock."
- (interactive "P")
- (let* ((vc-fileset (vc-deduce-fileset nil t 'state-model-only-files))
- (backend (car vc-fileset))
- (files (nth 1 vc-fileset))
- ;; (fileset-only-files (nth 2 vc-fileset))
- ;; FIXME: We used to call `vc-recompute-state' here.
- (state (nth 3 vc-fileset))
- ;; The backend should check that the checkout-model is consistent
- ;; among all the `files'.
- (model (nth 4 vc-fileset)))
- ;; If a buffer has unsaved changes, a checkout would discard those
- ;; changes, so treat the buffer as having unlocked changes.
- (when (and (not (eq model 'implicit)) (eq state 'up-to-date))
- (dolist (file files)
- (let ((buffer (get-file-buffer file)))
- (and buffer
- (buffer-modified-p buffer)
- (setq state 'unlocked-changes)))))
- ;; Do the right thing.
- (cond
- ((eq state 'missing)
- (error "Fileset files are missing, so cannot be operated on"))
- ((eq state 'ignored)
- (error "Fileset files are ignored by the version-control system"))
- ((or (null state) (eq state 'unregistered))
- (vc-register vc-fileset))
- ;; Files are up-to-date, or need a merge and user specified a revision
- ((or (eq state 'up-to-date) (and verbose (eq state 'needs-update)))
- (cond
- (verbose
- ;; Go to a different revision.
- (let* ((revision
- ;; FIXME: Provide completion.
- (read-string "Branch, revision, or backend to move to: "))
- (revision-downcase (downcase revision)))
- (if (member
- revision-downcase
- (mapcar (lambda (arg) (downcase (symbol-name arg)))
- vc-handled-backends))
- (let ((vsym (intern-soft revision-downcase)))
- (dolist (file files) (vc-transfer-file file vsym)))
- (dolist (file files)
- (vc-checkout file revision)))))
- ((not (eq model 'implicit))
- ;; check the files out
- (dolist (file files) (vc-checkout file)))
- (t
- ;; do nothing
- (message "Fileset is up-to-date"))))
- ;; Files have local changes
- ((vc-compatible-state state 'edited)
- (let ((ready-for-commit files))
- ;; CVS, SVN and bzr don't care about read-only (bug#9781).
- ;; RCS does, SCCS might (someone should check...).
- (when (memq backend '(RCS SCCS))
- ;; If files are edited but read-only, give user a chance to correct.
- (dolist (file files)
- ;; If committing a mix of removed and edited files, the
- ;; fileset has state = 'edited. Rather than checking the
- ;; state of each individual file in the fileset, it seems
- ;; simplest to just check if the file exists. Bug#9781.
- (when (and (file-exists-p file) (not (file-writable-p file)))
- ;; Make the file-buffer read-write.
- (unless (y-or-n-p (format "%s is edited but read-only; make it writable and continue? " file))
- (error "Aborted"))
- ;; Maybe we somehow lost permissions on the directory.
- (condition-case nil
- (set-file-modes file (logior (file-modes file) 128))
- (error (error "Unable to make file writable")))
- (let ((visited (get-file-buffer file)))
- (when visited
- (with-current-buffer visited
- (read-only-mode -1)))))))
- ;; Allow user to revert files with no changes
- (save-excursion
- (dolist (file files)
- (let ((visited (get-file-buffer file)))
- ;; For files with locking, if the file does not contain
- ;; any changes, just let go of the lock, i.e. revert.
- (when (and (not (eq model 'implicit))
- (eq state 'up-to-date)
- ;; If buffer is modified, that means the user just
- ;; said no to saving it; in that case, don't revert,
- ;; because the user might intend to save after
- ;; finishing the log entry and committing.
- (not (and visited (buffer-modified-p))))
- (vc-revert-file file)
- (setq ready-for-commit (delete file ready-for-commit))))))
- ;; Remaining files need to be committed
- (if (not ready-for-commit)
- (message "No files remain to be committed")
- (if (not verbose)
- (vc-checkin ready-for-commit backend)
- (let* ((revision (read-string "New revision or backend: "))
- (revision-downcase (downcase revision)))
- (if (member
- revision-downcase
- (mapcar (lambda (arg) (downcase (symbol-name arg)))
- vc-handled-backends))
- (let ((vsym (intern revision-downcase)))
- (dolist (file files) (vc-transfer-file file vsym)))
- (vc-checkin ready-for-commit backend nil nil revision)))))))
- ;; locked by somebody else (locking VCSes only)
- ((stringp state)
- ;; In the old days, we computed the revision once and used it on
- ;; the single file. Then, for the 2007-2008 fileset rewrite, we
- ;; computed the revision once (incorrectly, using a free var) and
- ;; used it on all files. To fix the free var bug, we can either
- ;; use `(car files)' or do what we do here: distribute the
- ;; revision computation among `files'. Although this may be
- ;; tedious for those backends where a "revision" is a trans-file
- ;; concept, it is nonetheless correct for both those and (more
- ;; importantly) for those where "revision" is a per-file concept.
- ;; If the intersection of the former group and "locking VCSes" is
- ;; non-empty [I vaguely doubt it --ttn], we can reinstate the
- ;; pre-computation approach of yore.
- (dolist (file files)
- (vc-steal-lock
- file (if verbose
- (read-string (format "%s revision to steal: " file))
- (vc-working-revision file))
- state)))
- ;; conflict
- ((eq state 'conflict)
- ;; FIXME: Is it really the UI we want to provide?
- ;; In my experience, the conflicted files should be marked as resolved
- ;; one-by-one when saving the file after resolving the conflicts.
- ;; I.e. stating explicitly that the conflicts are resolved is done
- ;; very rarely.
- (vc-mark-resolved backend files))
- ;; needs-update
- ((eq state 'needs-update)
- (dolist (file files)
- (if (yes-or-no-p (format
- "%s is not up-to-date. Get latest revision? "
- (file-name-nondirectory file)))
- (vc-checkout file t)
- (when (and (not (eq model 'implicit))
- (yes-or-no-p "Lock this revision? "))
- (vc-checkout file)))))
- ;; needs-merge
- ((eq state 'needs-merge)
- (dolist (file files)
- (when (yes-or-no-p (format
- "%s is not up-to-date. Merge in changes now? "
- (file-name-nondirectory file)))
- (vc-maybe-resolve-conflicts
- file (vc-call-backend backend 'merge-news file)))))
- ;; unlocked-changes
- ((eq state 'unlocked-changes)
- (dolist (file files)
- (when (not (equal buffer-file-name file))
- (find-file-other-window file))
- (if (save-window-excursion
- (vc-diff-internal nil
- (cons (car vc-fileset) (cons (cadr vc-fileset) (list file)))
- (vc-working-revision file) nil)
- (goto-char (point-min))
- (let ((inhibit-read-only t))
- (insert
- (format "Changes to %s since last lock:\n\n" file)))
- (not (beep))
- (yes-or-no-p (concat "File has unlocked changes. "
- "Claim lock retaining changes? ")))
- (progn (vc-call-backend backend 'steal-lock file)
- (clear-visited-file-modtime)
- (write-file buffer-file-name)
- (vc-mode-line file backend))
- (if (not (yes-or-no-p
- "Revert to checked-in revision, instead? "))
- (error "Checkout aborted")
- (vc-revert-buffer-internal t t)
- (vc-checkout file)))))
- ;; Unknown fileset state
- (t
- (error "Fileset is in an unknown state %s" state)))))
- (defun vc-create-repo (backend)
- "Create an empty repository in the current directory."
- (interactive
- (list
- (intern
- (upcase
- (completing-read
- "Create repository for: "
- (mapcar (lambda (b) (list (downcase (symbol-name b)))) vc-handled-backends)
- nil t)))))
- (vc-call-backend backend 'create-repo))
- (declare-function vc-dir-move-to-goal-column "vc-dir" ())
- ;;;###autoload
- (defun vc-register (&optional vc-fileset comment)
- "Register into a version control system.
- If VC-FILESET is given, register the files in that fileset.
- Otherwise register the current file.
- If COMMENT is present, use that as an initial comment.
- The version control system to use is found by cycling through the list
- `vc-handled-backends'. The first backend in that list which declares
- itself responsible for the file (usually because other files in that
- directory are already registered under that backend) will be used to
- register the file. If no backend declares itself responsible, the
- first backend that could register the file is used."
- (interactive "P")
- (let* ((fileset-arg (or vc-fileset (vc-deduce-fileset nil t)))
- (backend (car fileset-arg))
- (files (nth 1 fileset-arg)))
- ;; We used to operate on `only-files', but VC wants to provide the
- ;; possibility to register directories rather than files only, since
- ;; many VCS allow that as well.
- (dolist (fname files)
- (let ((bname (get-file-buffer fname)))
- (unless fname
- (setq fname buffer-file-name))
- (when (vc-call-backend backend 'registered fname)
- (error "This file is already registered"))
- ;; Watch out for new buffers of size 0: the corresponding file
- ;; does not exist yet, even though buffer-modified-p is nil.
- (when bname
- (with-current-buffer bname
- (when (and (not (buffer-modified-p))
- (zerop (buffer-size))
- (not (file-exists-p buffer-file-name)))
- (set-buffer-modified-p t))
- (vc-buffer-sync)))))
- (message "Registering %s... " files)
- (mapc 'vc-file-clearprops files)
- (vc-call-backend backend 'register files comment)
- (mapc
- (lambda (file)
- (vc-file-setprop file 'vc-backend backend)
- ;; FIXME: This is wrong: it should set `backup-inhibited' in all
- ;; the buffers visiting files affected by this `vc-register', not
- ;; in the current-buffer.
- ;; (unless vc-make-backup-files
- ;; (make-local-variable 'backup-inhibited)
- ;; (setq backup-inhibited t))
- (vc-resynch-buffer file t t))
- files)
- (when (derived-mode-p 'vc-dir-mode)
- (vc-dir-move-to-goal-column))
- (message "Registering %s... done" files)))
- (defun vc-register-with (backend)
- "Register the current file with a specified back end."
- (interactive "SBackend: ")
- (when (not (member backend vc-handled-backends))
- (error "Unknown back end"))
- (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.
- 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."
- (interactive
- (list
- (if (not current-prefix-arg)
- (read-file-name "File to ignore: ")
- (completing-read
- "File 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 file wildcard, relative to the root directory of DIRECTORY.
- 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-directory)))
- (pattern (file-relative-name
- (expand-file-name file) (file-name-directory file))))
- (if remove
- (vc--remove-regexp pattern ignore)
- (vc--add-line pattern ignore))))
- (defun vc-default-ignore-completion-table (backend file)
- "Return the list of ignored files under BACKEND."
- (cl-delete-if
- (lambda (str)
- ;; Commented or empty lines.
- (string-match-p "\\`\\(?:#\\|[ \t\r\n]*\\'\\)" str))
- (let ((file (vc-call-backend backend 'find-ignore-file file)))
- (and (file-exists-p file)
- (vc--read-lines file)))))
- (defun vc--read-lines (file)
- "Return a list of lines of FILE."
- (with-temp-buffer
- (insert-file-contents file)
- (split-string (buffer-string) "\n" t)))
- ;; Subroutine for `vc-git-ignore' and `vc-hg-ignore'.
- (defun vc--add-line (string file)
- "Add STRING as a line to FILE."
- (with-temp-buffer
- (insert-file-contents file)
- (unless (re-search-forward (concat "^" (regexp-quote string) "$") nil t)
- (goto-char (point-max))
- (insert (concat "\n" string))
- (write-region (point-min) (point-max) file))))
- (defun vc--remove-regexp (regexp file)
- "Remove all matching for REGEXP in FILE."
- (with-temp-buffer
- (insert-file-contents file)
- (while (re-search-forward regexp nil t)
- (replace-match ""))
- (write-region (point-min) (point-max) file)))
- (defun vc-checkout (file &optional rev)
- "Retrieve a copy of the revision REV of FILE.
- REV defaults to the latest revision.
- After check-out, runs the normal hook `vc-checkout-hook'."
- (and (not rev)
- (vc-call make-version-backups-p file)
- (vc-up-to-date-p file)
- (vc-make-version-backup file))
- (let ((backend (vc-backend file)))
- (with-vc-properties (list file)
- (condition-case err
- (vc-call-backend backend 'checkout file rev)
- (file-error
- ;; Maybe the backend is not installed ;-(
- (when t
- (let ((buf (get-file-buffer file)))
- (when buf (with-current-buffer buf (read-only-mode -1)))))
- (signal (car err) (cdr err))))
- `((vc-state . ,(if (or (eq (vc-checkout-model backend (list file)) 'implicit)
- nil)
- 'up-to-date
- 'edited))
- (vc-checkout-time . ,(nth 5 (file-attributes file))))))
- (vc-resynch-buffer file t t)
- (run-hooks 'vc-checkout-hook))
- (defun vc-mark-resolved (backend files)
- (prog1 (with-vc-properties
- files
- (vc-call-backend backend 'mark-resolved files)
- ;; FIXME: Is this TRTD? Might not be.
- `((vc-state . edited)))
- (message
- (substitute-command-keys
- "Conflicts have been resolved in %s. \
- Type \\[vc-next-action] to check in changes.")
- (if (> (length files) 1)
- (format "%d files" (length files))
- "this file"))))
- (defun vc-steal-lock (file rev owner)
- "Steal the lock on FILE."
- (let (file-description)
- (if rev
- (setq file-description (format "%s:%s" file rev))
- (setq file-description file))
- (when (not (yes-or-no-p (format "Steal the lock on %s from %s? "
- file-description owner)))
- (error "Steal canceled"))
- (message "Stealing lock on %s..." file)
- (with-vc-properties
- (list file)
- (vc-call steal-lock file rev)
- `((vc-state . edited)))
- (vc-resynch-buffer file t t)
- (message "Stealing lock on %s...done" file)
- ;; Write mail after actually stealing, because if the stealing
- ;; goes wrong, we don't want to send any mail.
- (compose-mail owner (format "Stolen lock on %s" file-description))
- (setq default-directory (expand-file-name "~/"))
- (goto-char (point-max))
- (insert
- (format "I stole the lock on %s, " file-description)
- (current-time-string)
- ".\n")
- (message "Please explain why you stole the lock. Type C-c C-c when done.")))
- (defun vc-checkin (files backend &optional comment initial-contents rev)
- "Check in FILES. COMMENT is a comment string; if omitted, a
- buffer is popped up to accept a comment. If INITIAL-CONTENTS is
- non-nil, then COMMENT is used as the initial contents of the log
- entry buffer.
- The optional argument REV may be a string specifying the new revision
- level (only supported for some older VCSes, like RCS and CVS).
- Runs the normal hooks `vc-before-checkin-hook' and `vc-checkin-hook'."
- (when vc-before-checkin-hook
- (run-hooks 'vc-before-checkin-hook))
- (vc-start-logentry
- files comment initial-contents
- "Enter a change comment."
- "*vc-log*"
- (lambda ()
- (vc-call-backend backend 'log-edit-mode))
- (lambda (files comment)
- (message "Checking in %s..." (vc-delistify files))
- ;; "This log message intentionally left almost blank".
- ;; RCS 5.7 gripes about white-space-only comments too.
- (or (and comment (string-match "[^\t\n ]" comment))
- (setq comment "*** empty log message ***"))
- (with-vc-properties
- files
- ;; We used to change buffers to get local value of
- ;; vc-checkin-switches, but 'the' local buffer is
- ;; not a well-defined concept for filesets.
- (progn
- (vc-call-backend backend 'checkin files comment rev)
- (mapc 'vc-delete-automatic-version-backups files))
- `((vc-state . up-to-date)
- (vc-checkout-time . ,(nth 5 (file-attributes file)))
- (vc-working-revision . nil)))
- (message "Checking in %s...done" (vc-delistify files)))
- 'vc-checkin-hook
- backend))
- ;;; Additional entry points for examining version histories
- ;; (defun vc-default-diff-tree (backend dir rev1 rev2)
- ;; "List differences for all registered files at and below DIR.
- ;; The meaning of REV1 and REV2 is the same as for `vc-revision-diff'."
- ;; ;; This implementation does an explicit tree walk, and calls
- ;; ;; vc-BACKEND-diff directly for each file. An optimization
- ;; ;; would be to use `vc-diff-internal', so that diffs can be local,
- ;; ;; and to call it only for files that are actually changed.
- ;; ;; However, this is expensive for some backends, and so it is left
- ;; ;; to backend-specific implementations.
- ;; (setq default-directory dir)
- ;; (vc-file-tree-walk
- ;; default-directory
- ;; (lambda (f)
- ;; (vc-run-delayed
- ;; (let ((coding-system-for-read (vc-coding-system-for-diff f)))
- ;; (message "Looking at %s" f)
- ;; (vc-call-backend (vc-backend f)
- ;; 'diff (list f) rev1 rev2))))))
- (defvar vc-coding-system-inherit-eol t
- "When non-nil, inherit the EOL format for reading Diff output from the file.
- Used in `vc-coding-system-for-diff' to determine the EOL format to use
- for reading Diff output for a file. If non-nil, the EOL format is
- inherited from the file itself.
- Set this variable to nil if your Diff tool might use a different
- EOL. Then Emacs will auto-detect the EOL format in Diff output, which
- gives better results.") ;; Cf. bug#4451.
- (defun vc-coding-system-for-diff (file)
- "Return the coding system for reading diff output for FILE."
- (or coding-system-for-read
- ;; if we already have this file open,
- ;; use the buffer's coding system
- (let ((buf (find-buffer-visiting file)))
- (when buf (with-current-buffer buf
- (if vc-coding-system-inherit-eol
- buffer-file-coding-system
- ;; Don't inherit the EOL part of the coding-system,
- ;; because some Diff tools may choose to use
- ;; a different one. bug#4451.
- (coding-system-base buffer-file-coding-system)))))
- ;; otherwise, try to find one based on the file name
- (car (find-operation-coding-system 'insert-file-contents file))
- ;; and a final fallback
- 'undecided))
- (defun vc-switches (backend op)
- "Return a list of vc-BACKEND switches for operation OP.
- BACKEND is a symbol such as `CVS', which will be downcased.
- OP is a symbol such as `diff'.
- In decreasing order of preference, return the value of:
- vc-BACKEND-OP-switches (e.g. `vc-cvs-diff-switches');
- vc-OP-switches (e.g. `vc-diff-switches'); or, in the case of
- diff only, `diff-switches'.
- If the chosen value is not a string or a list, return nil.
- This is so that you may set, e.g. `vc-svn-diff-switches' to t in order
- to override the value of `vc-diff-switches' and `diff-switches'."
- (let ((switches
- (or (when backend
- (let ((sym (vc-make-backend-sym
- backend (intern (concat (symbol-name op)
- "-switches")))))
- (when (boundp sym) (symbol-value sym))))
- (let ((sym (intern (format "vc-%s-switches" (symbol-name op)))))
- (when (boundp sym) (symbol-value sym)))
- (cond
- ((eq op 'diff) diff-switches)))))
- (if (stringp switches) (list switches)
- ;; If not a list, return nil.
- ;; This is so we can set vc-diff-switches to t to override
- ;; any switches in diff-switches.
- (when (listp switches) switches))))
- ;; Old def for compatibility with Emacs-21.[123].
- (defmacro vc-diff-switches-list (backend)
- (declare (obsolete vc-switches "22.1"))
- `(vc-switches ',backend 'diff))
- (defun vc-diff-finish (buffer messages)
- ;; The empty sync output case has already been handled, so the only
- ;; possibility of an empty output is for an async process.
- (when (buffer-live-p buffer)
- (let ((window (get-buffer-window buffer t))
- (emptyp (zerop (buffer-size buffer))))
- (with-current-buffer buffer
- (and messages emptyp
- (let ((inhibit-read-only t))
- (insert (cdr messages) ".\n")
- (message "%s" (cdr messages))))
- (diff-setup-whitespace)
- (goto-char (point-min))
- (when window
- (shrink-window-if-larger-than-buffer window)))
- (when (and messages (not emptyp))
- (message "%sdone" (car messages))))))
- (defvar vc-diff-added-files nil
- "If non-nil, diff added files by comparing them to /dev/null.")
- (defun vc-diff-internal (async vc-fileset rev1 rev2 &optional verbose buffer)
- "Report diffs between two revisions of a fileset.
- Output goes to the buffer BUFFER, which defaults to *vc-diff*.
- BUFFER, if non-nil, should be a buffer or a buffer name.
- Return t if the buffer had changes, nil otherwise."
- (unless buffer
- (setq buffer "*vc-diff*"))
- (let* ((files (cadr vc-fileset))
- (messages (cons (format "Finding changes in %s..."
- (vc-delistify files))
- (format "No changes between %s and %s"
- (or rev1 "working revision")
- (or rev2 "workfile"))))
- ;; Set coding system based on the first file. It's a kluge,
- ;; but the only way to set it for each file included would
- ;; be to call the back end separately for each file.
- (coding-system-for-read
- (if files (vc-coding-system-for-diff (car files)) 'undecided)))
- ;; On MS-Windows and MS-DOS, Diff is likely to produce DOS-style
- ;; EOLs, which will look ugly if (car files) happens to have Unix
- ;; EOLs.
- (if (memq system-type '(windows-nt ms-dos))
- (setq coding-system-for-read
- (coding-system-change-eol-conversion coding-system-for-read
- 'dos)))
- (vc-setup-buffer buffer)
- (message "%s" (car messages))
- ;; Many backends don't handle well the case of a file that has been
- ;; added but not yet committed to the repo (notably CVS and Subversion).
- ;; Do that work here so the backends don't have to futz with it. --ESR
- ;;
- ;; Actually most backends (including CVS) have options to control the
- ;; behavior since which one is better depends on the user and on the
- ;; situation). Worse yet: this code does not handle the case where
- ;; `file' is a directory which contains added files.
- ;; I made it conditional on vc-diff-added-files but it should probably
- ;; just be removed (or copied/moved to specific backends). --Stef.
- (when vc-diff-added-files
- (let ((filtered '())
- process-file-side-effects)
- (dolist (file files)
- (if (or (file-directory-p file)
- (not (string= (vc-working-revision file) "0")))
- (push file filtered)
- ;; This file is added but not yet committed;
- ;; there is no repository version to diff against.
- (if (or rev1 rev2)
- (error "No revisions of %s exist" file)
- ;; We regard this as "changed".
- ;; Diff it against /dev/null.
- (apply 'vc-do-command buffer
- (if async 'async 1) "diff" file
- (append (vc-switches nil 'diff) '("/dev/null"))))))
- (setq files (nreverse filtered))))
- (vc-call-backend (car vc-fileset) 'diff files rev1 rev2 buffer async)
- (set-buffer buffer)
- (diff-mode)
- (set (make-local-variable 'diff-vc-backend) (car vc-fileset))
- (set (make-local-variable 'revert-buffer-function)
- (lambda (_ignore-auto _noconfirm)
- (vc-diff-internal async vc-fileset rev1 rev2 verbose)))
- ;; Make the *vc-diff* buffer read only, the diff-mode key
- ;; bindings are nicer for read only buffers. pcl-cvs does the
- ;; same thing.
- (setq buffer-read-only t)
- (if (and (zerop (buffer-size))
- (not (get-buffer-process (current-buffer))))
- ;; Treat this case specially so as not to pop the buffer.
- (progn
- (message "%s" (cdr messages))
- nil)
- ;; Display the buffer, but at the end because it can change point.
- (pop-to-buffer (current-buffer))
- ;; The diff process may finish early, so call `vc-diff-finish'
- ;; after `pop-to-buffer'; the former assumes the diff buffer is
- ;; shown in some window.
- (let ((buf (current-buffer)))
- (vc-run-delayed (vc-diff-finish buf (when verbose messages))))
- ;; In the async case, we return t even if there are no differences
- ;; because we don't know that yet.
- t)))
- (defun vc-read-revision (prompt &optional files backend default initial-input)
- (cond
- ((null files)
- (let ((vc-fileset (vc-deduce-fileset t))) ;FIXME: why t? --Stef
- (setq files (cadr vc-fileset))
- (setq backend (car vc-fileset))))
- ((null backend) (setq backend (vc-backend (car files)))))
- (let ((completion-table
- (vc-call-backend backend 'revision-completion-table files)))
- (if completion-table
- (completing-read prompt completion-table
- nil nil initial-input nil default)
- (read-string prompt initial-input nil default))))
- (defun vc-diff-build-argument-list-internal ()
- "Build argument list for calling internal diff functions."
- (let* ((vc-fileset (vc-deduce-fileset t)) ;FIXME: why t? --Stef
- (files (cadr vc-fileset))
- (backend (car vc-fileset))
- (first (car files))
- (rev1-default nil)
- (rev2-default nil))
- (cond
- ;; someday we may be able to do revision completion on non-singleton
- ;; filesets, but not yet.
- ((/= (length files) 1)
- nil)
- ;; if it's a directory, don't supply any revision default
- ((file-directory-p first)
- nil)
- ;; if the file is not up-to-date, use working revision as older revision
- ((not (vc-up-to-date-p first))
- (setq rev1-default (vc-working-revision first)))
- ;; if the file is not locked, use last revision and current source as defaults
- (t
- (setq rev1-default (ignore-errors ;If `previous-revision' doesn't work.
- (vc-call-backend backend 'previous-revision first
- (vc-working-revision first))))
- (when (string= rev1-default "") (setq rev1-default nil))))
- ;; construct argument list
- (let* ((rev1-prompt (if rev1-default
- (concat "Older revision (default "
- rev1-default "): ")
- "Older revision: "))
- (rev2-prompt (concat "Newer revision (default "
- (or rev2-default "current source") "): "))
- (rev1 (vc-read-revision rev1-prompt files backend rev1-default))
- (rev2 (vc-read-revision rev2-prompt files backend rev2-default)))
- (when (string= rev1 "") (setq rev1 nil))
- (when (string= rev2 "") (setq rev2 nil))
- (list files rev1 rev2))))
- ;;;###autoload
- (defun vc-version-diff (_files rev1 rev2)
- "Report diffs between revisions of the fileset in the repository history."
- (interactive (vc-diff-build-argument-list-internal))
- ;; All that was just so we could do argument completion!
- (when (and (not rev1) rev2)
- (error "Not a valid revision range"))
- ;; Yes, it's painful to call (vc-deduce-fileset) again. Alas, the
- ;; placement rules for (interactive) don't actually leave us a choice.
- (vc-diff-internal t (vc-deduce-fileset t) rev1 rev2
- (called-interactively-p 'interactive)))
- ;;;###autoload
- (defun vc-diff (&optional historic not-urgent)
- "Display diffs between file revisions.
- Normally this compares the currently selected fileset with their
- working revisions. With a prefix argument HISTORIC, it reads two revision
- designators specifying which revisions to compare.
- The optional argument NOT-URGENT non-nil means it is ok to say no to
- saving the buffer."
- (interactive (list current-prefix-arg t))
- (if historic
- (call-interactively 'vc-version-diff)
- (when buffer-file-name (vc-buffer-sync not-urgent))
- (vc-diff-internal t (vc-deduce-fileset t) nil nil
- (called-interactively-p 'interactive))))
- (declare-function ediff-load-version-control "ediff" (&optional silent))
- (declare-function ediff-vc-internal "ediff-vers"
- (rev1 rev2 &optional startup-hooks))
- ;;;###autoload
- (defun vc-version-ediff (files rev1 rev2)
- "Show differences between revisions of the fileset in the
- repository history using ediff."
- (interactive (vc-diff-build-argument-list-internal))
- ;; All that was just so we could do argument completion!
- (when (and (not rev1) rev2)
- (error "Not a valid revision range"))
- (message "%s" (format "Finding changes in %s..." (vc-delistify files)))
- ;; Functions ediff-(vc|rcs)-internal use "" instead of nil.
- (when (null rev1) (setq rev1 ""))
- (when (null rev2) (setq rev2 ""))
- (cond
- ;; FIXME We only support running ediff on one file for now.
- ;; We could spin off an ediff session per file in the file set.
- ((= (length files) 1)
- (require 'ediff)
- (ediff-load-version-control) ; loads ediff-vers
- (find-file (car files)) ;FIXME: find-file from Elisp is bad.
- (ediff-vc-internal rev1 rev2 nil))
- (t
- (error "More than one file is not supported"))))
- ;;;###autoload
- (defun vc-ediff (historic &optional not-urgent)
- "Display diffs between file revisions using ediff.
- Normally this compares the currently selected fileset with their
- working revisions. With a prefix argument HISTORIC, it reads two revision
- designators specifying which revisions to compare.
- The optional argument NOT-URGENT non-nil means it is ok to say no to
- saving the buffer."
- (interactive (list current-prefix-arg t))
- (if historic
- (call-interactively 'vc-version-ediff)
- (when buffer-file-name (vc-buffer-sync not-urgent))
- (vc-version-ediff (cadr (vc-deduce-fileset t)) nil nil)))
- ;;;###autoload
- (defun vc-root-diff (historic &optional not-urgent)
- "Display diffs between VC-controlled whole tree revisions.
- Normally, this compares the tree corresponding to the current
- fileset with the working revision.
- With a prefix argument HISTORIC, prompt for two revision
- designators specifying which revisions to compare.
- The optional argument NOT-URGENT non-nil means it is ok to say no to
- saving the buffer."
- (interactive (list current-prefix-arg t))
- (if historic
- ;; FIXME: this does not work right, `vc-version-diff' ends up
- ;; calling `vc-deduce-fileset' to find the files to diff, and
- ;; that's not what we want here, we want the diff for the VC root dir.
- (call-interactively 'vc-version-diff)
- (when buffer-file-name (vc-buffer-sync not-urgent))
- (let ((backend (vc-deduce-backend))
- (default-directory default-directory)
- rootdir working-revision)
- (if backend
- (setq rootdir (vc-call-backend backend 'root default-directory))
- (setq rootdir (read-directory-name "Directory for VC root-diff: "))
- (setq backend (vc-responsible-backend rootdir))
- (if backend
- (setq default-directory rootdir)
- (error "Directory is not version controlled")))
- (setq working-revision (vc-working-revision rootdir))
- ;; VC diff for the root directory produces output that is
- ;; relative to it. Bind default-directory to the root directory
- ;; here, this way the *vc-diff* buffer is setup correctly, so
- ;; relative file names work.
- (let ((default-directory rootdir))
- (vc-diff-internal
- t (list backend (list rootdir) working-revision) nil nil
- (called-interactively-p 'interactive))))))
- ;;;###autoload
- (defun vc-root-dir ()
- "Return the root directory for the current VC tree.
- Return nil if the root directory cannot be identified."
- (let ((backend (vc-deduce-backend)))
- (if backend
- (condition-case err
- (vc-call-backend backend 'root default-directory)
- (vc-not-supported
- (unless (eq (cadr err) 'root)
- (signal (car err) (cdr err)))
- nil)))))
- ;;;###autoload
- (defun vc-revision-other-window (rev)
- "Visit revision REV of the current file in another window.
- If the current file is named `F', the revision is named `F.~REV~'.
- If `F.~REV~' already exists, use it instead of checking it out again."
- (interactive
- (save-current-buffer
- (vc-ensure-vc-buffer)
- (list
- (vc-read-revision "Revision to visit (default is working revision): "
- (list buffer-file-name)))))
- (vc-ensure-vc-buffer)
- (let* ((file buffer-file-name)
- (revision (if (string-equal rev "")
- (vc-working-revision file)
- rev)))
- (switch-to-buffer-other-window (vc-find-revision file revision))))
- (defun vc-find-revision (file revision &optional backend)
- "Read REVISION of FILE into a buffer and return the buffer.
- Use BACKEND as the VC backend if specified."
- (let ((automatic-backup (vc-version-backup-file-name file revision))
- (filebuf (or (get-file-buffer file) (current-buffer)))
- (filename (vc-version-backup-file-name file revision 'manual)))
- (unless (file-exists-p filename)
- (if (file-exists-p automatic-backup)
- (rename-file automatic-backup filename nil)
- (message "Checking out %s..." filename)
- (with-current-buffer filebuf
- (let ((failed t))
- (unwind-protect
- (let ((coding-system-for-read 'no-conversion)
- (coding-system-for-write 'no-conversion))
- (with-temp-file filename
- (let ((outbuf (current-buffer)))
- ;; Change buffer to get local value of
- ;; vc-checkout-switches.
- (with-current-buffer filebuf
- (if backend
- (vc-call-backend backend 'find-revision file revision outbuf)
- (vc-call find-revision file revision outbuf)))))
- (setq failed nil))
- (when (and failed (file-exists-p filename))
- (delete-file filename))))
- (vc-mode-line file))
- (message "Checking out %s...done" filename)))
- (let ((result-buf (find-file-noselect filename)))
- (with-current-buffer result-buf
- ;; Set the parent buffer so that things like
- ;; C-x v g, C-x v l, ... etc work.
- (set (make-local-variable 'vc-parent-buffer) filebuf))
- result-buf)))
- ;; Header-insertion code
- ;;;###autoload
- (defun vc-insert-headers ()
- "Insert headers into a file for use with a version control system.
- Headers desired are inserted at point, and are pulled from
- the variable `vc-BACKEND-header'."
- (interactive)
- (vc-ensure-vc-buffer)
- (save-excursion
- (save-restriction
- (widen)
- (when (or (not (vc-check-headers))
- (y-or-n-p "Version headers already exist. Insert another set? "))
- (let* ((delims (cdr (assq major-mode vc-comment-alist)))
- (comment-start-vc (or (car delims) comment-start "#"))
- (comment-end-vc (or (car (cdr delims)) comment-end ""))
- (hdsym (vc-make-backend-sym (vc-backend buffer-file-name)
- 'header))
- (hdstrings (and (boundp hdsym) (symbol-value hdsym))))
- (dolist (s hdstrings)
- (insert comment-start-vc "\t" s "\t"
- comment-end-vc "\n"))
- (when vc-static-header-alist
- (dolist (f vc-static-header-alist)
- (when (string-match (car f) buffer-file-name)
- (insert (format (cdr f) (car hdstrings)))))))))))
- (defun vc-modify-change-comment (files rev oldcomment)
- "Edit the comment associated with the given files and revision."
- ;; Less of a kluge than it looks like; log-view mode only passes
- ;; this function a singleton list. Arguments left in this form in
- ;; case the more general operation ever becomes meaningful.
- (let ((backend (vc-responsible-backend (car files))))
- (vc-start-logentry
- files oldcomment t
- "Enter a replacement change comment."
- "*vc-log*"
- (lambda () (vc-call-backend backend 'log-edit-mode))
- (lambda (files comment)
- (vc-call-backend backend
- 'modify-change-comment files rev comment)))))
- ;;;###autoload
- (defun vc-merge ()
- "Perform a version control merge operation.
- You must be visiting a version controlled file, or in a `vc-dir' buffer.
- On a distributed version control system, this runs a \"merge\"
- operation to incorporate changes from another branch onto the
- current branch, prompting for an argument list.
- On a non-distributed version control system, this merges changes
- between two revisions into the current fileset. This asks for
- two revisions to merge from in the minibuffer. If the first
- revision is a branch number, then merge all changes from that
- branch. If the first revision is empty, merge the most recent
- changes from the current branch."
- (interactive)
- (let* ((vc-fileset (vc-deduce-fileset t))
- (backend (car vc-fileset))
- (files (cadr vc-fileset)))
- (cond
- ;; If a branch-merge operation is defined, use it.
- ((vc-find-backend-function backend 'merge-branch)
- (vc-call-backend backend 'merge-branch))
- ;; Otherwise, do a per-file merge.
- ((vc-find-backend-function backend 'merge)
- (vc-buffer-sync)
- (dolist (file files)
- (let* ((state (vc-state file))
- status)
- (cond
- ((stringp state) ;; Locking VCses only
- (error "File %s is locked by %s" file state))
- ((not (vc-editable-p file))
- (vc-checkout file t)))
- (setq status (vc-call-backend backend 'merge-file file))
- (vc-maybe-resolve-conflicts file status "WORKFILE" "MERGE SOURCE"))))
- (t
- (error "Sorry, merging is not implemented for %s" backend)))))
- (defun vc-maybe-resolve-conflicts (file status &optional _name-A _name-B)
- (vc-resynch-buffer file t (not (buffer-modified-p)))
- (if (zerop status) (message "Merge successful")
- (smerge-mode 1)
- (message "File contains conflicts.")))
- ;;;###autoload
- (defun vc-message-unresolved-conflicts (filename)
- "Display a message indicating unresolved conflicts in FILENAME."
- ;; This enables all VC backends to give a standard, recognizable
- ;; conflict message that indicates which file is conflicted.
- (message "There are unresolved conflicts in %s" filename))
- ;;;###autoload
- (defalias 'vc-resolve-conflicts 'smerge-ediff)
- ;; TODO: This is OK but maybe we could integrate it better.
- ;; E.g. it could be run semi-automatically (via a prompt?) when saving a file
- ;; that was conflicted (i.e. upon mark-resolved).
- ;; FIXME: should we add an "other-window" version? Or maybe we should
- ;; hook it inside find-file so it automatically works for
- ;; find-file-other-window as well. E.g. find-file could use a new
- ;; `default-next-file' variable for its default file (M-n), and
- ;; we could then set it upon mark-resolve, so C-x C-s C-x C-f M-n would
- ;; automatically offer the next conflicted file.
- (defun vc-find-conflicted-file ()
- "Visit the next conflicted file in the current project."
- (interactive)
- (let* ((backend (or (if buffer-file-name (vc-backend buffer-file-name))
- (vc-responsible-backend default-directory)
- (error "No VC backend")))
- (root (vc-root-dir))
- (files (vc-call-backend backend
- 'conflicted-files (or root default-directory))))
- ;; Don't try and visit the current file.
- (if (equal (car files) buffer-file-name) (pop files))
- (if (null files)
- (message "No more conflicted files")
- (find-file (pop files))
- (message "%s more conflicted files after this one"
- (if files (length files) "No")))))
- ;; Named-configuration entry points
- (defun vc-tag-precondition (dir)
- "Scan the tree below DIR, looking for files not up-to-date.
- If any file is not up-to-date, return the name of the first such file.
- \(This means, neither tag creation nor retrieval is allowed.)
- If one or more of the files are currently visited, return `visited'.
- Otherwise, return nil."
- (let ((status nil))
- (catch 'vc-locked-example
- (vc-file-tree-walk
- dir
- (lambda (f)
- (if (not (vc-up-to-date-p f)) (throw 'vc-locked-example f)
- (when (get-file-buffer f) (setq status 'visited)))))
- status)))
- ;;;###autoload
- (defun vc-create-tag (dir name branchp)
- "Descending recursively from DIR, make a tag called NAME.
- For each registered file, the working revision becomes part of
- the named configuration. If the prefix argument BRANCHP is
- given, the tag is made as a new branch and the files are
- checked out in that new branch."
- (interactive
- (let ((granularity
- (vc-call-backend (vc-responsible-backend default-directory)
- 'revision-granularity)))
- (list
- (if (eq granularity 'repository)
- ;; For VC's that do not work at file level, it's pointless
- ;; to ask for a directory, branches are created at repository level.
- default-directory
- (read-directory-name "Directory: " default-directory default-directory t))
- (read-string (if current-prefix-arg "New branch name: " "New tag name: "))
- current-prefix-arg)))
- (message "Making %s... " (if branchp "branch" "tag"))
- (when (file-directory-p dir) (setq dir (file-name-as-directory dir)))
- (vc-call-backend (vc-responsible-backend dir)
- 'create-tag dir name branchp)
- (vc-resynch-buffer dir t t t)
- (message "Making %s... done" (if branchp "branch" "tag")))
- ;;;###autoload
- (defun vc-retrieve-tag (dir name)
- "For each file in or below DIR, retrieve their tagged version NAME.
- NAME can name a branch, in which case this command will switch to the
- named branch in the directory DIR.
- Interactively, prompt for DIR only for VCS that works at file level;
- otherwise use the default directory of the current buffer.
- If NAME is empty, it refers to the latest revisions of the current branch.
- If locking is used for the files in DIR, then there must not be any
- locked files at or below DIR (but if NAME is empty, locked files are
- allowed and simply skipped)."
- (interactive
- (let ((granularity
- (vc-call-backend (vc-responsible-backend default-directory)
- 'revision-granularity)))
- (list
- (if (eq granularity 'repository)
- ;; For VC's that do not work at file level, it's pointless
- ;; to ask for a directory, branches are created at repository level.
- default-directory
- (read-directory-name "Directory: " default-directory default-directory t))
- (read-string "Tag name to retrieve (default latest revisions): "))))
- (let ((update (yes-or-no-p "Update any affected buffers? "))
- (msg (if (or (not name) (string= name ""))
- (format "Updating %s... " (abbreviate-file-name dir))
- (format "Retrieving tag into %s... "
- (abbreviate-file-name dir)))))
- (message "%s" msg)
- (vc-call-backend (vc-responsible-backend dir)
- 'retrieve-tag dir name update)
- (vc-resynch-buffer dir t t t)
- (message "%s" (concat msg "done"))))
- ;; Miscellaneous other entry points
- ;; FIXME: this should be a defcustom
- ;; FIXME: maybe add another choice:
- ;; `root-directory' (or somesuch), which would mean show a short log
- ;; for the root directory.
- (defvar vc-log-short-style '(directory)
- "Whether or not to show a short log.
- If it contains `directory' then if the fileset contains a directory show a short log.
- If it contains `file' then show short logs for files.
- Not all VC backends support short logs!")
- (defvar log-view-vc-fileset)
- (defun vc-print-log-setup-buttons (working-revision is-start-revision limit pl-return)
- "Insert at the end of the current buffer buttons to show more log entries.
- In the new log, leave point at WORKING-REVISION (if non-nil).
- LIMIT is the number of entries currently shown.
- Does nothing if IS-START-REVISION is non-nil, or if LIMIT is nil,
- or if PL-RETURN is 'limit-unsupported."
- (when (and limit (not (eq 'limit-unsupported pl-return))
- (not is-start-revision))
- (goto-char (point-max))
- (insert "\n")
- (insert-text-button "Show 2X entries"
- 'action (lambda (&rest _ignore)
- (vc-print-log-internal
- log-view-vc-backend log-view-vc-fileset
- working-revision nil (* 2 limit)))
- 'help-echo "Show the log again, and double the number of log entries shown")
- (insert " ")
- (insert-text-button "Show unlimited entries"
- 'action (lambda (&rest _ignore)
- (vc-print-log-internal
- log-view-vc-backend log-view-vc-fileset
- working-revision nil nil))
- 'help-echo "Show the log again, including all entries")))
- (defun vc-print-log-internal (backend files working-revision
- &optional is-start-revision limit)
- "For specified BACKEND and FILES, show the VC log.
- Leave point at WORKING-REVISION, if it is non-nil.
- If IS-START-REVISION is non-nil, start the log from WORKING-REVISION
- \(not all backends support this); i.e., show only WORKING-REVISION and
- earlier revisions. Show up to LIMIT entries (non-nil means unlimited)."
- ;; As of 2013/04 the only thing that passes IS-START-REVISION non-nil
- ;; is vc-annotate-show-log-revision-at-line, which sets LIMIT = 1.
- ;; Don't switch to the output buffer before running the command,
- ;; so that any buffer-local settings in the vc-controlled
- ;; buffer can be accessed by the command.
- (let* ((dir-present (cl-some #'file-directory-p files))
- (shortlog (not (null (memq (if dir-present 'directory 'file)
- vc-log-short-style))))
- (buffer-name "*vc-change-log*")
- (type (if shortlog 'short 'long)))
- (vc-log-internal-common
- backend buffer-name files type
- (lambda (bk buf _type-arg files-arg)
- (vc-call-backend bk 'print-log files-arg buf shortlog
- (when is-start-revision working-revision) limit))
- (lambda (_bk _files-arg ret)
- (vc-print-log-setup-buttons working-revision
- is-start-revision limit ret))
- ;; When it's nil, point really shouldn't move (bug#15322).
- (when working-revision
- (lambda (bk)
- (vc-call-backend bk 'show-log-entry working-revision)))
- (lambda (_ignore-auto _noconfirm)
- (vc-print-log-internal backend files working-revision
- is-start-revision limit)))))
- (defvar vc-log-view-type nil
- "Set this to differentiate the different types of logs.")
- (put 'vc-log-view-type 'permanent-local t)
- (defvar vc-sentinel-movepoint)
- (defun vc-log-internal-common (backend
- buffer-name
- files
- type
- backend-func
- setup-buttons-func
- goto-location-func
- rev-buff-func)
- (let (retval)
- (with-current-buffer (get-buffer-create buffer-name)
- (set (make-local-variable 'vc-log-view-type) type))
- (setq retval (funcall backend-func backend buffer-name type files))
- (with-current-buffer (get-buffer buffer-name)
- (let ((inhibit-read-only t))
- ;; log-view-mode used to be called with inhibit-read-only bound
- ;; to t, so let's keep doing it, just in case.
- (vc-call-backend backend 'log-view-mode)
- (set (make-local-variable 'log-view-vc-backend) backend)
- (set (make-local-variable 'log-view-vc-fileset) files)
- (set (make-local-variable 'revert-buffer-function)
- rev-buff-func)))
- ;; Display after setting up major-mode, so display-buffer-alist can know
- ;; the major-mode.
- (pop-to-buffer buffer-name)
- (vc-run-delayed
- (let ((inhibit-read-only t))
- (funcall setup-buttons-func backend files retval)
- (shrink-window-if-larger-than-buffer)
- (when goto-location-func
- (funcall goto-location-func backend)
- (setq vc-sentinel-movepoint (point)))
- (set-buffer-modified-p nil)))))
- (defun vc-incoming-outgoing-internal (backend remote-location buffer-name type)
- (vc-log-internal-common
- backend buffer-name nil type
- (lambda (bk buf type-arg _files)
- (vc-call-backend bk type-arg buf remote-location))
- (lambda (_bk _files-arg _ret) nil)
- nil ;; Don't move point.
- (lambda (_ignore-auto _noconfirm)
- (vc-incoming-outgoing-internal backend remote-location buffer-name type))))
- ;;;###autoload
- (defun vc-print-log (&optional working-revision limit)
- "List the change log of the current fileset in a window.
- If WORKING-REVISION is non-nil, leave point at that revision.
- If LIMIT is non-nil, it should be a number specifying the maximum
- number of revisions to show; the default is `vc-log-show-limit'.
- When called interactively with a prefix argument, prompt for
- WORKING-REVISION and LIMIT."
- (interactive
- (cond
- (current-prefix-arg
- (let ((rev (read-from-minibuffer "Leave point at revision (default: last revision): " nil
- nil nil nil))
- (lim (string-to-number
- (read-from-minibuffer
- "Limit display (unlimited: 0): "
- (format "%s" vc-log-show-limit)
- nil nil nil))))
- (when (string= rev "") (setq rev nil))
- (when (<= lim 0) (setq lim nil))
- (list rev lim)))
- (t
- (list nil (when (> vc-log-show-limit 0) vc-log-show-limit)))))
- (let* ((vc-fileset (vc-deduce-fileset t)) ;FIXME: Why t? --Stef
- (backend (car vc-fileset))
- (files (cadr vc-fileset))
- ;; (working-revision (or working-revision (vc-working-revision (car files))))
- )
- (vc-print-log-internal backend files working-revision nil limit)))
- ;;;###autoload
- (defun vc-print-root-log (&optional limit)
- "List the change log for the current VC controlled tree in a window.
- If LIMIT is non-nil, it should be a number specifying the maximum
- number of revisions to show; the default is `vc-log-show-limit'.
- When called interactively with a prefix argument, prompt for LIMIT."
- (interactive
- (cond
- (current-prefix-arg
- (let ((lim (string-to-number
- (read-from-minibuffer
- "Limit display (unlimited: 0): "
- (format "%s" vc-log-show-limit)
- nil nil nil))))
- (when (<= lim 0) (setq lim nil))
- (list lim)))
- (t
- (list (when (> vc-log-show-limit 0) vc-log-show-limit)))))
- (let ((backend (vc-deduce-backend))
- (default-directory default-directory)
- rootdir)
- (if backend
- (setq rootdir (vc-call-backend backend 'root default-directory))
- (setq rootdir (read-directory-name "Directory for VC root-log: "))
- (setq backend (vc-responsible-backend rootdir))
- (unless backend
- (error "Directory is not version controlled")))
- (setq default-directory rootdir)
- (vc-print-log-internal backend (list rootdir) nil nil limit)))
- ;;;###autoload
- (defun vc-log-incoming (&optional remote-location)
- "Show a log of changes that will be received with a pull operation from REMOTE-LOCATION.
- When called interactively with a prefix argument, prompt for REMOTE-LOCATION."
- (interactive
- (when current-prefix-arg
- (list (read-string "Remote location (empty for default): "))))
- (let ((backend (vc-deduce-backend)))
- (unless backend
- (error "Buffer is not version controlled"))
- (vc-incoming-outgoing-internal backend remote-location "*vc-incoming*"
- 'log-incoming)))
- ;;;###autoload
- (defun vc-log-outgoing (&optional remote-location)
- "Show a log of changes that will be sent with a push operation to REMOTE-LOCATION.
- When called interactively with a prefix argument, prompt for REMOTE-LOCATION."
- (interactive
- (when current-prefix-arg
- (list (read-string "Remote location (empty for default): "))))
- (let ((backend (vc-deduce-backend)))
- (unless backend
- (error "Buffer is not version controlled"))
- (vc-incoming-outgoing-internal backend remote-location "*vc-outgoing*"
- 'log-outgoing)))
- ;;;###autoload
- (defun vc-region-history (from to)
- "Show the history of the region FROM..TO."
- (interactive "r")
- (let* ((lfrom (line-number-at-pos from))
- (lto (line-number-at-pos to))
- (file buffer-file-name)
- (backend (vc-backend file))
- (buf (get-buffer-create "*VC-history*")))
- (with-current-buffer buf
- (setq-local vc-log-view-type 'long))
- (vc-call region-history file buf lfrom lto)
- (with-current-buffer buf
- (vc-call-backend backend 'region-history-mode)
- (set (make-local-variable 'log-view-vc-backend) backend)
- (set (make-local-variable 'log-view-vc-fileset) file)
- (set (make-local-variable 'revert-buffer-function)
- (lambda (_ignore-auto _noconfirm)
- (with-current-buffer buf
- (let ((inhibit-read-only t)) (erase-buffer)))
- (vc-call region-history file buf lfrom lto))))
- (display-buffer buf)))
- ;;;###autoload
- (defun vc-revert ()
- "Revert working copies of the selected fileset to their repository contents.
- This asks for confirmation if the buffer contents are not identical
- to the working revision (except for keyword expansion)."
- (interactive)
- (let* ((vc-fileset (vc-deduce-fileset))
- (files (cadr vc-fileset))
- (queried nil)
- diff-buffer)
- ;; If any of the files is visited by the current buffer, make sure
- ;; buffer is saved. If the user says `no', abort since we cannot
- ;; show the changes and ask for confirmation to discard them.
- (when (or (not files) (memq (buffer-file-name) files))
- (vc-buffer-sync nil))
- (dolist (file files)
- (let ((buf (get-file-buffer file)))
- (when (and buf (buffer-modified-p buf))
- (error "Please kill or save all modified buffers before reverting")))
- (when (vc-up-to-date-p file)
- (if (yes-or-no-p (format "%s seems up-to-date. Revert anyway? " file))
- (setq queried t)
- (error "Revert canceled"))))
- (unwind-protect
- (when (if vc-revert-show-diff
- (progn
- (setq diff-buffer (generate-new-buffer-name "*vc-diff*"))
- (vc-diff-internal vc-allow-async-revert vc-fileset
- nil nil nil diff-buffer))
- ;; Avoid querying the user again.
- (null queried))
- (unless (yes-or-no-p
- (format "Discard changes in %s? "
- (let ((str (vc-delistify files))
- (nfiles (length files)))
- (if (< (length str) 50)
- str
- (format "%d file%s" nfiles
- (if (= nfiles 1) "" "s"))))))
- (error "Revert canceled")))
- (when diff-buffer
- (quit-windows-on diff-buffer)))
- (dolist (file files)
- (message "Reverting %s..." (vc-delistify files))
- (vc-revert-file file)
- (message "Reverting %s...done" (vc-delistify files)))))
- ;;;###autoload
- (define-obsolete-function-alias 'vc-revert-buffer 'vc-revert "23.1")
- ;;;###autoload
- (defun vc-pull (&optional arg)
- "Update the current fileset or branch.
- You must be visiting a version controlled file, or in a `vc-dir' buffer.
- On a distributed version control system, this runs a \"pull\"
- operation to update the current branch, prompting for an argument
- list if required. Optional prefix ARG forces a prompt.
- On a non-distributed version control system, update the current
- fileset to the tip revisions. For each unchanged and unlocked
- file, this simply replaces the work file with the latest revision
- on its branch. If the file contains changes, any changes in the
- tip revision are merged into the working file."
- (interactive "P")
- (let* ((vc-fileset (vc-deduce-fileset t))
- (backend (car vc-fileset))
- (files (cadr vc-fileset)))
- (cond
- ;; If a pull operation is defined, use it.
- ((vc-find-backend-function backend 'pull)
- (vc-call-backend backend 'pull arg))
- ;; If VCS has `merge-news' functionality (CVS and SVN), use it.
- ((vc-find-backend-function backend 'merge-news)
- (save-some-buffers ; save buffers visiting files
- nil (lambda ()
- (and (buffer-modified-p)
- (let ((file (buffer-file-name)))
- (and file (member file files))))))
- (dolist (file files)
- (if (vc-up-to-date-p file)
- (vc-checkout file t)
- (vc-maybe-resolve-conflicts
- file (vc-call-backend backend 'merge-news file)))))
- ;; For a locking VCS, check out each file.
- ((eq (vc-checkout-model backend files) 'locking)
- (dolist (file files)
- (if (vc-up-to-date-p file)
- (vc-checkout file t))))
- (t
- (error "VC update is unsupported for `%s'" backend)))))
- ;;;###autoload
- (defalias 'vc-update 'vc-pull)
- ;;;###autoload
- (defun vc-push (&optional arg)
- "Push the current branch.
- You must be visiting a version controlled file, or in a `vc-dir' buffer.
- On a distributed version control system, this runs a \"push\"
- operation on the current branch, prompting for the precise command
- if required. Optional prefix ARG non-nil forces a prompt.
- On a non-distributed version control system, this signals an error."
- (interactive "P")
- (let* ((vc-fileset (vc-deduce-fileset t))
- (backend (car vc-fileset)))
- ;;; (files (cadr vc-fileset)))
- (if (vc-find-backend-function backend 'push)
- (vc-call-backend backend 'push arg)
- (user-error "VC push is unsupported for `%s'" backend))))
- (defun vc-version-backup-file (file &optional rev)
- "Return name of backup file for revision REV of FILE.
- If version backups should be used for FILE, and there exists
- such a backup for REV or the working revision of file, return
- its name; otherwise return nil."
- (when (vc-call make-version-backups-p file)
- (let ((backup-file (vc-version-backup-file-name file rev)))
- (if (file-exists-p backup-file)
- backup-file
- ;; there is no automatic backup, but maybe the user made one manually
- (setq backup-file (vc-version-backup-file-name file rev 'manual))
- (when (file-exists-p backup-file)
- backup-file)))))
- (defun vc-revert-file (file)
- "Revert FILE back to the repository working revision it was based on."
- (with-vc-properties
- (list file)
- (let ((backup-file (vc-version-backup-file file)))
- (when backup-file
- (copy-file backup-file file 'ok-if-already-exists)
- (vc-delete-automatic-version-backups file))
- (vc-call revert file backup-file))
- `((vc-state . up-to-date)
- (vc-checkout-time . ,(nth 5 (file-attributes file)))))
- (vc-resynch-buffer file t t))
- ;;;###autoload
- (defun vc-switch-backend (file backend)
- "Make BACKEND the current version control system for FILE.
- FILE must already be registered in BACKEND. The change is not
- permanent, only for the current session. This function only changes
- VC's perspective on FILE, it does not register or unregister it.
- By default, this command cycles through the registered backends.
- To get a prompt, use a prefix argument."
- (interactive
- (list
- (or buffer-file-name
- (error "There is no version-controlled file in this buffer"))
- (let ((crt-bk (vc-backend buffer-file-name))
- (backends nil))
- (unless crt-bk
- (error "File %s is not under version control" buffer-file-name))
- ;; Find the registered backends.
- (dolist (crt vc-handled-backends)
- (when (and (vc-call-backend crt 'registered buffer-file-name)
- (not (eq crt-bk crt)))
- (push crt backends)))
- ;; Find the next backend.
- (let ((def (car backends))
- (others backends))
- (cond
- ((null others) (error "No other backend to switch to"))
- (current-prefix-arg
- (intern
- (upcase
- (completing-read
- (format "Switch to backend [%s]: " def)
- (mapcar (lambda (b) (list (downcase (symbol-name b)))) backends)
- nil t nil nil (downcase (symbol-name def))))))
- (t def))))))
- (unless (eq backend (vc-backend file))
- (vc-file-clearprops file)
- (vc-file-setprop file 'vc-backend backend)
- ;; Force recomputation of the state
- (unless (vc-call-backend backend 'registered file)
- (vc-file-clearprops file)
- (error "%s is not registered in %s" file backend))
- (vc-mode-line file)))
- ;;;###autoload
- (defun vc-transfer-file (file new-backend)
- "Transfer FILE to another version control system NEW-BACKEND.
- If NEW-BACKEND has a higher precedence than FILE's current backend
- \(i.e. it comes earlier in `vc-handled-backends'), then register FILE in
- NEW-BACKEND, using the revision number from the current backend as the
- base level. If NEW-BACKEND has a lower precedence than the current
- backend, then commit all changes that were made under the current
- backend to NEW-BACKEND, and unregister FILE from the current backend.
- \(If FILE is not yet registered under NEW-BACKEND, register it.)"
- (let* ((old-backend (vc-backend file))
- (edited (memq (vc-state file) '(edited needs-merge)))
- (registered (vc-call-backend new-backend 'registered file))
- (move
- (and registered ; Never move if not registered in new-backend yet.
- ;; move if new-backend comes later in vc-handled-backends
- (or (memq new-backend (memq old-backend vc-handled-backends))
- (y-or-n-p "Final transfer? "))))
- (comment nil))
- (when (eq old-backend new-backend)
- (error "%s is the current backend of %s" new-backend file))
- (if registered
- (set-file-modes file (logior (file-modes file) 128))
- ;; `registered' might have switched under us.
- (vc-switch-backend file old-backend)
- (let* ((rev (vc-working-revision file))
- (modified-file (and edited (make-temp-file file)))
- (unmodified-file (and modified-file (vc-version-backup-file file))))
- ;; Go back to the base unmodified file.
- (unwind-protect
- (progn
- (when modified-file
- (copy-file file modified-file 'ok-if-already-exists)
- ;; If we have a local copy of the unmodified file, handle that
- ;; here and not in vc-revert-file because we don't want to
- ;; delete that copy -- it is still useful for OLD-BACKEND.
- (if unmodified-file
- (copy-file unmodified-file file
- 'ok-if-already-exists 'keep-date)
- (when (y-or-n-p "Get base revision from repository? ")
- (vc-revert-file file))))
- (vc-call-backend new-backend 'receive-file file rev))
- (when modified-file
- (vc-switch-backend file new-backend)
- (unless (eq (vc-checkout-model new-backend (list file)) 'implicit)
- (vc-checkout file))
- (rename-file modified-file file 'ok-if-already-exists)
- (vc-file-setprop file 'vc-checkout-time nil)))))
- (when move
- (vc-switch-backend file old-backend)
- (setq comment (vc-call-backend old-backend 'comment-history file))
- (vc-call-backend old-backend 'unregister file))
- (vc-switch-backend file new-backend)
- (when (or move edited)
- (vc-file-setprop file 'vc-state 'edited)
- (vc-mode-line file new-backend)
- (vc-checkin file new-backend comment (stringp comment)))))
- ;;;###autoload
- (defun vc-delete-file (file)
- "Delete file and mark it as such in the version control system.
- If called interactively, read FILE, defaulting to the current
- buffer's file name if it's under version control."
- (interactive (list (read-file-name "VC delete file: " nil
- (when (vc-backend buffer-file-name)
- buffer-file-name) t)))
- (setq file (expand-file-name file))
- (let ((buf (get-file-buffer file))
- (backend (vc-backend file)))
- (unless backend
- (error "File %s is not under version control"
- (file-name-nondirectory file)))
- (unless (vc-find-backend-function backend 'delete-file)
- (error "Deleting files under %s is not supported in VC" backend))
- (when (and buf (buffer-modified-p buf))
- (error "Please save or undo your changes before deleting %s" file))
- (let ((state (vc-state file)))
- (when (eq state 'edited)
- (error "Please commit or undo your changes before deleting %s" file))
- (when (eq state 'conflict)
- (error "Please resolve the conflicts before deleting %s" file)))
- (unless (y-or-n-p (format "Really want to delete %s? "
- (file-name-nondirectory file)))
- (error "Abort!"))
- (unless (or (file-directory-p file) (null make-backup-files)
- (not (file-exists-p file)))
- (with-current-buffer (or buf (find-file-noselect file))
- (let ((backup-inhibited nil))
- (backup-buffer))))
- ;; Bind `default-directory' so that the command that the backend
- ;; runs to remove the file is invoked in the correct context.
- (let ((default-directory (file-name-directory file)))
- (vc-call-backend backend 'delete-file file))
- ;; If the backend hasn't deleted the file itself, let's do it for him.
- (when (file-exists-p file) (delete-file file))
- ;; Forget what VC knew about the file.
- (vc-file-clearprops file)
- ;; Make sure the buffer is deleted and the *vc-dir* buffers are
- ;; updated after this.
- (vc-resynch-buffer file nil t)))
- ;;;###autoload
- (defun vc-rename-file (old new)
- "Rename file OLD to NEW in both work area and repository.
- If called interactively, read OLD and NEW, defaulting OLD to the
- current buffer's file name if it's under version control."
- (interactive (list (read-file-name "VC rename file: " nil
- (when (vc-backend buffer-file-name)
- buffer-file-name) t)
- (read-file-name "Rename to: ")))
- ;; in CL I would have said (setq new (merge-pathnames new old))
- (let ((old-base (file-name-nondirectory old)))
- (when (and (not (string= "" old-base))
- (string= "" (file-name-nondirectory new)))
- (setq new (concat new old-base))))
- (let ((oldbuf (get-file-buffer old)))
- (when (and oldbuf (buffer-modified-p oldbuf))
- (error "Please save files before moving them"))
- (when (get-file-buffer new)
- (error "Already editing new file name"))
- (when (file-exists-p new)
- (error "New file already exists"))
- (let ((state (vc-state old)))
- (unless (memq state '(up-to-date edited))
- (error "Please %s files before moving them"
- (if (stringp state) "check in" "update"))))
- (vc-call rename-file old new)
- (vc-file-clearprops old)
- ;; Move the actual file (unless the backend did it already)
- (when (file-exists-p old) (rename-file old new))
- ;; ?? Renaming a file might change its contents due to keyword expansion.
- ;; We should really check out a new copy if the old copy was precisely equal
- ;; to some checked-in revision. However, testing for this is tricky....
- (when oldbuf
- (with-current-buffer oldbuf
- (let ((buffer-read-only buffer-read-only))
- (set-visited-file-name new))
- (vc-mode-line new (vc-backend new))
- (set-buffer-modified-p nil)))))
- ;;;###autoload
- (defun vc-update-change-log (&rest args)
- "Find change log file and add entries from recent version control logs.
- Normally, find log entries for all registered files in the default
- directory.
- With prefix arg of \\[universal-argument], only find log entries for the current buffer's file.
- With any numeric prefix arg, find log entries for all currently visited
- files that are under version control. This puts all the entries in the
- log for the default directory, which may not be appropriate.
- From a program, any ARGS are assumed to be filenames for which
- log entries should be gathered."
- (interactive
- (cond ((consp current-prefix-arg) ;C-u
- (list buffer-file-name))
- (current-prefix-arg ;Numeric argument.
- (let ((files nil))
- (dolist (buffer (buffer-list))
- (let ((file (buffer-file-name buffer)))
- (and file (vc-backend file)
- (setq files (cons file files)))))
- files))
- (t
- ;; Don't supply any filenames to backend; this means
- ;; it should find all relevant files relative to
- ;; the default-directory.
- nil)))
- (vc-call-backend (vc-responsible-backend default-directory)
- 'update-changelog args))
- ;; functions that operate on RCS revision numbers. This code should
- ;; also be moved into the backends. It stays for now, however, since
- ;; it is used in code below.
- (defun vc-branch-p (rev)
- "Return t if REV is a branch revision."
- (not (eq nil (string-match "\\`[0-9]+\\(\\.[0-9]+\\.[0-9]+\\)*\\'" rev))))
- ;;;###autoload
- (defun vc-branch-part (rev)
- "Return the branch part of a revision number REV."
- (let ((index (string-match "\\.[0-9]+\\'" rev)))
- (when index
- (substring rev 0 index))))
- (defun vc-default-responsible-p (_backend _file)
- "Indicate whether BACKEND is responsible for FILE.
- The default is to return nil always."
- nil)
- (defun vc-default-find-revision (backend file rev buffer)
- "Provide the new `find-revision' op based on the old `checkout' op.
- This is only for compatibility with old backends. They should be updated
- to provide the `find-revision' operation instead."
- (let ((tmpfile (make-temp-file (expand-file-name file))))
- (unwind-protect
- (progn
- (vc-call-backend backend 'checkout file nil rev tmpfile)
- (with-current-buffer buffer
- (insert-file-contents-literally tmpfile)))
- (delete-file tmpfile))))
- (defun vc-default-rename-file (_backend old new)
- (condition-case nil
- (add-name-to-file old new)
- (error (rename-file old new)))
- (vc-delete-file old)
- (with-current-buffer (find-file-noselect new)
- (vc-register)))
- (defalias 'vc-default-check-headers 'ignore)
- (declare-function log-edit-mode "log-edit" ())
- (defun vc-default-log-edit-mode (_backend) (log-edit-mode))
- (defun vc-default-log-view-mode (_backend) (log-view-mode))
- (defun vc-default-show-log-entry (_backend rev)
- (with-no-warnings
- (log-view-goto-rev rev)))
- (defun vc-default-comment-history (backend file)
- "Return a string with all log entries stored in BACKEND for FILE."
- (when (vc-find-backend-function backend 'print-log)
- (with-current-buffer "*vc*"
- (vc-call-backend backend 'print-log (list file))
- (buffer-string))))
- (defun vc-default-receive-file (backend file rev)
- "Let BACKEND receive FILE from another version control system."
- (vc-call-backend backend 'register (list file) rev ""))
- (defun vc-default-retrieve-tag (backend dir name update)
- (if (string= name "")
- (progn
- (vc-file-tree-walk
- dir
- (lambda (f) (and
- (vc-up-to-date-p f)
- (vc-error-occurred
- (vc-call-backend backend 'checkout f nil "")
- (when update (vc-resynch-buffer f t t)))))))
- (let ((result (vc-tag-precondition dir)))
- (if (stringp result)
- (error "File %s is locked" result)
- (setq update (and (eq result 'visited) update))
- (vc-file-tree-walk
- dir
- (lambda (f) (vc-error-occurred
- (vc-call-backend backend 'checkout f nil name)
- (when update (vc-resynch-buffer f t t)))))))))
- (defun vc-default-revert (backend file contents-done)
- (unless contents-done
- (let ((rev (vc-working-revision file))
- (file-buffer (or (get-file-buffer file) (current-buffer))))
- (message "Checking out %s..." file)
- (let ((failed t)
- (backup-name (car (find-backup-file-name file))))
- (when backup-name
- (copy-file file backup-name 'ok-if-already-exists 'keep-date)
- (unless (file-writable-p file)
- (set-file-modes file (logior (file-modes file) 128))))
- (unwind-protect
- (let ((coding-system-for-read 'no-conversion)
- (coding-system-for-write 'no-conversion))
- (with-temp-file file
- (let ((outbuf (current-buffer)))
- ;; Change buffer to get local value of vc-checkout-switches.
- (with-current-buffer file-buffer
- (let ((default-directory (file-name-directory file)))
- (vc-call-backend backend 'find-revision
- file rev outbuf)))))
- (setq failed nil))
- (when backup-name
- (if failed
- (rename-file backup-name file 'ok-if-already-exists)
- (and (not vc-make-backup-files) (delete-file backup-name))))))
- (message "Checking out %s...done" file))))
- (defalias 'vc-default-revision-completion-table 'ignore)
- (defalias 'vc-default-mark-resolved 'ignore)
- (defun vc-default-dir-status-files (_backend _dir files update-function)
- (funcall update-function
- (mapcar (lambda (file) (list file 'up-to-date)) files)))
- (defun vc-check-headers ()
- "Check if the current file has any headers in it."
- (interactive)
- (vc-call-backend (vc-backend buffer-file-name) 'check-headers))
- ;; These things should probably be generally available
- (define-obsolete-function-alias 'vc-string-prefix-p 'string-prefix-p "24.3")
- (defun vc-file-tree-walk (dirname func &rest args)
- "Walk recursively through DIRNAME.
- Invoke FUNC f ARGS on each VC-managed file f underneath it."
- (vc-file-tree-walk-internal (expand-file-name dirname) func args)
- (message "Traversing directory %s...done" dirname))
- (defun vc-file-tree-walk-internal (file func args)
- (if (not (file-directory-p file))
- (when (vc-backend file) (apply func file args))
- (message "Traversing directory %s..." (abbreviate-file-name file))
- (let ((dir (file-name-as-directory file)))
- (mapcar
- (lambda (f) (or
- (string-equal f ".")
- (string-equal f "..")
- (member f vc-directory-exclusion-list)
- (let ((dirf (expand-file-name f dir)))
- (or
- (file-symlink-p dirf) ;; Avoid possible loops.
- (vc-file-tree-walk-internal dirf func args)))))
- (directory-files dir)))))
- (provide 'vc)
- ;;; vc.el ends here
|